93 lines
2.8 KiB
Python
93 lines
2.8 KiB
Python
![]() |
# 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)}")
|