@@ -13,71 +13,86 @@ object Day10 {
1313
1414 case class Machine (lights : Lights , buttons : Buttons , joltages : Joltages )
1515
16- def fewestPresses (machine : Machine ): Int = {
17- val graphSearch = new GraphSearch [Lights ] with UnitNeighbors [Lights ] with TargetNode [Lights ] {
18- override val startNode : Lights = machine.lights.map(_ => false )
16+ trait Part {
17+ def fewestPresses (machine : Machine ): Int
1918
20- override def unitNeighbors ( lights : Lights ): IterableOnce [ Lights ] =
21- machine.buttons.map(_.foldLeft(lights)((acc, i) => acc.updated(i, ! acc(i))))
19+ def sumFewestPresses ( machines : Seq [ Machine ] ): Int = machines.map(fewestPresses).sum
20+ }
2221
23- override val targetNode : Lights = machine.lights
24- }
22+ object Part1 extends Part {
23+ override def fewestPresses (machine : Machine ): Int = {
24+ val graphSearch = new GraphSearch [Lights ] with UnitNeighbors [Lights ] with TargetNode [Lights ] {
25+ override val startNode : Lights = machine.lights.map(_ => false )
2526
26- BFS .search(graphSearch).target.get._2
27- }
27+ override def unitNeighbors ( lights : Lights ) : IterableOnce [ Lights ] =
28+ machine.buttons.map(_.foldLeft(lights)((acc, i) => acc.updated(i, ! acc(i))))
2829
29- def sumFewestPresses (machines : Seq [Machine ]): Int = machines.map(fewestPresses).sum
30+ override val targetNode : Lights = machine.lights
31+ }
3032
31- /*
32- x0 x1 x2 x3 x4 x5
33- (3) (1,3) (2) (2,3) (0,2) (0,1)
34- 0: x4 x5 = 3
35- 1: x1 x5 = 5
36- 2: x2 x3 x4 = 4
37- 3: x0 x3 = 7
33+ BFS .search(graphSearch).target.get._2
34+ }
35+ }
36+
37+ trait Part2Solution extends Part
3838
39+ /**
40+ * Solution, which naively finds fewest presses by BFS.
41+ * Does not scale to inputs.
3942 */
43+ object NaivePart2Solution extends Part2Solution {
44+ override def fewestPresses (machine : Machine ): Int = {
45+ val graphSearch = new GraphSearch [Joltages ] with UnitNeighbors [Joltages ] with TargetNode [Joltages ] {
46+ override val startNode : Joltages = machine.joltages.map(_ => 0 )
4047
41- // TODO: optimize
42- def fewestPresses2 (machine : Machine ): Int = {
43- /* val graphSearch = new GraphSearch[Joltages] with UnitNeighbors[Joltages] with TargetNode[Joltages] {
44- override val startNode: Joltages = machine.joltages.map(_ => 0)
48+ override def unitNeighbors (joltages : Joltages ): IterableOnce [Joltages ] =
49+ machine.buttons.map(_.foldLeft(joltages)((acc, i) => acc.updated(i, acc(i) + 1 )))
4550
46- override def unitNeighbors(joltages : Joltages): IterableOnce[Joltages] =
47- machine.buttons.map(_.foldLeft(joltages)((acc, i) => acc.updated(i, acc(i) + 1)))
51+ override val targetNode : Joltages = machine.joltages
52+ }
4853
49- override val targetNode: Joltages = machine.joltages
54+ BFS .search(graphSearch).target.get._2
5055 }
56+ }
5157
52- BFS.search(graphSearch).target.get._2*/
53-
54- val ctx = new Context (Map (" model" -> " true" ).asJava)
55- import ctx ._
56- val s = mkOptimize()
57-
58- val buttonVars = machine.buttons.zipWithIndex.map((_, i) => mkIntConst(s " x $i" ))
59- val lhss =
60- machine.buttons
61- .lazyZip(buttonVars)
62- .foldLeft(machine.joltages.map[ArithExpr [IntSort ]](i => mkInt(i)))({ case (accs, (button, buttonVar)) =>
63- button.foldLeft(accs)((accs, i) => accs.updated(i, mkSub(accs(i), buttonVar)))
58+ /**
59+ * Solution, which finds fewest presses via an ILP problem, solved by Z3.
60+ */
61+ object Z3Part2Solution extends Part2Solution {
62+ /*
63+ x0 x1 x2 x3 x4 x5
64+ (3) (1,3) (2) (2,3) (0,2) (0,1)
65+ 0: x4 x5 = 3
66+ 1: x1 x5 = 5
67+ 2: x2 x3 x4 = 4
68+ 3: x0 x3 = 7
69+ */
70+
71+ override def fewestPresses (machine : Machine ): Int = {
72+ val ctx = new Context (Map (" model" -> " true" ).asJava)
73+ import ctx ._
74+ val s = mkOptimize()
75+
76+ val buttonPresses = machine.buttons.zipWithIndex.map((_, i) => mkIntConst(s " x $i" ))
77+ for (presses <- buttonPresses)
78+ s.Add (mkGe(presses, mkInt(0 )))
79+
80+ val totalPresses = buttonPresses.foldLeft[ArithExpr [IntSort ]](mkInt(0 ))(mkAdd(_, _))
81+ s.MkMinimize (totalPresses)
82+
83+ (machine.buttons lazyZip buttonPresses)
84+ .foldLeft(machine.joltages.map[ArithExpr [IntSort ]](mkInt))({ case (acc, (button, presses)) =>
85+ button.foldLeft(acc)((acc, i) => acc.updated(i, mkSub(acc(i), presses)))
6486 })
87+ .foreach(joltageLeft =>
88+ s.Add (mkEq(joltageLeft, mkInt(0 )))
89+ )
6590
66- for (lhs <- lhss)
67- s.Add (mkEq(lhs, mkInt(0 )))
68-
69- for (v <- buttonVars)
70- s.Add (mkGe(v, mkInt(0 )))
71-
72- val presses = buttonVars.foldLeft[ArithExpr [IntSort ]](mkInt(0 ))((acc, v) => mkAdd(acc, v))
73- s.MkMinimize (presses)
74- assert(s.Check () == Status .SATISFIABLE )
75- // println(s.getModel)
76- s.getModel.evaluate(presses, false ).toString.toInt
91+ assert(s.Check () == Status .SATISFIABLE )
92+ s.getModel.evaluate(totalPresses, false ).toString.toInt
93+ }
7794 }
7895
79- def sumFewestPresses2 (machines : Seq [Machine ]): Int = machines.map(fewestPresses2).tapEach(println).sum
80-
8196 def parseMachine (s : String ): Machine = s match {
8297 case s " [ $lightsStr] $buttonsStr { $joltagesStr} " =>
8398 val lights = lightsStr.map(_ == '#' ).toVector
@@ -91,7 +106,7 @@ object Day10 {
91106 lazy val input : String = scala.io.Source .fromInputStream(getClass.getResourceAsStream(" day10.txt" )).mkString.trim
92107
93108 def main (args : Array [String ]): Unit = {
94- println(sumFewestPresses(parseMachines(input)))
95- println(sumFewestPresses2 (parseMachines(input)))
109+ println(Part1 . sumFewestPresses(parseMachines(input)))
110+ println(Z3Part2Solution .sumFewestPresses (parseMachines(input)))
96111 }
97112}
0 commit comments