diff --git a/2023/d13/ex1/ex1.py b/2023/d13/ex1/ex1.py new file mode 100755 index 0000000..a591a9e --- /dev/null +++ b/2023/d13/ex1/ex1.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python + +import sys +from typing import NamedTuple, Optional + + +class Point(NamedTuple): + x: int + y: int + + +class Grid(NamedTuple): + points: set[Point] + lines: int + rows: int + + +def solve(input: str) -> int: + def parse_grid(grid: list[str]) -> Grid: + points: set[Point] = set() + + for x, line in enumerate(grid): + for y, c in enumerate(line): + if c != "#": + continue + points.add(Point(x, y)) + + return Grid(points, len(grid), len(grid[0])) + + def parse(input: str) -> list[Grid]: + return [parse_grid(grid.splitlines()) for grid in input.split("\n\n")] + + def reflect_vertical(grid: Grid) -> Optional[int]: + def try_row(row: int) -> bool: + for y in range(min(row, grid.rows - row)): + for x in range(grid.lines): + left = Point(x, row - y - 1) in grid.points + right = Point(x, row + y) in grid.points + if left and right: + continue + if left or right: + return False + return True + + for row in range(1, grid.rows): + if try_row(row): + return row + return None + + def reflect_horizontal(grid: Grid) -> Optional[int]: + def try_line(line: int) -> bool: + for x in range(min(line, grid.lines - line)): + for y in range(grid.rows): + up = Point(line - x - 1, y) in grid.points + down = Point(line + x, y) in grid.points + if up and down: + continue + if up or down: + return False + return True + + for line in range(1, grid.lines): + if try_line(line): + return line + return None + + def solve(grid: Grid) -> int: + if (columns := reflect_vertical(grid)) is not None: + return columns + if (rows := reflect_horizontal(grid)) is not None: + return 100 * rows + assert False # Sanity check + + grids = parse(input) + return sum(map(solve, grids)) + + +def main() -> None: + input = sys.stdin.read() + print(solve(input)) + + +if __name__ == "__main__": + main()