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