Skip to content

Commit e8c1725

Browse files
committed
2024-24 Refactoring
1 parent eb7a970 commit e8c1725

File tree

2 files changed

+61
-17
lines changed

2 files changed

+61
-17
lines changed

scala2/src/main/scala/jurisk/adventofcode/y2024/Advent24.scala

+59-17
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import jurisk.adventofcode.y2024.Advent24.Operation.Or
77
import jurisk.adventofcode.y2024.Advent24.Operation.Xor
88
import jurisk.algorithms.graph.GraphAlgorithms
99
import jurisk.collections.immutable.SetOfTwo
10+
import jurisk.math.LongOps
1011
import jurisk.utils.CollectionOps.OptionOps
1112
import jurisk.utils.ConversionOps.BooleanOps
1213
import jurisk.utils.ConversionOps.IntOps
@@ -16,6 +17,7 @@ import jurisk.utils.Parsing.StringOps
1617
import mouse.all.booleanSyntaxMouse
1718

1819
import scala.annotation.tailrec
20+
import scala.util.Random
1921

2022
object Advent24 extends IOApp.Simple {
2123
private val InputBits = 45
@@ -29,11 +31,27 @@ object Advent24 extends IOApp.Simple {
2931
def +(pair: (Wire, Boolean)): Values = new Values(map + pair)
3032
def ++(other: Values): Values = new Values(map ++ other.map)
3133

34+
private def bitsToLong(bits: Map[Int, Boolean]) = {
35+
val bitsStr = bits.toSeq.sorted
36+
.map { case (_, b) => if (b) "1" else "0" }
37+
.mkString
38+
.reverse
39+
java.lang.Long.parseLong(bitsStr, 2)
40+
}
41+
42+
def xValue: Long = {
43+
val z = map.collect { case (Wire.X(zIdx), v) => (zIdx, v) }
44+
bitsToLong(z)
45+
}
46+
47+
def yValue: Long = {
48+
val z = map.collect { case (Wire.Y(zIdx), v) => (zIdx, v) }
49+
bitsToLong(z)
50+
}
51+
3252
def zValue: Long = {
33-
val z = map.collect { case (Wire.Z(zIdx), v) => (zIdx, v) }.toList
34-
val zBits =
35-
z.sorted.map { case (_, b) => if (b) "1" else "0" }.mkString.reverse
36-
java.lang.Long.parseLong(zBits, 2)
53+
val z = map.collect { case (Wire.Z(zIdx), v) => (zIdx, v) }
54+
bitsToLong(z)
3755
}
3856
}
3957

@@ -47,6 +65,15 @@ object Advent24 extends IOApp.Simple {
4765
}.toMap
4866
}
4967

68+
def randomXY: Values = Values {
69+
(0 until InputBits).flatMap { b =>
70+
List(
71+
Wire.X(b) -> Random.nextBoolean(),
72+
Wire.Y(b) -> Random.nextBoolean(),
73+
)
74+
}.toMap
75+
}
76+
5077
def apply(pairs: (Wire, Boolean)*): Values = Values(pairs.toMap)
5178

5279
def parse(s: String): Values = {
@@ -105,15 +132,33 @@ object Advent24 extends IOApp.Simple {
105132
}
106133

107134
final case class Connections private (map: Map[Wire, Connection]) {
108-
val allWires: Set[Wire] = map.flatMap { case (k, v) =>
135+
val allWires: Set[Wire] = map.flatMap { case (k, v) =>
109136
Set(k, v.a, v.b)
110137
}.toSet
111-
val allOutputs: Set[Wire] = map.keySet
138+
private val allOutputs: Set[Wire] = map.keySet
112139

113140
def foreach(f: Connection => Unit): Unit = map.values foreach f
114141

115-
// TODO: This doesn't do a sufficient test, as these bit-by-bit tests don't catch all issues that could happen. Consider adding random numbers.
116-
private def errorsOnAddition: Int = {
142+
private def errorsOnAddition: Int =
143+
// We care more about `errorsBitByBit`, but since they didn't catch everything, we also care about `errorsOnRandomAddition`
144+
128 * errorsBitByBit + errorsOnRandomAddition
145+
146+
private def errorsOnRandomAddition: Int = {
147+
val Samples = 16
148+
(for {
149+
_ <- 0 until Samples
150+
r = Values.randomXY
151+
x = r.xValue
152+
y = r.yValue
153+
expectedZ = x + y
154+
solved = propagate(r).orFail("Failed to propagate")
155+
z = solved.zValue
156+
wrongBits = z ^ expectedZ
157+
wrongBitsCount = wrongBits.bitCount
158+
} yield wrongBitsCount).sum
159+
}
160+
161+
private def errorsBitByBit: Int = {
117162
def errorsAddingBit(bit: Int): Int =
118163
List(
119164
(false, false, false, false),
@@ -194,15 +239,12 @@ object Advent24 extends IOApp.Simple {
194239
(current, currentSwaps)
195240
} else {
196241
// TODO: Try to apply Genetic Algorithm or similar...
197-
// TODO: Have a wider set of swaps to pick from!
198-
val swaps = Set(
199-
SetOfTwo("hbk", "z14"),
200-
SetOfTwo("kvn", "z18"),
201-
SetOfTwo("dbb", "z23"),
202-
SetOfTwo("cvh", "tfn"),
203-
)
204-
val candidates = swaps.flatMap(_.toSet).map(Wire.parse).toIndexedSeq
205-
// val candidates = current.allOutputs
242+
// Note: The swaps for our data are:
243+
// 1. hbk <-> z14
244+
// 2. kvn <-> z18
245+
// 3. dbb <-> z23
246+
// 4. cvh <-> tfn
247+
val candidates = current.allOutputs.toIndexedSeq
206248
(for {
207249
aIdx <- candidates.indices
208250
bIdx <- candidates.indices

scala2/src/main/scala/jurisk/math/package.scala

+2
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,14 @@ package object math {
88
def parity: Int = n % 2
99
def halfRoundingUp: Int = n - halfRoundingDown
1010
def halfRoundingDown: Int = n / 2
11+
def bitCount: Int = java.lang.Integer.bitCount(n)
1112
}
1213

1314
implicit class LongOps(n: Long) {
1415
def parity: Long = n % 2
1516
def halfRoundingUp: Long = n - halfRoundingDown
1617
def halfRoundingDown: Long = n / 2
18+
def bitCount: Int = java.lang.Long.bitCount(n)
1719
}
1820

1921
def absForWrappingAround[N: Integral](x: N, y: N): N =

0 commit comments

Comments
 (0)