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

you are viewing a single comment's thread
view the rest of the comments
[โ€“] 3 points 10 months ago (2 children)

Kotlin

Pretty simple solution, just plain count / remove the rolls until none can be removed anymore. I would've liked to try using imaginary numbers this year (due to this article), but sadly Kotlin doesn't natively support them and I was too lazy to use a library.

Solution

class Day04 : Puzzle {

    val grid = mutableListOf<MutableList<Char>>()

    override fun readFile() {
        val input = readInputFromFile("src/main/resources/a2025/day04.txt")
        for ((r, line) in input.lines().filter(String::isNotBlank).withIndex()) {
            grid.add(mutableListOf())
            for (char in line) {
                grid[r].add(char)
            }
        }
    }

    override fun solvePartOne(): String {
        return countRolls(grid).toString()
    }

    override fun solvePartTwo(): String {
        var sum = 0
        var removed = -1
        while (removed != 0) {
            // main grid is modified here, not the best but doesn't really matter
            // also no idea how to clone 2D list in Kotlin
            removed = countRolls(grid, true)
            sum += removed
        }
        return sum.toString()
    }

    private fun countRolls(grid: MutableList<MutableList<Char>>, removeRolls: Boolean = false): Int {
        val dr = listOf(-1, -1, -1, 0, 0, 1, 1, 1)
        val dc = listOf(-1, 0, 1, -1, 1, -1, 0, 1)

        var sum = 0
        for (r in grid.indices) {
            for (c in grid[r].indices) {
                if (grid[r][c] != '@') continue

                var neighborCount = 0
                for (i in dr.indices) {
                    if (gridGet(grid, r + dr[i], c + dc[i]) == '@') neighborCount++
                }
                if (neighborCount < 4) {
                    sum++
                    if (removeRolls) grid[r][c] = '.'
                }
            }
        }
        return sum
    }

    private fun gridGet(grid: List<List<Char>>, r: Int, c: Int, default: Char = '.'): Char {
        return if (r in grid.indices && c in grid[r].indices) {
            grid[r][c]
        } else {
            default
        }
    }
}

full code on Codeberg

  • source
  • hideshow 2 child comments
  • [โ€“] 2 points 10 months ago*

    Edit: looking at your code, I had forgotten about .indices. That would have made this a little easier to write.

    I completely forgot to do the puzzle yesterday somehow. I struggled a bit on this one for a while because I'd used a <= 4 where I should have used a < 4. Just a complete brainfart of thinking, "It needs to be 4 or less". I wasted more time on that than I'd like to admit.

    My first stab at this set all of the adjacency counts to 0, and that lead to a few rolls that had no rolls adjacent to them staying on the map by accident.

    const val toiletPaperRoll = '@'
    const val adjacentLimit = 4
    var height = 0
    var width = 0
    
    fun main() {
        val input = getInput(4)
        val map = parseInput1(input)
        height = map.size
        width = map[0].size
        val adjacencyMap = createAdjacencyMap(map)
        var anyRemoved = true
        var count = 0
        while (anyRemoved) {
            anyRemoved = false
            for (y in 0..<height) {
                for (x in 0..<width) {
                    if (adjacencyMap[y][x] in 0..<adjacentLimit) {
                        count++
                        anyRemoved = true
                        removeToiletPaperRoll(adjacencyMap, x, y)
                    }
                }
            }
        }
        println(count)
    }
    
    fun parseInput1(input: String): List<List<Char>> = input
        .lines()
        .filter { it.isNotBlank() }
        .map { it.toCharArray().toList() }
    
    fun createAdjacencyMap(map: List<List<Char>>): List<MutableList<Int>> {
        val adjacencyMap = List(height) { MutableList(width) { -1 } }
        for (y in 0..<height) {
            for (x in 0..<width) {
                if (map[y][x] != toiletPaperRoll) {
                    continue
                }
                adjacencyMap[y][x]++
                for (y2 in y - 1..y + 1) {
                    for (x2 in x - 1..x + 1) {
                        if (y == y2 && x == x2 || (y2 < 0 || x2 < 0 || y2 >= height || x2 >= width)) {
                            continue
                        }
                        if (map[y2][x2] == toiletPaperRoll) {
                            adjacencyMap[y2][x2]++
                        }
                    }
                }
            }
        }
        return adjacencyMap
    }
    
    fun removeToiletPaperRoll(adjacencyMap: List<MutableList<Int>>, x: Int, y: Int) {
        adjacencyMap[y][x] = -1
        for (y2 in y - 1..y + 1) {
            for (x2 in x - 1..x + 1) {
                if (y == y2 && x == x2 || (y2 < 0 || x2 < 0 || y2 >= height || x2 >= width)) {
                    continue
                }
                if (adjacencyMap[y2][x2] >= adjacentLimit) {
                    adjacencyMap[y2][x2]--
                }
            }
        }
    }
    
  • source
  • parent