diff --git a/puzzles/13.py b/puzzles/13.py new file mode 100644 index 0000000..ade66bc --- /dev/null +++ b/puzzles/13.py @@ -0,0 +1,96 @@ +import re +from textwrap import dedent +from typing import Iterator, NamedTuple, override +from unittest import TestCase +from puzzles._solver import Solver + + +class ClawMachine(NamedTuple): + ax: int + ay: int + bx: int + by: int + px: int + py: int + + +a_spec_pattern = re.compile(r"Button A: X\+(\d+), Y\+(\d+)") +b_spec_pattern = re.compile(r"Button B: X\+(\d+), Y\+(\d+)") +p_spec_pattern = re.compile(r"Prize: X=(\d+), Y=(\d+)") + + +def solve_machine(m: ClawMachine) -> tuple[float, float]: + c = (m.px * m.by - m.py * m.bx) / (m.ax * m.by - m.ay * m.bx) + d = (m.px - m.ax * c) / m.bx + return c, d + + +class DayThirteenSolver(Solver): + machines: list[ClawMachine] + + @override + def __init__(self, puzzle_input: str): + self.machines = [] + for spec in puzzle_input.strip().split("\n\n"): + a_spec, b_spec, p_spec = spec.split("\n") + ax, ay = map(int, a_spec_pattern.match(a_spec).groups()) + bx, by = map(int, b_spec_pattern.match(b_spec).groups()) + px, py = map(int, p_spec_pattern.match(p_spec).groups()) + self.machines.append(ClawMachine(ax, ay, bx, by, px, py)) + + @override + def solve_p1(self) -> int: + return sum( + int(c * 3 + d) + for c, d in map(solve_machine, self.machines) + if c.is_integer() and d.is_integer() + ) + + @override + def solve_p2(self) -> int: + corrected_machines = map( + lambda m: ClawMachine( + m.ax, + m.ay, + m.bx, + m.by, + m.px + 10000000000000, + m.py + 10000000000000, + ), + self.machines, + ) + return sum( + int(c * 3 + d) + for c, d in map(solve_machine, corrected_machines) + if c.is_integer() and d.is_integer() + ) + + +class TestDayThirteenSolver(TestCase): + def test(self): + solver = DayThirteenSolver( + dedent( + """ + Button A: X+94, Y+34 + Button B: X+22, Y+67 + Prize: X=8400, Y=5400 + + Button A: X+26, Y+66 + Button B: X+67, Y+21 + Prize: X=12748, Y=12176 + + Button A: X+17, Y+86 + Button B: X+84, Y+37 + Prize: X=7870, Y=6450 + + Button A: X+69, Y+23 + Button B: X+27, Y+71 + Prize: X=18641, Y=10279 + """ + ) + ) + self.assertEqual( + solver.machines[0], + ClawMachine(94, 34, 22, 67, 8400, 5400), + ) + self.assertEqual(solver.solve_p1(), 480)