68 lines
1.9 KiB
Python
68 lines
1.9 KiB
Python
from __future__ import annotations
|
|
from dataclasses import dataclass
|
|
import itertools
|
|
import math
|
|
from typing import Optional
|
|
|
|
from .aoc import Aoc2, AocSameParser
|
|
|
|
|
|
@dataclass
|
|
class Map:
|
|
directions: list[int]
|
|
rules: dict[str, tuple[str, str]]
|
|
|
|
@staticmethod
|
|
def from_input(inpt: str) -> Map:
|
|
lines = inpt.splitlines()
|
|
|
|
directions: list[int] = [0 if rl == "L" else 1 for rl in lines[0]]
|
|
rules = {line[:3]: (line[7:10], line[12:15]) for line in lines[2:]}
|
|
return Map(directions, rules)
|
|
|
|
|
|
class Day08(AocSameParser[Map], Aoc2[Map, Map]):
|
|
def parseinput(self, inpt: str) -> Map:
|
|
return Map.from_input(inpt)
|
|
|
|
def part1(self, inpt: Map) -> int:
|
|
current = "AAA"
|
|
|
|
for steps, direction in enumerate(itertools.cycle(inpt.directions)):
|
|
if current == "ZZZ":
|
|
return steps
|
|
current = inpt.rules[current][direction]
|
|
raise RuntimeError("End of the rainbow")
|
|
|
|
def find_loop(self, list_of_nrs: list[int]) -> Optional[tuple[int, int]]:
|
|
for index, nr in enumerate(list_of_nrs):
|
|
for nr2 in list_of_nrs[index + 1 :]:
|
|
diff = nr2 - nr
|
|
if nr2 + diff in list_of_nrs:
|
|
return nr, diff
|
|
return None
|
|
|
|
def part2(self, inpt: Map) -> int:
|
|
current = [node for node in inpt.rules.keys() if node.endswith("A")]
|
|
steps_to_z = [0] * len(current)
|
|
for direction in itertools.cycle(inpt.directions):
|
|
dirty = False
|
|
for n, node in enumerate(current):
|
|
if node.endswith("Z"):
|
|
continue
|
|
dirty = True
|
|
steps_to_z[n] += 1
|
|
current[n] = inpt.rules[node][direction]
|
|
if not dirty:
|
|
break
|
|
|
|
return math.lcm(*steps_to_z)
|
|
|
|
|
|
def main() -> None:
|
|
day08 = Day08(2023, 8, example_code_nr1=1, example_code_nr2=-1)
|
|
day08.run()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|