-
Notifications
You must be signed in to change notification settings - Fork 2
/
clawMachine.py
82 lines (63 loc) · 2.3 KB
/
clawMachine.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
#
# clawMachine.py
#
# From 13th day of Advent of Code 2024
# https://adventofcode.com/2024/day/13
#
# For each machine, you have the following information:
# - How many cells the A button moves the claw in the X and Y axis [buttonA]
# - How many cells the B button moves the claw in the X and Y axis [buttonB]
# - The X and Y coordinates of the prize [prize]
#
# It costs 3 tokens to push the A button and 1 token to push the B button.
#
# What is the smallest number of tokens you would have to spend
# to win as many prizes as possible?
#
# Z3 solution by dalps
#
from z3 import *
from collections import namedtuple
from pyparsing import *
Point = namedtuple("Point", ["x", "y"])
Machine = namedtuple("Machine", ["buttonA", "buttonB", "prize"])
integer = Word(nums)
movement = Suppress("Button " + (Literal("A") ^ Literal("B")) +
": X+") + integer("x") + Suppress(", Y+") + integer("y")
prize = Suppress("Prize: X=") + integer("px") + \
Suppress(", Y=") + integer("py")
expr = Group(movement("a") + movement("b") + prize("p"))
p = OneOrMore(expr)
machines = p.parseFile("clawMachine.txt")
def solve(prizeShift=0):
totalCost = 0 # Accumulate tokens spent on all machines
for m in machines:
m = Machine(buttonA=Point(int(m["a"][0]), int(m["a"][1])),
buttonB=Point(int(m["b"][0]), int(m["b"][1])),
prize=Point(int(m["p"][0]), int(m["p"][1])))
costA = Int("costA")
costB = Int("costB")
c = IntVal(prizeShift)
s = Solver()
s.add(m.buttonA.x * costA + m.buttonB.x * costB == m.prize.x + c)
s.add(m.buttonA.y * costA + m.buttonB.y * costB == m.prize.y + c)
print("Printing the assertions...")
for c in s.assertions():
print(c)
print("Solving constraints in the solver s...")
print(s.check())
if s.check() == sat:
# The prize can be reached, but at what cost?
# Let's extract the token expense from the model.
# [as_long()] converts a Z3 Integer to a regular integer
mdl = s.model()
machineCost = mdl[costA].as_long() * 3 + mdl[costB].as_long()
totalCost += machineCost
print("Traversing model...")
for d in mdl.decls():
print("%s = %s" % (d.name(), mdl[d]))
print("---")
return totalCost
solve1 = solve()
solve2 = solve(10000000000000)
print(solve1, solve2)