summaryrefslogtreecommitdiffstats
path: root/day16/__init__.py
diff options
context:
space:
mode:
Diffstat (limited to 'day16/__init__.py')
-rw-r--r--day16/__init__.py130
1 files changed, 130 insertions, 0 deletions
diff --git a/day16/__init__.py b/day16/__init__.py
new file mode 100644
index 0000000..66c0d36
--- /dev/null
+++ b/day16/__init__.py
@@ -0,0 +1,130 @@
1import math
2from abc import ABC
3from dataclasses import dataclass
4from typing import List, Tuple, Iterator, TypedDict, Dict, Union
5
6from aoc import BaseAssignment
7
8@dataclass
9class Packet:
10 raw: str
11 version: int = None
12 type: int = None
13 length_type = str
14 padding: int = 0
15 payload_length = None
16 payload_start = None
17 payload: Union[int, List['Packet']] = None
18
19 LITERAL = 4
20
21 FIFTEEN = '0'
22 ELEVEN = '1'
23
24 PAYLOAD_LENGTH_TYPE = {
25 FIFTEEN: 15,
26 ELEVEN: 11
27 }
28
29 @classmethod
30 def from_hex(cls, value: str) -> 'Packet':
31 print()
32 print(value)
33 for byte in value:
34 print(byte, bin(int(byte, 16))[2:].rjust(4, '0'))
35
36 bits = ''.join([
37 bin(int(byte, 16))[2:]
38 .rjust(4, '0')
39 for byte
40 in value
41 ])
42
43 return cls(
44 raw=bits
45 )
46
47
48 def __post_init__(self):
49 self.version = int(self.raw[:3], 2)
50 self.type = int(self.raw[3:6], 2)
51
52 match self.type:
53 case self.LITERAL:
54 self.payload = self.parse_literal()
55 case _:
56 self.length_type = self.raw[6]
57 self.payload_start = 8 + self.PAYLOAD_LENGTH_TYPE[self.length_type]
58 self.payload_length = int(self.raw[8:self.payload_start], 2)
59
60 print(self.raw[:3], self.raw[3:6], self.raw[7], self.raw[7:self.payload_start], self.raw[self.payload_start:])
61
62 self.payload = self.parse_operator()
63
64
65 def __len__(self):
66 return len(self.raw) - self.padding
67
68 def pad(self):
69 added_zeroes = 4 - len(self.raw) % 4
70 self.raw += added_zeroes * '0'
71 self.padding = added_zeroes
72
73 def parse_literal(self) -> int:
74 start_index = 6
75
76 end = False
77 bits = []
78
79 while not end:
80 bits.append(self.raw[start_index + 1:start_index + 5])
81 if self.raw[start_index] == '0':
82 end = True
83
84 start_index += 5
85
86 self.raw = self.raw[:start_index]
87 self.pad()
88
89 return int(''.join(bits), 2)
90
91 def parse_operator(self) -> List['Packet']:
92 packets = []
93 payload_end = self.payload_start + self.payload_length
94 payload = self.raw[self.payload_start:payload_end]
95
96 while len(payload) > 0:
97 packets.append(Packet(raw=payload))
98 payload = payload[len(packets[-1]):]
99
100 self.raw = self.raw[:payload_end]
101 self.pad()
102
103 return packets
104
105
106class Assignment(BaseAssignment, ABC):
107 def parse_item(self, item: str) -> Packet:
108 return Packet.from_hex(item)
109
110
111 def read_input(self, example=False) -> Packet:
112 return next(super().read_input(example))
113
114class AssignmentOne(Assignment):
115 example_result = 31
116
117 def version_sum(self, packet: Packet):
118 if packet.type == Packet.LITERAL:
119 return packet.version
120
121 return packet.version + sum(
122 self.version_sum(p)
123 for p in packet.payload
124 )
125
126 def run(self, input: Packet):
127 return self.version_sum(input)
128
129
130