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

  • eco_game@discuss.tchncs.de
    link
    fedilink
    arrow-up
    4
    ·
    edit-2
    21 days ago

    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

    • CameronDev@programming.devOPM
      link
      fedilink
      arrow-up
      4
      ·
      21 days ago

      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.

      • eco_game@discuss.tchncs.de
        link
        fedilink
        arrow-up
        3
        ·
        21 days ago

        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…

    • chunkystyles@sopuli.xyz
      link
      fedilink
      English
      arrow-up
      2
      ·
      21 days ago

      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 281ms
      

      Part 2 run time:

      Input IO: 0m 0s 11ms
      Input Parse: 0m 0s 22ms
      Algorithm: 0m 0s 255ms
      Total: 0m 0s 288ms
      

      Code:

      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)
      }
      
      • eco_game@discuss.tchncs.de
        link
        fedilink
        arrow-up
        2
        ·
        21 days ago

        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

        • chunkystyles@sopuli.xyz
          link
          fedilink
          English
          arrow-up
          2
          ·
          21 days ago

          It can be confusing, for sure. But I really like Pairs and Triples.

          answer = distance.first.first.first * distance.first.second.first is not very elegant, lol.