Skip to content

Commit 1b3ad89

Browse files
committed
AoC 2024 Day 13 - cleanup
1 parent 1e0075f commit 1b3ad89

File tree

1 file changed

+49
-60
lines changed

1 file changed

+49
-60
lines changed

src/main/python/AoC2024_13.py

Lines changed: 49 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,16 @@
33
# Advent of Code 2024 Day 13
44
#
55

6+
from __future__ import annotations
7+
68
import sys
9+
from typing import NamedTuple
710

811
from aoc import my_aocd
912
from aoc.common import InputData
1013
from aoc.common import SolutionBase
1114
from aoc.common import aoc_samples
1215

13-
Input = list[tuple[tuple[int, int], tuple[int, int], tuple[int, int]]]
14-
Output1 = int
15-
Output2 = int
16-
17-
1816
TEST = """\
1917
Button A: X+94, Y+34
2018
Button B: X+22, Y+67
@@ -34,67 +32,58 @@
3432
"""
3533

3634

35+
class Machine(NamedTuple):
36+
ax: int
37+
bx: int
38+
ay: int
39+
by: int
40+
px: int
41+
py: int
42+
43+
@classmethod
44+
def from_input(cls, block: list[str]) -> Machine:
45+
a, b = ((int(block[i][12:14]), int(block[i][18:20])) for i in range(2))
46+
sp = block[2].split(", ")
47+
px, py = int(sp[0].split("=")[1]), int(sp[1][2:])
48+
return Machine(a[0], b[0], a[1], b[1], px, py)
49+
50+
51+
Input = list[Machine]
52+
Output1 = int
53+
Output2 = int
54+
55+
3756
class Solution(SolutionBase[Input, Output1, Output2]):
3857
def parse_input(self, input_data: InputData) -> Input:
39-
machines = []
40-
for block in my_aocd.to_blocks(input_data):
41-
a = (int(block[0][12:14]), int(block[0][18:20]))
42-
b = (int(block[1][12:14]), int(block[1][18:20]))
43-
sp = block[2].split(", ")
44-
px = int(sp[0].split("=")[1])
45-
py = int(sp[1][2:])
46-
machines.append((a, b, (px, py)))
47-
return machines
48-
49-
def guess(
50-
self, ax: int, bx: int, ay: int, by: int, px: int, py: int
51-
) -> int | None:
52-
# best = sys.maxsize
53-
div = bx * ay - ax * by
54-
ans_a = (py * bx - px * by) / div
55-
ans_b = (px * ay - py * ax) / div
56-
if int(ans_a) == ans_a:
57-
return int(ans_a) * 3 + int(ans_b)
58-
# for ans_a in range(100, 0, -1):
59-
# for ans_b in range(1, 101):
60-
# if (
61-
# ans_a * ax + ans_b * bx == px
62-
# and ans_a * ay + ans_b * by == py
63-
# ):
64-
# best = min(best, ans_a * 3 + ans_b)
65-
# if best < sys.maxsize:
66-
# return best
67-
else:
68-
return None
58+
return [
59+
Machine.from_input(block)
60+
for block in my_aocd.to_blocks(input_data)
61+
]
62+
63+
def solve(self, machines: list[Machine], offset: int = 0) -> int:
64+
def calc_tokens(machine: Machine, offset: int) -> int | None:
65+
px, py = machine.px + offset, machine.py + offset
66+
div = machine.bx * machine.ay - machine.ax * machine.by
67+
ans_a = (py * machine.bx - px * machine.by) / div
68+
ans_b = (px * machine.ay - py * machine.ax) / div
69+
if int(ans_a) == ans_a and int(ans_b) == ans_b:
70+
return int(ans_a) * 3 + int(ans_b)
71+
else:
72+
return None
73+
74+
return sum(
75+
tokens
76+
for tokens in (calc_tokens(m, offset) for m in machines)
77+
if tokens is not None
78+
)
6979

7080
def part_1(self, machines: Input) -> Output1:
71-
ans = 0
72-
for a, b, p in machines:
73-
ax, ay = a
74-
bx, by = b
75-
px, py = p
76-
g = self.guess(ax, bx, ay, by, px, py)
77-
if g is not None:
78-
ans += g
79-
return ans
81+
return self.solve(machines)
8082

8183
def part_2(self, machines: Input) -> Output2:
82-
MOD = 10_000_000_000_000
83-
ans = 0
84-
for a, b, p in machines:
85-
ax, ay = a
86-
bx, by = b
87-
px, py = p
88-
g = self.guess(ax, bx, ay, by, MOD + px, MOD + py)
89-
if g is not None:
90-
ans += g
91-
return ans
92-
93-
@aoc_samples(
94-
(
95-
("part_1", TEST, 480),
96-
)
97-
)
84+
return self.solve(machines, offset=10_000_000_000_000)
85+
86+
@aoc_samples((("part_1", TEST, 480),))
9887
def samples(self) -> None:
9988
pass
10089

0 commit comments

Comments
 (0)