Skip to content

Commit 55bb00b

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

File tree

1 file changed

+82
-0
lines changed

1 file changed

+82
-0
lines changed

2025/src/day08.scala

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

0 commit comments

Comments
 (0)