Skip to content

Instantly share code, notes, and snippets.

@pchmielowski
Last active December 11, 2021 11:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pchmielowski/94392ec124b293aab5880097a828c012 to your computer and use it in GitHub Desktop.
Save pchmielowski/94392ec124b293aab5880097a828c012 to your computer and use it in GitHub Desktop.
Advent of code 2021, day 11
import java.io.File
fun main() {
val file = File("input.txt")
val lines = file.readLines()
val initial = parseInitialStep(lines)
println(countFlashes(initial, steps = 100))
println(findFirstStepWithSyncFlash(initial))
}
fun parseInitialStep(input: List<String>) = input
.map { line ->
line
.split("")
.filterNot(String::isEmpty)
.map(::Energy)
}
.let(::Step)
// Day 11, part 1
fun countFlashes(initial: Step, steps: Int): Int {
var current = initial
var count = 0
repeat(steps + 1) {
count += current.flashesCount
current = current
.reset()
.increaseEnergies()
.triggerFlashes()
}
return count
}
// Day 11, part 2
fun findFirstStepWithSyncFlash(initial: Step): Int {
var current = initial
var step = 0
while (true) {
if (current.areFlashesInSync) {
return step
}
current = current
.reset()
.increaseEnergies()
.triggerFlashes()
step++
}
}
data class Step(
private val energies: List<List<Energy>>,
private val flashes: Set<Coordinate> = emptySet(),
) {
val areFlashesInSync get() = flashes.size == energies.sumOf { column -> column.size }
val flashesCount get() = flashes.size
fun increaseEnergies() = copy(
energies = energies
.map { line ->
line.map(Energy::increase)
}
)
fun triggerFlashes(): Step {
// Break recursion if no more flashes in this step
val flashing = findNextFlash() ?: return this
val updated = energies.mapIndexed { rowIndex, row ->
row.mapIndexed { columnIndex, energy ->
if (isAdjacent(flashing, row = rowIndex, column = columnIndex)) {
energy.increase()
} else {
energy
}
}
}
return copy(energies = updated, flashes = flashes + flashing).triggerFlashes()
}
private fun findNextFlash(): Coordinate? {
energies.forEachIndexed { rowIndex, row ->
row.forEachIndexed { columnIndex, energy ->
val coordinate = Coordinate(rowIndex, columnIndex)
if (energy.canFlash && !flashes.contains(coordinate)) {
return coordinate
}
}
}
return null
}
private fun isAdjacent(coordinate: Coordinate, row: Int, column: Int): Boolean {
if (row == coordinate.row && column == coordinate.column) {
return false
}
if (row in (coordinate.row - 1..coordinate.row + 1)) {
if (column in (coordinate.column - 1..coordinate.column + 1)) {
return true
}
}
return false
}
fun reset() = copy(
energies = energies.map { column ->
column.map(Energy::reset)
},
flashes = emptySet(),
)
data class Coordinate(val row: Int, val column: Int)
}
@JvmInline
value class Energy(private val value: Int) {
constructor(raw: String) : this(raw.toInt())
init {
require(0 <= value)
}
val canFlash get() = value > 9
fun increase() = Energy(value + 1)
fun reset() = Energy(if (value > 9) 0 else value)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment