Advertisement
Guest User

Untitled

a guest
Dec 13th, 2022
118
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 3.32 KB | None | 0 0
  1. import itertools
  2. from dataclasses import dataclass
  3. from typing import List
  4. import pathlib
  5.  
  6.  
  7. def read_data():
  8.     with open(f"data/{pathlib.Path(__file__).stem}.txt") as raw_data:
  9.         return [line.strip() for line in raw_data.readlines()]
  10.  
  11.  
  12. Data = List['Data'] | int
  13. Stream = List[Data]
  14.  
  15.  
  16. @dataclass
  17. class Packet:
  18.     stream: Stream
  19.  
  20.     def __lt__(self, other: 'Packet'):
  21.         return test_data_order(self.stream, other.stream) == -1
  22.  
  23.  
  24. def find_close_bracket(line: str, start: int) -> int:
  25.     """ find the index of the closing bracket of the first, outermost bracket pair"""
  26.     bracket_count = 0
  27.     for idx, ch in itertools.islice(enumerate(line), start, None):
  28.         match ch:
  29.             case '[':
  30.                 bracket_count += 1
  31.             case ']':
  32.                 bracket_count -= 1
  33.                 if bracket_count == 0:
  34.                     return idx
  35.  
  36.  
  37. def parse_stream(line: str) -> Stream:
  38.     stream: Stream = []
  39.     idx = 1
  40.  
  41.     # ignore surrounding brackets
  42.     while idx < len(line) - 1:
  43.         if line[idx] == '[':
  44.             close_idx = find_close_bracket(line, idx)
  45.             stream.append(parse_stream(line[idx:close_idx + 1]))
  46.             idx = close_idx + 2
  47.  
  48.         else:
  49.             try:
  50.                 next_idx = line[idx:].index(",")
  51.                 stream.append(int(line[idx:idx + next_idx]))
  52.                 idx += next_idx + 1
  53.             except Exception as e:
  54.                 # we're on the last number
  55.                 stream.append(int(line[idx:-1]))
  56.                 break
  57.  
  58.     return stream
  59.  
  60.  
  61. def test_data_order(left: Data, right: Data) -> int:
  62.     match left, right:
  63.         case [], []:
  64.             return 0
  65.         case [_, *_], []:
  66.             return 1
  67.         case [], [_, *_]:
  68.             return -1
  69.         case [x, *xs], [y, *ys]:
  70.             if (ret := test_data_order(x, y)) != 0:
  71.                 return ret
  72.             return test_data_order(xs, ys)
  73.         case int(x), int(y):
  74.             if x < y:
  75.                 return -1
  76.             elif x > y:
  77.                 return 1
  78.             else:
  79.                 return 0
  80.         case int(x), [*y]:
  81.             return test_data_order([x], y)
  82.         case [*x], int(y):
  83.             return test_data_order(x, [y])
  84.  
  85.  
  86. def parse_packets(data: List[str]) -> List[Packet]:
  87.     packets: List[Packet] = []
  88.     data = iter(data)
  89.  
  90.     while True:
  91.  
  92.         left = Packet(parse_stream(next(data)))
  93.         right = Packet(parse_stream(next(data)))
  94.         packets.extend((left, right))
  95.  
  96.         try:
  97.             next(data)
  98.         except:
  99.             break
  100.  
  101.     return packets
  102.  
  103.  
  104. def part1(data: List[str]) -> int:
  105.     packets = parse_packets(data)
  106.     order_sum = 0
  107.     for idx, (left, right) in enumerate(zip(packets[::2], packets[1::2])):
  108.         if left < right:
  109.             order_sum += idx + 1
  110.  
  111.     return order_sum
  112.  
  113.  
  114. def part2(data: List[str]) -> int:
  115.     divider1 = Packet(parse_stream("[[2]]"))
  116.     divider2 = Packet(parse_stream("[[6]]"))
  117.  
  118.     packets = parse_packets(data)
  119.     packets.extend([divider1, divider2])
  120.  
  121.     packets.sort()
  122.  
  123.     return (packets.index(divider1) + 1) * (packets.index(divider2) + 1)
  124.  
  125.  
  126. def main():
  127.     data = read_data()
  128.     print(f"Part 1: {part1(data)}")
  129.     print(f"Part 2: {part2(data)}")
  130.  
  131.  
  132. if __name__ == "__main__":
  133.     main()
  134.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement