Advertisement
Guest User

Advent of Code 2021 Day 16

a guest
Dec 16th, 2021
225
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 5.15 KB | None | 0 0
  1. from __future__ import annotations
  2. from enum import Enum, IntEnum
  3. from functools import reduce
  4.  
  5.  
  6. HexStr = str
  7. PacketBinary = list[str]
  8.  
  9.  
  10. def parse_input(filepath) -> str:
  11.     with open(filepath, "r") as f:
  12.         return f.read()
  13.  
  14.  
  15. def make_hex_to_binary_map() -> dict[str, str]:
  16.     hexbinmap: dict[str, str] = {}
  17.     with open(r"data/hexbinref.txt", "r") as f:
  18.         for line in f.readlines():
  19.             hex, bin = line.strip().split(" = ")
  20.             hexbinmap[hex] = bin
  21.     return hexbinmap
  22.  
  23.  
  24. HEX_BINARY_MAP = make_hex_to_binary_map()
  25.  
  26.  
  27. class Converter:
  28.     @staticmethod
  29.     def bin_to_base_10(binary: str) -> int:
  30.         return int(binary, 2)
  31.  
  32.     @staticmethod
  33.     def hex_to_bin(hex_packet: HexStr) -> list[str]:
  34.         global HEX_BINARY_MAP
  35.         bin_format = []
  36.         for char in hex_packet:
  37.             bin_format.extend([*HEX_BINARY_MAP[char]])
  38.         return bin_format
  39.  
  40.  
  41. class TypeID(IntEnum):
  42.     Sum = 0
  43.     Product = 1
  44.     Minimum = 2
  45.     Maximum = 3
  46.     IntLiteral = 4
  47.     GreaterThan = 5
  48.     LessThan = 6
  49.     EqualTo = 7
  50.  
  51.  
  52. class LenTypeID(str, Enum):
  53.     TotalLenInBits = "0"
  54.     NumOfSubPackets = "1"
  55.  
  56.  
  57. class Packet:
  58.     version_length = 3
  59.     type_id_length = 3
  60.  
  61.     operations = {
  62.         TypeID.Sum: lambda x: sum(x),
  63.         TypeID.Product: lambda x: reduce(lambda a, b: a * b, x),
  64.         TypeID.Minimum: lambda x: min(x),
  65.         TypeID.Maximum: lambda x: max(x),
  66.         TypeID.GreaterThan: lambda x: int(x[0] > x[1]),
  67.         TypeID.LessThan: lambda x: int(x[0] < x[1]),
  68.         TypeID.EqualTo: lambda x: int(x[0] == x[1]),
  69.     }
  70.  
  71.     def __init__(self, packet: PacketBinary) -> None:
  72.         self.packet: PacketBinary = packet
  73.         self.current_position = 0
  74.         self.version_sum: int = 0
  75.         self.value = None
  76.  
  77.     def process(self):
  78.         self.value = self.process_subpacket()
  79.  
  80.     def advance_position(self, n_positions: int = 1) -> None:
  81.         self.current_position += n_positions
  82.  
  83.     def get_version(self) -> int:
  84.         _version = self.packet[
  85.             self.current_position : self.current_position + self.version_length
  86.         ]
  87.         return Converter.bin_to_base_10("".join(_version))
  88.  
  89.     def get_typeid(self) -> int:
  90.         _typeid = self.packet[
  91.             self.current_position : self.current_position + self.type_id_length
  92.         ]
  93.         return Converter.bin_to_base_10("".join(_typeid))
  94.  
  95.     def process_subpacket(self) -> int:
  96.         version: int = self.get_version()
  97.         self.version_sum += version
  98.         self.advance_position(self.version_length)
  99.  
  100.         typeid: int = self.get_typeid()
  101.         self.advance_position(self.type_id_length)
  102.  
  103.         if typeid != TypeID.IntLiteral:
  104.             length_type_id = self.packet[self.current_position]
  105.             self.advance_position()
  106.             if length_type_id == LenTypeID.TotalLenInBits:
  107.                 return self.process_sub_packet_by_length(typeid)
  108.             elif length_type_id == LenTypeID.NumOfSubPackets:
  109.                 return self.process_sub_packet_by_number_of_sub_packets(typeid)
  110.         else:
  111.             return self.process_int_literal()
  112.  
  113.     def process_int_literal(self) -> int:
  114.         int_list = []
  115.         chunk_len = 5
  116.         first_val = None
  117.         while first_val != "0":
  118.             first_val, *int_chunk = self.packet[
  119.                 self.current_position : self.current_position + chunk_len
  120.             ]
  121.             int_list.extend(int_chunk)
  122.             self.advance_position(chunk_len)
  123.         binary_number = "".join(int_list)
  124.         int_literal = Converter.bin_to_base_10(binary_number)
  125.         return int_literal
  126.  
  127.     def process_sub_packet_by_length(self, procedure, field_len=15):
  128.         n_bits_bin = "".join(
  129.             self.packet[self.current_position : self.current_position + field_len]
  130.         )
  131.         n_bits = Converter.bin_to_base_10(n_bits_bin)
  132.  
  133.         self.advance_position(field_len)
  134.  
  135.         position = self.current_position
  136.         end_position = position + n_bits
  137.  
  138.         vals = []
  139.         while self.current_position < end_position:
  140.             vals.append(self.process_subpacket())
  141.         return self.operations[procedure](vals)
  142.  
  143.     def process_sub_packet_by_number_of_sub_packets(self, procedure, field_len=11):
  144.         n_sub_packets_bin = "".join(
  145.             self.packet[self.current_position : self.current_position + field_len]
  146.         )
  147.         n_sub_packets = Converter.bin_to_base_10(n_sub_packets_bin)
  148.  
  149.         self.advance_position(field_len)
  150.  
  151.         vals = []
  152.         for _ in range(n_sub_packets):
  153.             vals.append(self.process_subpacket())
  154.         return self.operations[procedure](vals)
  155.  
  156.  
  157. def part_a():
  158.     fp = r"data/day16.txt"
  159.     hex_packet = parse_input(fp)
  160.     bin_packet = Converter.hex_to_bin(hex_packet)
  161.     packet = Packet(bin_packet)
  162.     packet.process()
  163.     return packet.version_sum
  164.  
  165.  
  166. def part_b():
  167.     fp = r"data/day16.txt"
  168.     hex_packet = parse_input(fp)
  169.     bin_packet = Converter.hex_to_bin(hex_packet)
  170.     packet = Packet(bin_packet)
  171.     packet.process()
  172.     return packet.value
  173.  
  174.  
  175. if __name__ == "__main__":
  176.     print(part_a())
  177.     print(part_b())
  178.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement