Files
advent-of-code-2021/day-16/day-16.py
Pascal Lais cadeaf1bba
All checks were successful
continuous-integration/drone/push Build is passing
Add description for day 16
2021-12-16 17:59:47 +01:00

110 lines
3.9 KiB
Python

#!/usr/bin/env python3
from pathlib import Path
from math import prod
def get_next_packet(msg, idx, packets, res=0):
"""The function is called recursively and will read a single packet starting at index 'idx' in
'msg'. The packet will be appended to the 'packets' list. For each packet the type_id and a
payload is stored. The payload is a literal for packets of type 4 or will be filled with
subsequent packages for other types by recursion of this function. The version will be added to
the result.
At the end the updated index and result will be returned.
"""
if (idx+11) >= len(msg):
idx = -1
return idx, res
version = int(msg[idx:idx + 3], 2)
idx += 3
res += version
type_id = int(msg[idx:idx + 3], 2)
idx += 3
payload = None
match type_id:
case 4:
end = False
literal = 0
while not end:
if not int(msg[idx:idx + 1], 2):
end = True
idx += 1
literal = (literal << 4) | int(msg[idx:idx + 4], 2)
idx += 4
payload = literal
case _:
length_id = int(msg[idx:idx + 1], 2)
idx += 1
length = 0
payload = []
if length_id:
length = int(msg[idx:idx + 11], 2)
idx += 11
for _ in range(length):
idx, res = get_next_packet(msg, idx, payload, res)
else:
length = int(msg[idx:idx + 15], 2)
idx += 15
next_idx = idx + length
while idx < next_idx:
idx, res = get_next_packet(msg, idx, payload, res)
packets.append({'type_id': type_id, 'payload': payload})
return idx, res
def calc_result(packets):
"""The function recursively steps through the packets and performs the defined calculations."""
res = 0
for p in packets:
match p['type_id']:
case 4:
res = p['payload']
case _:
sub_packets = []
for i in range(len(p['payload'])):
sub_packets.append(calc_result([p['payload'][i]]))
match p['type_id']:
case 0:
res = sum(sub_packets)
case 1:
res = prod(sub_packets)
case 2:
res = min(sub_packets)
case 3:
res = max(sub_packets)
case 5:
res = 1 if sub_packets[0] > sub_packets[1] else 0
case 6:
res = 1 if sub_packets[0] < sub_packets[1] else 0
case 7:
res = 1 if sub_packets[0] == sub_packets[1] else 0
return res
def solve(input):
"""Part 1 and part 2 are solved in one go so we don't have to parse the bitstream twice. We
start by stripping newline and zeros at the end of the input line, convert it to an integer
with base 16 (hexadecimal) and back to a binary string. The bin() function adds '0b' at the
beginning so we only store te actual binary data. Afterwards we need to add zeros in the
beginning until the binary string has a length of four times the hexadecimal representation
(Each hex digit is four binary digits).
"""
result_p1 = 0
line = input[0].rstrip().strip('0')
msg = bin(int(line, 16))[2:]
while len(msg) < len(line) * 4:
msg = '0' + msg
packets = []
next = 0
while -1 != next:
next, result_p1 = get_next_packet(msg, next, packets, result_p1)
result_p2 = calc_result(packets)
print("Part 1 result:", result_p1)
print("Part 2 result:", result_p2)
input = list()
p = Path(__file__).with_name('input.txt')
with open(p) as f:
input = f.readlines()
solve(input)