import math from dataclasses import dataclass from typing import Any, Iterator import parse # pyright: ignore[reportMissingTypeStubs] from ..base import BaseSolver @dataclass(frozen=True) class Machine: prize: tuple[int, int] button_a: tuple[int, int] button_b: tuple[int, int] def read_machine(block: str) -> Machine: ba = parse.parse( # type: ignore """Button A: X{bax:d}, Y{bay:d} Button B: X{bbx:d}, Y{bby:d} Prize: X={px:d}, Y={py:d}""", block, ) return Machine( prize=(ba["px"], ba["py"]), # type: ignore button_a=(ba["bax"], ba["bay"]), # type: ignore button_b=(ba["bbx"], ba["bby"]), # type: ignore ) def diophantine(a: int, b: int, c: int) -> tuple[int, int]: q, r = divmod(a, b) if r == 0: return (0, c // b) else: u, v = diophantine(b, r, c) return (v, u - q * v) def solve(machine: Machine) -> int: (ax, ay), (bx, by), (px, py) = machine.button_a, machine.button_b, machine.prize dx, dy = math.gcd(ax, bx), math.gcd(ay, by) if px % dx != 0 or py % dy != 0: return 0 xa, xb = diophantine(ax, bx, px) ya, yb = diophantine(ay, by, py) # expr (x): xa - kx * bx / dx, xb + kx * ax / dx # expr (y): ya - ky * by / dy, yb + ky * ay / dy num = ay * (ya - xa) + by * (yb - xb) den = (ax * by - ay * bx) // dx if num % den != 0: return 0 kx = num // den pa, pb = xa - kx * bx // dx, xb + kx * ax // dx return 3 * pa + pb class Solver(BaseSolver): def solve(self, input: str) -> Iterator[Any]: machines = [read_machine(block) for block in input.split("\n\n")] yield sum(map(solve, machines)) shift = 10000000000000 machines = [ Machine( prize=(shift + m.prize[0], shift + m.prize[1]), button_a=m.button_a, button_b=m.button_b, ) for m in machines ] yield sum(map(solve, machines))