Day 8: Playground
Megathread guidelines
- Keep top level comments as only solutions, if you want to say something other than a solution put it in a new post. (replies to comments can be whatever)
- You can send code in code blocks by using three backticks, the code, and then three backticks or use something such as https://topaz.github.io/paste/ if you prefer sending it through a URL
FAQ
- What is this?: Here is a post with a large amount of details: https://programming.dev/post/6637268
- Where do I participate?: https://adventofcode.com/
- Is there a leaderboard for the community?: We have a programming.dev leaderboard with the info on how to join in this post: https://programming.dev/post/6631465


Kotlin
First tricky puzzle today, instantly leading me to more or less brute force for part2. I took a lot of time to understand which data structures I needed today, and how to compute my matrix.
Runtimes:
part1: 141ms
part2: 45.9 seconds .-.
Solution
class Day08 : Puzzle { lateinit var boxes: List<Point3D> lateinit var edges: List<Pair<List<Point3D>, Double>> override fun readFile() { val input = readInputFromFile(2025, 8, false) boxes = input.lines().filter { it.isNotBlank() } .map { it.split(",") } .map { Point3D(it[0].toInt(), it[1].toInt(), it[2].toInt()) } edges = calculateEdges(boxes) } private fun calculateEdges(boxes: List<Point3D>): List<Pair<List<Point3D>, Double>> { val edges = mutableListOf<Pair<MutableList<Point3D>, Double>>() for (i in boxes.indices) { for (j in i + 1 until boxes.size) { edges.add(Pair(mutableListOf(boxes[i], boxes[j]), boxes[i].dist(boxes[j]))) } } return edges.sortedBy { it.second } } override fun solvePartOne(): String { val connections = buildEmptyConnectionMatrix(boxes.size) val mutableEdges = edges.toMutableList() for (i in 0..<1000) { connectNextEdge(mutableEdges, connections) } val connectionSet = buildConnectionSet(boxes, connections) return connectionSet.map { it.size } .sortedByDescending { it } .take(3) .reduce(Int::times) .toString() } override fun solvePartTwo(): String { val connections = buildEmptyConnectionMatrix(boxes.size) val mutableEdges = edges.toMutableList() var result: Long? = null while (result == null) { result = connectNextEdge(mutableEdges, connections, true) } return result.toString() } // size: width & height of (square) matrix private fun buildEmptyConnectionMatrix(size: Int): Array<Array<Boolean>> { val connections = Array(size) { Array(size) { false } } for (i in connections.indices) { connections[i][i] = true } return connections } private fun connectNextEdge(mutableEdges: MutableList<Pair<List<Point3D>, Double>>, connections: Array<Array<Boolean>>, part2: Boolean = false): Long? { if (mutableEdges.isEmpty()) return null val next = mutableEdges[0] val point = next.first[0] val other = next.first[1] connectAll(boxes.indexOf(point), boxes.indexOf(other), connections) mutableEdges.remove(next) // all nodes are connected, assume that this is happening for the first time return if (part2 && connections[0].all { it }) { next.first[0].x.toLong() * next.first[1].x.toLong() } else { null } } private fun connectAll(index: Int, other: Int, connections: Array<Array<Boolean>>) { fun connectHelper(hIndex: Int) { val newConnections = mutableSetOf<Int>() for (i in connections[hIndex].indices) { if (connections[hIndex][i]) newConnections.add(i) } for (boxIndex in newConnections.filter { it != hIndex }) { for (conn in newConnections.filter { it != boxIndex }) { connections[boxIndex][conn] = true } } } connections[index][other] = true connectHelper(index) // update matrix with all values from node at [index] connectHelper(other) // update matrix with all values from node at [other] } // returns 2D-list of all indices of currently active connections private fun buildConnectionSet(boxes: List<Point3D>, connections: Array<Array<Boolean>>): Set<List<Int>> { val connectionSet = mutableSetOf<List<Int>>() val indices = (0 until boxes.size).toMutableList() while (indices.isNotEmpty()) { val list = mutableListOf<Int>() val curr = indices.removeFirst() val array = connections[curr] for (j in array.indices) { if (array[j]) list.add(j) } connectionSet.add(list) } return connectionSet } data class Point3D(val x: Int, val y: Int, val z: Int) { fun dist(other: Point3D): Double { return sqrt( (x - other.x).toDouble().pow(2) + (y - other.y).toDouble().pow(2) + (z - other.z).toDouble().pow(2) ) } } }full code on Codeberg
That is a very unoptimal pt2 time! Have you tried profiling to see what’s causing the slowdown? Mine is 300ms, or worst case, 4s to process all links.
My algorithm is probably pretty horrible, I was purely out to get a solution as fast as possible. I might try improving it after Day12…
That’s interesting. I found part 1 to be the hard part and part 2 was just simplifying part 1. They ran in about the same time for me, too.
Part 1 run time:
Input IO: 0m 0s 10ms Input Parse: 0m 0s 21ms Algorithm: 0m 0s 250ms Total: 0m 0s 281msPart 2 run time:
Input IO: 0m 0s 11ms Input Parse: 0m 0s 22ms Algorithm: 0m 0s 255ms Total: 0m 0s 288msCode:
const val connectionsLimit = 1000 const val circuitsLimit = 3 fun part1() { val input = getInput(8) val circuits: MutableList<MutableSet<Triple<Int, Int, Int>>> = parseInput1(input) val distances: MutableList<Pair<Pair<Triple<Int, Int, Int>, Triple<Int, Int, Int>>, Double>> = mutableListOf() val tempList = circuits.toList() for (i in tempList.indices) { val left = tempList[i].first() for (j in i + 1..<tempList.size) { val right = tempList[j].first() val distance = calculateDistance(left, right) val coordinates = left to right distances.add(coordinates to distance) } } distances.sortBy { it.second } // Parts 1 and 2 are the same until here for (i in 0..<connectionsLimit) { val distance = distances[i] val leftCircuit = circuits.first { it.contains(distance.first.first) } val rightCircuit = circuits.first { it.contains(distance.first.second) } if (leftCircuit != rightCircuit) { leftCircuit.addAll(rightCircuit) circuits.remove(rightCircuit) } } val sizes = circuits.map { it.size.toLong() }.sortedDescending().slice(0..<circuitsLimit) val total = sizes.reduce { acc, i -> acc * i } println(total) } fun part2() { val input = getInput(8) val circuits: MutableList<MutableSet<Triple<Int, Int, Int>>> = parseInput1(input) val distances: MutableList<Pair<Pair<Triple<Int, Int, Int>, Triple<Int, Int, Int>>, Double>> = mutableListOf() val tempList = circuits.toList() for (i in tempList.indices) { val left = tempList[i].first() for (j in i + 1..<tempList.size) { val right = tempList[j].first() val distance = calculateDistance(left, right) val coordinates = left to right distances.add(coordinates to distance) } } distances.sortBy { it.second } // Part 2 differs starting here var answer = 0 for (distance in distances) { val leftCircuit = circuits.first { it.contains(distance.first.first) } val rightCircuit = circuits.first { it.contains(distance.first.second) } if (leftCircuit != rightCircuit) { leftCircuit.addAll(rightCircuit) circuits.remove(rightCircuit) if (circuits.size == 1) { answer = distance.first.first.first * distance.first.second.first break } } } println(answer) } fun parseInput1(input: String): MutableList<MutableSet<Triple<Int, Int, Int>>> { return input.lines() .filter { it.isNotBlank() } .map { val split = it.split(",") mutableSetOf(Triple(split[0].toInt(), split[1].toInt(), split[2].toInt())) } .toMutableList() } fun calculateDistance(left: Triple<Int, Int, Int>, right: Triple<Int, Int, Int>): Double { val dx = (left.first - right.first).toDouble() val dy = (left.second - right.second).toDouble() val dz = (left.third - right.third).toDouble() val distanceSquared = dx.pow(2) + dy.pow(2) + dz.pow(2) return sqrt(distanceSquared) }Oh wow, I see you went the Kotlin way of hacking together an object with Pairs and Triples. I used to do that too but then it got too confusing lol
It can be confusing, for sure. But I really like Pairs and Triples.
answer = distance.first.first.first * distance.first.second.firstis not very elegant, lol.