93 lines
2.8 KiB
Python
Raw Normal View History

2021-12-16 14:12:01 +01:00
# SPDX-License-Identifier: MIT
# Copyright (c) 2021 Akumatic
#
# https://adventofcode.com/2021/day/16
def read_file(filename: str = "input.txt") -> str:
with open(f"{__file__.rstrip('code.py')}{filename}", "r") as f:
return f.read().strip()
def hex2bin(hex_str: str) -> str:
return "".join(bin(int(h, 16))[2:].zfill(4) for h in hex_str)
def decode_packet(packet: str) -> tuple:
# packet header
version = int(packet[:3], 2)
type_id = int(packet[3: 6], 2)
size = 6
payload = list()
if type_id == 4: # literal value
for i in range(6, len(packet), 5):
payload.append(packet[i+1: i+5])
size += 5
if packet[i] == "0":
break
payload = int("".join(payload), 2)
else: # operator
size += 1
if packet[6] == "0": # length in bits of the subpacket
sub_size = int(packet[7: 22], 2)
size += 15 + sub_size
data = packet[22:]
i = 0
while i < sub_size:
decoded_sub = decode_packet(data[i:])
payload.append(decoded_sub)
i += decoded_sub[2]
else: # packet[6] == 1, number of sub-packets
sub_amount = int(packet[7: 18], 2)
size += 11
data = packet[size:]
for i in range(sub_amount):
decoded_sub = decode_packet(data)
payload.append(decoded_sub)
size += decoded_sub[2]
data = packet[size:]
return version, type_id, size, payload
def sum_versions(payload: tuple) -> int:
if payload[1] == 4:
return payload[0]
return payload[0] + sum(sum_versions(sub) for sub in payload[3])
def payload_value(payload: tuple) -> int:
# literal values
if payload[1] == 4:
return payload[3]
values = [payload_value(sub) for sub in payload[3]]
if payload[1] == 0: # sum
return sum(values)
elif payload[1] == 1: # product
prod = 1
for value in values:
prod *= value
return prod
elif payload[1] == 2: # minimum
return min(values)
elif payload[1] == 3: # maximum
return max(values)
elif payload[1] == 5: # greater than
return 1 if values[0] > values[1] else 0
elif payload[1] == 6: # less than
return 1 if values[0] < values[1] else 0
else: # payload[1] == 7, equal to
return 1 if values[0] == values[1] else 0
def part1(hex_value: str) -> int:
data = decode_packet(hex2bin(hex_value))
return sum_versions(data)
def part2(hex_value: str) -> int:
data = decode_packet(hex2bin(hex_value))
return payload_value(data)
if __name__ == "__main__":
vals = read_file()
print(f"Part 1: {part1(vals)}")
print(f"Part 2: {part2(vals)}")