Added day 13
This commit is contained in:
parent
c442207f20
commit
629b2e47fb
4 changed files with 1442 additions and 0 deletions
15
day13/example.txt
Normal file
15
day13/example.txt
Normal file
|
@ -0,0 +1,15 @@
|
|||
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
|
1279
day13/input.txt
Normal file
1279
day13/input.txt
Normal file
File diff suppressed because it is too large
Load diff
78
day13/solution.py
Normal file
78
day13/solution.py
Normal file
|
@ -0,0 +1,78 @@
|
|||
from argparse import ArgumentParser
|
||||
|
||||
def parse_line(line: str) -> tuple[int, int]:
|
||||
line = line.strip()
|
||||
parts = line.split(':', 1)
|
||||
coords_part = parts[1].strip()
|
||||
x_part, y_part = coords_part.split(',')
|
||||
x_val = x_part.strip()[1:]
|
||||
y_val = y_part.strip()[1:]
|
||||
return int(x_val), int(y_val)
|
||||
|
||||
def is_solvable(XA, YA, XB, YB, Xp, Yp, max_presses=100):
|
||||
best_cost = None
|
||||
for a in range(max_presses+1):
|
||||
for b in range(max_presses+1):
|
||||
X = XA * a + XB * b
|
||||
Y = YA * a + YB * b
|
||||
if X == Xp and Y == Yp:
|
||||
cost = 3*a + b
|
||||
if best_cost is None or cost < best_cost:
|
||||
best_cost = cost
|
||||
return best_cost
|
||||
|
||||
def format_buttons(a: str, b: str, prize: str) -> dict:
|
||||
XA, YA = parse_line(a)
|
||||
XB, YB = parse_line(b)
|
||||
_, p_coords = prize.split(':', 1)
|
||||
p_coords = p_coords.strip()
|
||||
# p_coords = "X=8400, Y=5400"
|
||||
x_part, y_part = p_coords.split(',')
|
||||
Xp = int(x_part.strip()[2:])
|
||||
Yp = int(y_part.strip()[2:])
|
||||
return {
|
||||
"A": {"X": XA, "Y": YA},
|
||||
"B": {"X": XB, "Y": YB},
|
||||
"PRIZE": {"X": Xp, "Y": Yp}
|
||||
}
|
||||
|
||||
def main(machines: list[list[str]]) -> int:
|
||||
results = []
|
||||
for part in machines:
|
||||
button_a = part[0]
|
||||
button_b = part[1]
|
||||
prize = part[2]
|
||||
|
||||
formatted = format_buttons(button_a, button_b, prize)
|
||||
|
||||
XA, YA = formatted["A"]["X"], formatted["A"]["Y"]
|
||||
XB, YB = formatted["B"]["X"], formatted["B"]["Y"]
|
||||
Xp, Yp = formatted["PRIZE"]["X"], formatted["PRIZE"]["Y"]
|
||||
|
||||
cost = is_solvable(XA, YA, XB, YB, Xp, Yp, max_presses=100)
|
||||
if cost is not None:
|
||||
results.append(cost)
|
||||
|
||||
if not results:
|
||||
return 0
|
||||
return sum(results)
|
||||
|
||||
|
||||
def parse_input(input_file) -> list[list[str]]:
|
||||
with open(input_file, "r") as f:
|
||||
content = f.read().strip()
|
||||
machine_blocks = content.split("\n\n")
|
||||
machines = []
|
||||
for block in machine_blocks:
|
||||
lines = block.strip().splitlines()
|
||||
if len(lines) == 3:
|
||||
machines.append(lines)
|
||||
return machines
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = ArgumentParser(description="Solve claw machine problem")
|
||||
parser.add_argument("-f", "--file", required=True, help="Input file")
|
||||
args = parser.parse_args()
|
||||
input_data = parse_input(args.file)
|
||||
print(main(input_data))
|
70
day13/solution2.py
Normal file
70
day13/solution2.py
Normal file
|
@ -0,0 +1,70 @@
|
|||
from argparse import ArgumentParser
|
||||
import sys
|
||||
|
||||
sys.setrecursionlimit(10**7)
|
||||
|
||||
OFFSET = 10_000_000_000_000
|
||||
|
||||
def parse_line(line: str) -> tuple[int, int]:
|
||||
line = line.strip()
|
||||
_, coords = line.split(":", 1)
|
||||
coords = coords.strip()
|
||||
x_part, y_part = coords.split(",")
|
||||
x_val = x_part.strip()[1:].replace("=", "")
|
||||
y_val = y_part.strip()[1:].replace("=", "")
|
||||
return int(x_val), int(y_val)
|
||||
|
||||
def parse_input(in_path: str) -> list[list[str]]:
|
||||
with open(in_path, "r") as f:
|
||||
content = f.read().strip()
|
||||
blocks = content.split("\n\n")
|
||||
res = []
|
||||
for block in blocks:
|
||||
lines = block.strip().splitlines()
|
||||
if len(lines) == 3:
|
||||
res.append(lines)
|
||||
return res
|
||||
|
||||
def extended_gcd(a: int, b: int) -> tuple[int, int, int]:
|
||||
if b == 0:
|
||||
return a, 1, 0
|
||||
g, x1, y1 = extended_gcd(b, a % b)
|
||||
return g, y1, x1 - (a // b)*y1
|
||||
|
||||
def solve_diophantine_system(xa: int, ya: int, xb: int, yb: int, xp: int, yp: int) -> tuple[int, int] | None:
|
||||
# Solve: xa*a + xb*b = xp and ya*a + yb*b = yp
|
||||
D = xa*yb - xb*ya
|
||||
if D == 0:
|
||||
return None
|
||||
a_num = yb*xp - xb*yp
|
||||
b_num = xa*yp - ya*xp
|
||||
if a_num % D != 0 or b_num % D != 0:
|
||||
return None
|
||||
a0 = a_num // D
|
||||
b0 = b_num // D
|
||||
if a0 < 0 or b0 < 0:
|
||||
return None
|
||||
return a0, b0
|
||||
|
||||
def main(in_path: str) -> int:
|
||||
machines = parse_input(in_path)
|
||||
costs = []
|
||||
for m in machines:
|
||||
xa, ya = parse_line(m[0])
|
||||
xb, yb = parse_line(m[1])
|
||||
xp, yp = parse_line(m[2])
|
||||
xp += OFFSET
|
||||
yp += OFFSET
|
||||
sol = solve_diophantine_system(xa, ya, xb, yb, xp, yp)
|
||||
if sol is not None:
|
||||
a, b = sol
|
||||
costs.append(3*a + b)
|
||||
if not costs:
|
||||
return 0
|
||||
return sum(costs)
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = ArgumentParser()
|
||||
parser.add_argument("-f", "--file", required=True)
|
||||
args = parser.parse_args()
|
||||
print(main(args.file))
|
Loading…
Reference in a new issue