Skip to content

Commit 4034eff

Browse files
authored
Utilize Z3 (#280)
1 parent 4080e10 commit 4034eff

File tree

2 files changed

+65
-1
lines changed

2 files changed

+65
-1
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,7 @@ as the value which passed the given day/phase combination
160160
#### 2024
161161
* Day 11 The fact that the order of stones didn't actually matter helped to allow us to just keep a list of how many we had.
162162
* Day 13 Being able to solve the basic with shortest path while needing math for part two is always fun re-learning linear algebra. And refactoring always speeds things up.
163+
* Day 17 Another good example of how powerful Z3 can really be.
163164

164165
## Takeaways
165166

src/main/kotlin/me/peckb/aoc/_2024/calendar/day17/Day17.kt

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
package me.peckb.aoc._2024.calendar.day17
22

3+
import com.microsoft.z3.BitVecExpr
4+
import com.microsoft.z3.BitVecNum
5+
import com.microsoft.z3.BitVecSort
6+
import com.microsoft.z3.Context
7+
import com.microsoft.z3.Expr
8+
import com.microsoft.z3.Status
39
import javax.inject.Inject
410
import me.peckb.aoc.generators.InputGenerator.InputGeneratorFactory
511
import kotlin.math.pow
@@ -16,7 +22,64 @@ class Day17 @Inject constructor(
1622
fun partTwo(filename: String) = generatorFactory.forFile(filename).read { input ->
1723
val computer = Computer.fromInput(input)
1824

19-
findProgram(computer, computer.operations.map{ it.toLong() })
25+
val recursive = findProgram(computer, computer.operations.map{ it.toLong() })
26+
val maths = uzeZ3(computer.operations)
27+
28+
if (recursive != maths) throw IllegalStateException("Uh oh!")
29+
30+
maths
31+
}
32+
33+
private fun uzeZ3(operations: List<Int>): Long {
34+
var result: Long = -1
35+
36+
Context().use { ctx ->
37+
val bits = 64 // the answer will fit inside a long
38+
39+
fun valueOf(value: Int) = ctx.mkBV(value, bits)
40+
41+
fun variableOf(name: String) = ctx.mkBVConst(name, bits)
42+
43+
operator fun Expr<BitVecSort>.rem(t: Expr<BitVecSort>) = ctx.mkBVSMod(this, t)
44+
45+
infix fun Expr<BitVecSort>.xor(t: Expr<BitVecSort>) = ctx.mkBVXOR(this, t)
46+
47+
infix fun Expr<BitVecSort>.equalTo(t: Expr<BitVecSort>) = ctx.mkEq(this, t)
48+
49+
infix fun Expr<BitVecSort>.shr(t: Expr<BitVecSort>) = ctx.mkBVLSHR(this, t)
50+
51+
val eight = valueOf(8)
52+
val five = valueOf(5)
53+
val six = valueOf(6)
54+
val three = valueOf(3)
55+
val zero = valueOf(0)
56+
57+
val a = variableOf("a")
58+
59+
var aReg = a
60+
var bReg: BitVecExpr
61+
var cReg: BitVecExpr
62+
63+
with(ctx.mkOptimize()) {
64+
operations.forEach { op ->
65+
bReg = aReg % eight
66+
bReg = bReg xor five
67+
cReg = aReg shr bReg
68+
bReg = bReg xor six
69+
bReg = bReg xor cReg
70+
Add(bReg % eight equalTo valueOf(op))
71+
aReg = aReg shr three
72+
}
73+
Add(aReg equalTo zero)
74+
MkMinimize(a)
75+
76+
if (Check() == Status.SATISFIABLE) {
77+
result = (model.evaluate(a, true) as BitVecNum).long
78+
}
79+
}
80+
}
81+
82+
return result
2083
}
2184

2285
private fun findProgram(computer: Computer, target: List<Long>): Long {

0 commit comments

Comments
 (0)