Day 4: Printing Department

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

  • GiantTree@feddit.org
    link
    fedilink
    arrow-up
    2
    ·
    edit-2
    2 months ago

    Kotlin

    I’m catching up on this year’s AOC.
    This one was rather easy. I already have a pretty versatile grid class that I have just iterated as often as needed.

    Doing this one also lead me into the rabbit hole that is source code generation in Gradle. I used this to generate all the implementations for the primitive types of the grid class as primitive arrays are not generic in the JVM.
    An Array<Int> is an array of integer references, but an IntArray is an array of primitive integers.

    Code on GitHub

    Code
    class Day04 : AOCSolution {
        override val year = 2025
        override val day = 4
    
        override fun part1(inputFile: String): String {
            val grid = readResourceLines(inputFile).map(CharSequence::toList).toCharGrid()
    
            var accessiblePaperRolls = 0
    
            // Quickly iterate the grid in top-left to bottom-right order
            for (y in 0 until grid.height) {
                for (x in 0 until grid.width) {
                    // Count the neighbours of each paper roll.
                    if (grid[x, y] == PAPER_ROLL &&
                        grid.countNeighbours(x, y, 1) { it == PAPER_ROLL } < 4
                    ) {
                        accessiblePaperRolls++
                    }
                }
            }
            return accessiblePaperRolls.toString()
        }
    
        override fun part2(inputFile: String): String {
            val grid = readResourceLines(inputFile).map(CharSequence::toList).toCharGrid()
    
            var count = 0
            while (true) {
                var iterationCount = 0
    
                // Quickly iterate the grid in top-left to bottom-right order
                for (y in 0 until grid.height) {
                    for (x in 0 until grid.width) {
                        if (grid[x, y] == PAPER_ROLL &&
                            grid.countNeighbours(x, y, 1) { it == PAPER_ROLL } < 4
                        ) {
                            // Remove the paper roll for the next iteration
                            grid[x, y] = REMOVED_PAPER_ROLL
                            iterationCount++
                        }
                    }
                }
                count += iterationCount
    
                // Repeat the count until no paper rolls are accessible.
                if (iterationCount == 0) {
                    break
                }
            }
    
            return count.toString()
        }
    
        private companion object {
            const val PAPER_ROLL = '@'
            const val REMOVED_PAPER_ROLL = 'x'
    
            /**
             * Count the neighbours of the given cell in the given [radius] of cells that satisfy the given predicate.
             *
             * @param startX the horizontal position of the center of the count in the grid
             * @param startY the vertical position of the center of the count in the grid
             * @param radius the radius counted in Manhattan distance to the center
             * @param predicate the test that needs to pass for a neighbour to count.
             */
            private fun CharGrid.countNeighbours(
                startX: Int,
                startY: Int,
                radius: Int,
                predicate: Predicate<Char>,
            ): Int {
                var count = 0
                for (y in maxOf(startY - radius, 0)..minOf(startY + radius, height - 1)) {
                    for (x in maxOf(startX - radius, 0)..minOf(startX + radius, width - 1)) {
                        if (y == startY && x == startX) {
                            continue
                        }
                        if (predicate.test(this[x, y])) {
                            count++
                        }
                    }
                }
                return count
            }
        }
    }