Skip to content

Commit ac5472a

Browse files
committed
Add code for day 8
1 parent 2a24583 commit ac5472a

File tree

1 file changed

+83
-0
lines changed

1 file changed

+83
-0
lines changed

2025/src/day08.scala

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
package day08
2+
3+
import scala.util.boundary
4+
import scala.util.boundary.break
5+
6+
import locations.Directory.currentDir
7+
import inputs.Input.loadFileSync
8+
9+
def loadInput(): String = loadFileSync(s"$currentDir/../input/day08")
10+
11+
12+
/**************/
13+
/* Data Model */
14+
/**************/
15+
16+
/** A junction box in 3D space with an associated circuit ID. */
17+
class Box(val x: Long, val y: Long, val z: Long, var circuit: Int):
18+
def distanceSquare(other: Box): Long =
19+
(x - other.x) * (x - other.x) + (y - other.y) * (y - other.y) + (z - other.z) * (z - other.z)
20+
21+
22+
/****************/
23+
/* Data Loading */
24+
/****************/
25+
26+
/** Parses comma-separated coordinates from the given `line` into a `Box` with
27+
* the given `circuit` ID.
28+
*/
29+
def parseBox(line: String, circuit: Int): Box =
30+
val parts = line.split(",")
31+
Box(parts(0).toLong, parts(1).toLong, parts(2).toLong, circuit)
32+
33+
/** Parses the input, returning a sequence of `Box`es and all unique pairs
34+
* of boxes sorted by distance.
35+
*/
36+
def load(input: String): (Seq[Box], Seq[(Box, Box)]) =
37+
val lines = input.linesIterator.filter(_.nonEmpty)
38+
val boxes = lines.zipWithIndex.map(parseBox).toSeq
39+
val pairsByDistance = boxes.pairs.toSeq.sortBy((b1, b2) => b1.distanceSquare(b2))
40+
(boxes, pairsByDistance)
41+
42+
extension [T](self: Seq[T])
43+
/** Generates all unique pairs (combinations of 2) from the sequence. */
44+
def pairs: Iterator[(T, T)] =
45+
self.combinations(2).map(pair => (pair(0), pair(1)))
46+
47+
48+
/**********/
49+
/* Part 1 */
50+
/**********/
51+
52+
@main def part1: Unit =
53+
println(s"The solution is ${part1(loadInput())}")
54+
55+
def part1(input: String): Int =
56+
val (boxes, pairsByDistance) = load(input)
57+
for (b1, b2) <- pairsByDistance.take(1000) if b1.circuit != b2.circuit do
58+
merge(b1.circuit, b2.circuit, boxes)
59+
val sizes = boxes.groupBy(_.circuit).values.map(_.size).toSeq.sortBy(-_)
60+
sizes.take(3).product
61+
62+
/** Sets all boxes with circuit `c2` to circuit `c1`. */
63+
def merge(c1: Int, c2: Int, boxes: Seq[Box]): Unit =
64+
for b <- boxes if b.circuit == c2 do b.circuit = c1
65+
66+
67+
/**********/
68+
/* Part 2 */
69+
/**********/
70+
71+
@main def part2: Unit =
72+
println(s"The solution is ${part2(loadInput())}")
73+
74+
def part2(input: String): Long =
75+
val (boxes, pairsByDistance) = load(input)
76+
var n = boxes.length
77+
boundary:
78+
for (b1, b2) <- pairsByDistance if b1.circuit != b2.circuit do
79+
merge(b1.circuit, b2.circuit, boxes)
80+
n -= 1
81+
if n <= 1 then
82+
break(b1.x * b2.x)
83+
throw Exception("Should not reach here")

0 commit comments

Comments
 (0)