diff --git a/aoc.py b/aoc.py index d9d58b6..ec12ae2 100644 --- a/aoc.py +++ b/aoc.py @@ -34,9 +34,13 @@ class CodeExtractor(HTMLParser): class Aoc2(ABC, Generic[T1, T2]): - def __init__(self, year: int, day: int): + def __init__( + self, year: int, day: int, example_code_nr1: int = 0, example_code_nr2: int = -1 + ): self._year = year self._day = day + self._example_code_nr1 = example_code_nr1 + self._example_code_nr2 = example_code_nr2 with open(".session", encoding="utf8") as f: self._session = f.read().strip() @@ -85,12 +89,12 @@ class Aoc2(ABC, Generic[T1, T2]): code_extractor.feed(site) return code_extractor.code - def run_example_1(self, ex_nr: int = 0) -> str: - inpt = self.get_examples_for_day()[ex_nr] + def run_example_1(self) -> str: + inpt = self.get_examples_for_day()[self._example_code_nr1] return str(self.part1(self.parseinput1(inpt))) - def run_example_2(self, ex_nr: int = -1) -> str: - inpt = self.get_examples_for_day()[ex_nr] + def run_example_2(self) -> str: + inpt = self.get_examples_for_day()[self._example_code_nr2] return str(self.part2(self.parseinput2(inpt))) def run_1(self) -> str: @@ -124,6 +128,7 @@ class Aoc(Generic[T1], Aoc2[T1, T1]): def part2(self, inpt: T1) -> Any: return self.part1(inpt) + class AocSameParser(Generic[T1]): @abstractmethod def parseinput(self, inpt: str) -> T1: @@ -135,6 +140,7 @@ class AocSameParser(Generic[T1]): def parseinput2(self, inpt: str) -> T1: return self.parseinput(inpt) + class AocSameImplementation(Generic[T1]): @abstractmethod def part(self, inpt: T1) -> Any: @@ -160,9 +166,11 @@ class AocParseLines(Generic[T1, T2], Protocol): def parseinput2(self, inpt: str) -> list[T2]: return [self.parseline2(line) for line in inpt.splitlines()] + class AocParseLinesSameParser(Generic[T1], AocParseLines[T1, T1]): def parseline1(self, inpt: str) -> T1: return self.parseline(inpt) + def parseline2(self, inpt: str) -> T1: return self.parseline(inpt) diff --git a/day05.py b/day05.py new file mode 100644 index 0000000..48bbd26 --- /dev/null +++ b/day05.py @@ -0,0 +1,80 @@ +from __future__ import annotations +from dataclasses import dataclass +from itertools import chain + + +import regex as re + +from aoc import Aoc2, AocSameParser + + +@dataclass +class Map: + source: str + dest: str + start_source: int + start_dest: int + id_range: int + + @staticmethod + def from_group(group: list[str]) -> list[Map]: + header_match = re.match(r"(?P[^-]*)-to-(?P[^ ]*) map:", group[0]) + if header_match is None: + raise RuntimeError(f'Could not parse "{group[0]}" as group header') + + source = header_match["source"] + dest = header_match["dest"] + + maps = [] + for line in group[1:]: + start_source, start_dest, id_range = [ + int(d) for d in re.findall(r"\d+", line) + ] + maps.append(Map(source, dest, start_source, start_dest, id_range)) + return maps + + +@dataclass +class Problem: + seeds: list[int] + maps: list[Map] + + @staticmethod + def from_input(inpt: str) -> Problem: + lines = inpt.splitlines() + seed_match = re.match(r"seeds:(\s*(?P\d+)\s*)*", lines[0]) + if seed_match is None: + raise RuntimeError(f'Could not parse "{lines[0]}" as seed list') + seeds = [int(s) for s in seed_match.captures("seedid")] + + groups = [] + for line in lines[1:]: + if not line: + groups.append([]) + else: + groups[-1].append(line) + maps = list(chain.from_iterable(Map.from_group(group) for group in groups)) + return Problem(seeds, maps) + + +class Day05(AocSameParser[Problem], Aoc2[Problem, Problem]): + def __init__(self) -> None: + Aoc2.__init__(self, 2023, 5, example_code_nr2=0) + + def parseinput(self, inpt: str) -> Problem: + return Problem.from_input(inpt) + + def part1(self, inpt: Problem) -> None: + print(inpt) + + def part2(self, inpt: Problem) -> None: + pass + + +def main(): + day5 = Day05() + day5.run_example_1() + + +if __name__ == "__main__": + main()