• 13 Posts
  • 273 Comments
Joined 3 years ago
cake
Cake day: June 17th, 2023

help-circle
  • Users who aren’t verified as adults will not be able to access age-restricted servers and channels, won’t be able to speak in Discord’s livestream-like “stage” channels, and will see content filters for any content Discord detects as graphic or sensitive. They will also get warning prompts for friend requests from potentially unfamiliar users, and DMs from unfamiliar users will be automatically filtered into a separate inbox.

    That shouldn’t be too restricting for people using discord to chat with their friends though.




  • My router (FRITZ!Box) provides entities for all network devices, whether they are currently connected to the network or not.

    I trigger all my stuff on when my phone connects/disconnects to my home WiFi. For me this is the best, simplest and least error prone way.

    For your use case this would mean needing to have WiFi reception outside your house / in front of your garage, and having your phone reliably and quickly connect to the WiFi when you get home, which might not work great, depending on the strength of your WiFi outside your house.

















  • 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




  • Kotlin

    Tried recursive for part1, didn’t work. LUCKILY for once I was smart and committed anyway, as it was the right solution for part2! I was losing my mind for a bit though as I had originally written my method with Integers…

    Solution
    class Day07 : Puzzle {
    
        val grid = mutableListOf<MutableList<Char>>()
        val partTwoCache = mutableMapOf<Pair<Int, Int>, Long>()
    
        override fun readFile() {
            val input = readInputFromFile(2025, 7, false)
            for (line in input.lines().filter { it.isNotBlank() }) {
                grid.add(line.toCharArray().toMutableList())
            }
        }
    
        override fun solvePartOne(): String {
            grid[1][grid[0].indexOf('S')] = '|'
    
            var splits = 0
            for (r in 1..<grid.size - 1) {
                for (c in 0..<grid[r].size) {
                    if (grid[r][c] == '|') {
                        if (grid[r+1][c] == '.') {
                            grid[r+1][c] = '|'
                        } else if (grid[r+1][c] == '^') {
                            grid[r+1][c-1] = '|'
                            grid[r+1][c+1] = '|'
                            splits++
                        }
                    }
                }
            }
            return splits.toString()
        }
    
        override fun solvePartTwo(): String {
            val start = grid[0].indexOf('S')
            return (1 + processBeamPartTwo(1, start)).toString() // don't forget to count the original timeline
        }
    
        private fun processBeamPartTwo(row: Int, column: Int): Long {
            if (partTwoCache.contains(Pair(row, column))) {
                return partTwoCache[Pair(row, column)]!!
            }
    
            if (row == grid.size) return 0L
            if (column == grid[row].size || column < 0) return 0L
    
            val out = if (grid[row][column] == '^') { // splitter
                1L + processBeamPartTwo(row, column - 1) + processBeamPartTwo(row, column + 1)
            } else {
                processBeamPartTwo(row + 1, column)
            }
            partTwoCache[Pair(row, column)] = out
            return out
        }
    }
    

    full code on Codeberg


  • Kotlin

    I’m not fully happy with my parsing today, but oh well. I also thought about just plain building the grid and then rotating it, but “normal input parsing” works too.

    Solution
    class Day06 : Puzzle {
    
        val numsPartOne = mutableListOf<MutableList<Long>>()
        val numsPartTwo = mutableListOf<List<Long>>()
        val ops = mutableListOf<(Long, Long) -> Long>()
    
        override fun readFile() {
            val input = readInputFromFile("src/main/resources/a2025/day06.txt")
            val lines = input.lines().filter { it.isNotBlank() }
    
            // parse part1 input
            for (line in lines.dropLast(1)) {
                for ((c, num) in line.trim().split(" +".toRegex()).withIndex()) {
                    if (numsPartOne.getOrNull(c) == null) numsPartOne.add(mutableListOf())
                    numsPartOne[c].add(num.toLong())
                }
            }
    
            // parse part2 input
            var numList = mutableListOf<Long>()
            for (c in 0..<lines.maxOf { it.length }) {
                var numStr = ""
                for (r in 0..<lines.size - 1) {
                    numStr += lines[r].getOrElse(c) { ' ' }
                }
                if (numStr.isBlank()) {
                    numsPartTwo.add(numList)
                    numList = mutableListOf()
                } else {
                    numList.add(numStr.trim().toLong())
                }
            }
            numsPartTwo.add(numList)
    
            // parse operators
            ops.addAll(
                lines.last().split(" +".toRegex())
                    .map { it.trim()[0] }
                    .map {
                        when (it) {
                            '*' -> { a: Long, b: Long -> a * b }
                            '+' -> { a: Long, b: Long -> a + b }
                            else -> throw IllegalArgumentException("Unknown operator: $it")
                        }
                    }
            )
        }
    
        override fun solvePartOne(): String {
            return numsPartOne.mapIndexed { c, list -> list.reduce { a, b -> ops[c](a, b) } }.sum().toString()
        }
    
        override fun solvePartTwo(): String {
            return numsPartTwo.mapIndexed { c, list -> list.reduce { a, b -> ops[c](a, b) } }.sum().toString()
        }
    }
    

    full code on Codeberg