Mikaël Capelle 67e41503c9
All checks were successful
continuous-integration/drone/push Build is passing
2024 day 13.
2024-12-13 09:15:23 +01:00

80 lines
2.0 KiB
Python

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))