day13 finished
This commit is contained in:
parent
6a7e34e322
commit
84d331a928
5 changed files with 646 additions and 0 deletions
133
advent/days/day13/solution.py
Normal file
133
advent/days/day13/solution.py
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
from __future__ import annotations
|
||||
from dataclasses import dataclass
|
||||
from itertools import zip_longest
|
||||
from math import prod
|
||||
|
||||
from typing import Iterator, Self
|
||||
|
||||
day_num = 13
|
||||
|
||||
|
||||
def part1(lines: Iterator[str]) -> int:
|
||||
pairs = parse_all_pairs(lines)
|
||||
return sum(pos for pos, (left, right) in enumerate(pairs, start=1) if left < right)
|
||||
|
||||
|
||||
def part2(lines: Iterator[str]) -> int:
|
||||
marker = [PacketList([[2]]), PacketList([[6]])]
|
||||
packet_lists = [PacketList.parse(line) for line in lines if line] + marker
|
||||
|
||||
return prod(pos for pos, lst in enumerate(sorted(packet_lists), start=1) if lst in marker)
|
||||
|
||||
|
||||
ListOrInt = list["ListOrInt"] | int
|
||||
|
||||
|
||||
@dataclass(slots=True, frozen=True, eq=False)
|
||||
class PacketList:
|
||||
line: list[ListOrInt]
|
||||
|
||||
@classmethod
|
||||
def _parse_sublist(cls, line_iter: Iterator[str]) -> list[ListOrInt]:
|
||||
""" Parses a sublist. Assumes that we do not find multiple commas, ot List in a row."""
|
||||
parsed: list[ListOrInt] = []
|
||||
number: int | None = None
|
||||
while True:
|
||||
match next(line_iter):
|
||||
case '[':
|
||||
if number is not None:
|
||||
raise Exception("Did not expect list")
|
||||
parsed.append(cls._parse_sublist(line_iter))
|
||||
|
||||
case ']':
|
||||
if number is not None:
|
||||
parsed.append(number)
|
||||
return parsed
|
||||
|
||||
case ',':
|
||||
if number is not None:
|
||||
parsed.append(number)
|
||||
number = None
|
||||
|
||||
case d if d in '0123456789':
|
||||
digit = ord(d) - ord('0')
|
||||
if number is None:
|
||||
number = digit
|
||||
else:
|
||||
number = number * 10 + digit
|
||||
|
||||
case c:
|
||||
raise Exception(f'Illegal Char: {c}')
|
||||
|
||||
@classmethod
|
||||
def parse(cls, line: str) -> Self:
|
||||
""" Parses the given line into a PacktList """
|
||||
line_iter = iter(line)
|
||||
if next(line_iter) != '[':
|
||||
raise Exception(f"line does not start with [: {line}")
|
||||
|
||||
return PacketList(cls._parse_sublist(line_iter))
|
||||
|
||||
@classmethod
|
||||
def _comp_sublists(cls, left: list[ListOrInt], right: list[ListOrInt]) -> bool | None:
|
||||
"""
|
||||
Compares two sublists.
|
||||
Return true if left comes before right
|
||||
false if right comes before left
|
||||
None if left and right are equal
|
||||
"""
|
||||
for left_item, right_item in zip_longest(left, right, fillvalue=None):
|
||||
match (left_item, right_item):
|
||||
case int(left_int), int(right_int):
|
||||
if left_int != right_int:
|
||||
return left_int < right_int
|
||||
|
||||
case list(left_list), list(right_list):
|
||||
result = cls._comp_sublists(left_list, right_list)
|
||||
if result is not None:
|
||||
return result
|
||||
|
||||
case int(left_int), list(right_list):
|
||||
result = cls._comp_sublists([left_int], right_list)
|
||||
if result is not None:
|
||||
return result
|
||||
|
||||
case list(left_list), int(right_int):
|
||||
result = cls._comp_sublists(left_list, [right_int])
|
||||
if result is not None:
|
||||
return result
|
||||
|
||||
case None, _:
|
||||
return True
|
||||
|
||||
case _, None:
|
||||
return False
|
||||
|
||||
case _:
|
||||
assert False, "unreachable"
|
||||
return None
|
||||
|
||||
def __eq__(self, other: object) -> bool:
|
||||
if not isinstance(other, PacketList):
|
||||
return False
|
||||
return PacketList._comp_sublists(self.line, other.line) is None
|
||||
|
||||
def __lt__(self, other: PacketList) -> bool:
|
||||
return PacketList._comp_sublists(self.line, other.line) is True
|
||||
|
||||
|
||||
def parse_single_pair(lines: Iterator[str]) -> tuple[PacketList, PacketList]:
|
||||
first = PacketList.parse(next(lines))
|
||||
second = PacketList.parse(next(lines))
|
||||
return first, second
|
||||
|
||||
|
||||
def parse_all_pairs(lines: Iterator[str]) -> list[tuple[PacketList, PacketList]]:
|
||||
result: list[tuple[PacketList, PacketList]] = []
|
||||
try:
|
||||
while True:
|
||||
result.append(parse_single_pair(lines))
|
||||
next(lines)
|
||||
except StopIteration:
|
||||
pass
|
||||
return result
|
||||
Loading…
Add table
Add a link
Reference in a new issue