You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
- Three coordinates (`x`, `y`, `z`) representing its position in 3D space
24
+
- A mutable `circuit` field to track which circuit the box belongs to (each circuit is identified by a distinct integer)
25
+
- A `distanceSquare` method that computes the squared Euclidean distance to another box (we use squared distance to avoid computing square roots, since we only need to compare distances)
26
+
27
+
## Data Loading
28
+
29
+
The following functions parse the input into a sequence of boxes and compute all unique pairs sorted by distance:
30
+
31
+
```scala
32
+
/** Parses comma-separated coordinates from the given `line` into a `Box`
For Part 1, we process the 1000 closest pairs of boxes and merge their circuits. The algorithm iterates through pairs in order of increasing distance; when two boxes belong to different circuits, we merge them into one. Finally, we find the three largest circuits and return the product of their sizes.
61
+
62
+
```scala
63
+
defpart1(input: String):Int=
64
+
val (boxes, pairsByDistance) = load(input)
65
+
for (b1, b2) <- pairsByDistance.take(1000) if b1.circuit != b2.circuit do
The `merge` function updates all boxes in one circuit to belong to another:
72
+
73
+
```scala
74
+
/** Sets all boxes with circuit `c2` to circuit `c1`. */
75
+
defmerge(c1: Int, c2: Int, boxes: Seq[Box]):Unit=
76
+
for b <- boxes if b.circuit == c2 do b.circuit = c1
77
+
```
78
+
79
+
80
+
## Part 2
81
+
82
+
For Part 2, we continue merging circuits until only one remains. We track the number of distinct circuits and return the product of the x-coordinates of the two boxes in the final merge.
83
+
84
+
```scala
85
+
defpart2(input: String):Long=
86
+
val (boxes, pairsByDistance) = load(input)
87
+
varn= boxes.length
88
+
boundary:
89
+
for (b1, b2) <- pairsByDistance if b1.circuit != b2.circuit do
90
+
merge(b1.circuit, b2.circuit, boxes)
91
+
n -=1
92
+
if n <=1then
93
+
break(b1.x * b2.x)
94
+
throwException("Should not reach here")
95
+
```
96
+
97
+
[`boundary` and `break`](https://www.scala-lang.org/api/3.x/scala/util/boundary$.html) provide a way to exit the loop early and return a value when only one circuit remains.
98
+
99
+
## Potential Optimizations
100
+
101
+
On my machine, both parts run in under two seconds, which is acceptable for this puzzle. Still, several optimizations are possible.
102
+
103
+
What we implemented is essentially the [Euclidean minimum spanning tree (EMST)](https://en.wikipedia.org/wiki/Euclidean_minimum_spanning_tree) computed via [Kruskal’s algorithm](https://en.wikipedia.org/wiki/Kruskal%27s_algorithm), after generating all pairwise distances.
104
+
105
+
This algorithm is normally paired with a [union–find](https://en.wikipedia.org/wiki/Disjoint-set_data_structure) structure to maintain connected components efficiently. Using it would speed up our current `merge` step, which is $\mathcal{O}(n)$ per merge, and reduce it to near-constant time.
106
+
107
+
We could also improve how we find the $k$ closest pairs. Computing all pairs and sorting them has $\mathcal{O}(n^2 \log n)$ complexity. A spatial index such as a [k-d tree](https://en.wikipedia.org/wiki/K-d_tree) would avoid generating all pairs and, in the average case, remove the quadratic blow-up. Another option is to restrict candidates to a geometric graph such as the [relative neighborhood graph](https://en.wikipedia.org/wiki/Relative_neighborhood_graph) or the 3D [Delaunay triangulation](https://en.wikipedia.org/wiki/Delaunay_triangulation), both of which contain the EMST and are much sparser than the complete graph.
108
+
109
+
## Final Code
110
+
111
+
See the complete code on [GitHub](https://github.com/scalacenter/scala-advent-of-code/blob/main/2025/src/day08.scala).
112
+
113
+
## Run it in the browser
114
+
115
+
Thanks to the [Scala.js](https://www.scala-js.org/) build, you can also experiment with this code directly in the browser.
116
+
117
+
### Part 1
118
+
119
+
<Solverpuzzle="day08-part1"year="2025"/>
120
+
121
+
### Part 2
122
+
123
+
<Solverpuzzle="day08-part2"year="2025"/>
124
+
9
125
## Solutions from the community
10
126
11
127
-[Solution](https://github.com/merlinorg/advent-of-code/blob/main/src/main/scala/year2025/day08.scala) by [merlinorg](https://github.com/merlinorg)
0 commit comments