11package me.peckb.aoc._2025.calendar.day10
22
3- import com.microsoft.z3.ArithExpr
3+ import com.microsoft.z3.BitVecNum
4+ import com.microsoft.z3.BitVecSort
45import com.microsoft.z3.Context
5- import com.microsoft.z3.IntExpr
6- import com.microsoft.z3.IntNum
7- import com.microsoft.z3.IntSort
6+ import com.microsoft.z3.Expr
7+ import com.microsoft.z3.Optimize
88import com.microsoft.z3.Status
99import javax.inject.Inject
1010import me.peckb.aoc.generators.InputGenerator.InputGeneratorFactory
11- import kotlin.collections.component1
12- import kotlin.collections.component2
11+ import org.apache.commons.math3.stat.inference.TestUtils.g
1312import kotlin.math.min
13+ import kotlin.use
1414
1515class Day10 @Inject constructor(
1616 private val generatorFactory : InputGeneratorFactory ,
1717) {
1818 fun partOne (filename : String ) = generatorFactory.forFile(filename).readAs(::day10) { input ->
19- input.sumOf { machine -> flipLights(machine.indicatorLights, machine.buttonSchematics) }
19+ input.sumOf { machine ->
20+ thing(" ." .repeat(machine.indicatorLights.length), machine.indicatorLights, machine.buttonSchematics)
21+ }
2022 }
2123
2224 fun partTwo (filename : String ) = generatorFactory.forFile(filename).readAs(::day10) { input ->
23- input.sumOf { machine -> enableJoltages(machine) }
24- }
25-
26- private fun enableJoltages (machine : Machine ) : Int {
27- return Context ().use { ctx ->
28- val opt = ctx.mkOptimize()
29-
30- fun intVariableOf (name : String ) = ctx.mkIntConst(name)
31-
32- fun intValueOf (value : Int ) = ctx.mkInt(value)
33-
34- infix fun IntExpr.gte (t : IntExpr ) = ctx.mkGe(this , t)
35-
36- operator fun ArithExpr<IntSort>.plus (t : IntExpr ) = ctx.mkAdd(this , t)
37-
38- infix fun ArithExpr<IntSort>.equalTo (t : IntExpr ) = ctx.mkEq(this , t)
39-
40- fun List<IntExpr>.sum () = ctx.mkAdd(* this .toTypedArray())
41-
42- val ZERO = intValueOf(0 )
43-
44- val affectedJoltages = mutableMapOf<Int , MutableList <IntExpr >>()
45- val buttonVariables = machine.buttonSchematics.mapIndexed { index, schematic ->
46- // create button variable
47- intVariableOf(index.toString()).also { buttonVariable ->
48- // track the joltages if affects
49- schematic.affectedIndices.forEach { affectedIndex ->
50- affectedJoltages.computeIfAbsent(affectedIndex) { _ -> mutableListOf () }.add(buttonVariable)
51- }
52- // add it to the optimizer
53- opt.Add (buttonVariable gte ZERO )
54- }
55- }
56-
57- affectedJoltages.forEach { (joltageIndex, buttonsAffecting) ->
58- val targetValue = intValueOf(machine.joltages[joltageIndex])
59- val sumOfButtonPresses = buttonsAffecting.sum()
60- opt.Add (sumOfButtonPresses equalTo targetValue)
61- }
62-
63- opt.MkMinimize (buttonVariables.sum())
64-
65- val status = opt.Check ()
66-
67- if (status == Status .SATISFIABLE ) {
68- val model = opt.getModel()
69- ((model.evaluate(buttonVariables.sum(), true ) as IntNum ).int)
70- } else {
71- throw IllegalStateException (" No Solution Found" )
25+ input.sumOf { machine ->
26+
27+ Context ().use { ctx ->
28+ var bestFound = Int .MAX_VALUE
29+
30+ val bits = 64 // the answer will fit inside a long
31+
32+ fun valueOf (value : Int ) = ctx.mkBV(value, bits)
33+
34+ fun variableOf (name : String ) = ctx.mkBVConst(name, bits)
35+
36+ infix fun Expr<BitVecSort>.equalTo (t : Expr <BitVecSort >) = ctx.mkEq(this , t)
37+
38+ infix fun Expr<BitVecSort>.notEqual (t : Expr <BitVecSort >) = ctx.mkNot(this equalTo t)
39+
40+ operator fun Expr<BitVecSort>.plus (t : Expr <BitVecSort >) = ctx.mkBVAdd(this , t)
41+
42+ operator fun Expr<BitVecSort>.times (t : Expr <BitVecSort >) = ctx.mkBVMul(this , t)
43+
44+ infix fun Expr<BitVecSort>.lessThan (t : Expr <BitVecSort >) = ctx.mkBVSLT(this , t)
45+
46+ infix fun Expr<BitVecSort>.gte (t : Expr <BitVecSort >) = ctx.mkBVSGE(this , t)
47+
48+ val a = variableOf(" a" )
49+ val b = variableOf(" b" )
50+ val c = variableOf(" c" )
51+ val d = variableOf(" d" )
52+ val e = variableOf(" e" )
53+ val f = variableOf(" f" )
54+
55+ val optimizer = ctx.mkOptimize()
56+
57+ optimizer.Add (e + f equalTo valueOf(3 ))
58+ optimizer.Add (b + f equalTo valueOf(5 ))
59+ optimizer.Add (c + d + e equalTo valueOf(4 ))
60+ optimizer.Add (a + b + d equalTo valueOf(7 ))
61+ // optimizer.Add(a lessThan valueOf(10000))
62+ // optimizer.Add(b lessThan valueOf(10000))
63+ // optimizer.Add(c lessThan valueOf(10000))
64+ // optimizer.Add(d lessThan valueOf(10000))
65+ // optimizer.Add(e lessThan valueOf(10000))
66+ // optimizer.Add(f lessThan valueOf(10000))
67+ optimizer.Add (a gte valueOf(0 ))
68+ optimizer.Add (b gte valueOf(0 ))
69+ optimizer.Add (c gte valueOf(0 ))
70+ optimizer.Add (d gte valueOf(0 ))
71+ optimizer.Add (e gte valueOf(0 ))
72+ optimizer.Add (f gte valueOf(0 ))
73+
74+ optimizer.MkMinimize (a + b + c + d + e + f)
75+
76+ optimizer.Check ()
77+ // while(optimizer.Check() == Status.SATISFIABLE) {
78+ val model = optimizer.model
79+
80+ val aEval = (model.evaluate(a, true ) as BitVecNum )
81+ val bEval = (model.evaluate(b, true ) as BitVecNum )
82+ val cEval = (model.evaluate(c, true ) as BitVecNum )
83+ val dEval = (model.evaluate(d, true ) as BitVecNum )
84+ val eEval = (model.evaluate(e, true ) as BitVecNum )
85+ val fEval = (model.evaluate(f, true ) as BitVecNum )
86+
87+ bestFound = min(bestFound, aEval.int + bEval.int + cEval.int + dEval.int + eEval.int + fEval.int)
88+
89+ // optimizer.Add(
90+ // ctx.mkOr(
91+ // a notEqual aEval,
92+ // b notEqual bEval,
93+ // c notEqual cEval,
94+ // d notEqual dEval,
95+ // e notEqual eEval,
96+ // f notEqual fEval,
97+ // )
98+ // )
99+ // }
100+ // println(bestFound)
101+ bestFound
72102 }
73103 }
74104 }
75105
76- fun flipLights (goal : String , buttonSchematics : List <ButtonSchematic >): Long {
77- val minPressesForState = mutableMapOf (" ." .repeat(goal.length) to 0L )
106+
107+ fun thing (current : String , goal : String , buttonSchematics : List <ButtonSchematic >): Long {
108+ val minPressesForState = mutableMapOf (current to 0L )
78109 while (! minPressesForState.containsKey(goal)) {
79110 minPressesForState.entries.toList().forEach { (light, cost) ->
80111 val nextStates = possibleLights(light, buttonSchematics)
@@ -97,7 +128,7 @@ class Day10 @Inject constructor(
97128
98129 private fun applyLightPress (current : String , buttonSchematic : ButtonSchematic ): String {
99130 return current.mapIndexed { index, c ->
100- if (buttonSchematic.affectedIndices .contains(index)) {
131+ if (buttonSchematic.lightsAffected .contains(index)) {
101132 if (c == ' #' ) { ' .' } else { ' #' }
102133 } else { c }
103134 }.joinToString(" " )
@@ -122,4 +153,6 @@ data class Machine(
122153 val joltages : List <Int >,
123154)
124155
125- data class ButtonSchematic (val affectedIndices : List <Int >)
156+ /* * zero indexed */
157+ // TODO rename
158+ data class ButtonSchematic (val lightsAffected : List <Int >)
0 commit comments