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