from __future__ import annotations from array import array from typing import TypeAlias from itertools import groupby, islice from .aoc import Aoc2, AocSameParser from .day11 import Matrix P1: TypeAlias = list[Matrix] class Day13(AocSameParser[P1], Aoc2[P1, P1]): def parseinput(self, inpt: str) -> P1: fields = inpt.split("\n\n") return [Matrix(group) for group in fields] @staticmethod def check_horiz(m: Matrix, max_diffs: int) -> int | None: rows = list(m.iter_rows()) nr_rows = len(rows) for x in range(1, nr_rows): y = 0 if x > nr_rows / 2: y = x + x - nr_rows diff = Day13.diff_lines(rows[y:x], list(reversed(rows[x : x + x - y]))) if diff == max_diffs: return x return None @staticmethod def check_vert(m: Matrix, max_diffs: int) -> int | None: cols = list(m.iter_cols()) nr_cols = len(cols) for x in range(1, nr_cols): y = 0 if x > nr_cols / 2: y = x + x - nr_cols diff = Day13.diff_lines(cols[y:x], list(reversed(cols[x : x + x - y]))) if diff == max_diffs: return x return None @staticmethod def diff_lines(a: list[array[str]], b: list[array[str]]) -> int: return sum(map(lambda x, y: Day13.diff_line(x, y), a, b)) @staticmethod def diff_line(a: array[str], b: array[str]) -> int: return sum(map(lambda x, y: x != y, a, b)) @staticmethod def compute(inpt: P1, max_diffs: int) -> int: verts = 0 horiz = 0 for m in inpt: mverts = Day13.check_vert(m, max_diffs) if mverts is not None: verts += mverts else: mhoriz = Day13.check_horiz(m, max_diffs) if mhoriz is not None: horiz += mhoriz return verts + 100 * horiz def part1(self, inpt: P1) -> int: return self.compute(inpt, 0) def part2(self, inpt: P1) -> int: return self.compute(inpt, 1) def main() -> None: aoc = Day13(2023, 13, example_code_nr2=0) aoc.run() if __name__ == "__main__": main()