V tento den mi chvíli (no, trochu déle) trvalo, než jsem vůbec pochopil zadaní první části, kterou jsem pak zvládl docela v pohodě. Ale pro část B jsem si již zašel pro radu na Kotlin Slack, kde trik spočíval v tom, že již po cca 100 iteracích se pattern usadí a přírůstky rostlin začnou být konstantní, což nám umožní všechny zbývající iterace vynechat úplně a výsledek jednoduše dopočítat.
import com.svetylkovo.rojo.Rojo
import java.io.File
val rulesMatcher = Rojo.matcher("(.+?) => (.+?)")
class Replanter(initial: List<Int>, val rules: Map<List<Int>, Int>) {
private var pots = initial.toMutableList()
private var originIndexOffset = 0
init {
ensureBothEmptyEnds()
}
fun nextStep() {
pots.toList()
.asSequence()
.windowed(5, 1)
.forEachIndexed { index, it ->
val nextPotState = rules[it]
pots[index + 2] = nextPotState ?: 0
}
ensureBothEmptyEnds()
}
private fun ensureBothEmptyEnds(emptyEndsSize: Int = 3) {
val leftEmpty = pots.takeWhile { it == 0 }.count()
val rightEmpty = pots.takeLastWhile { it == 0 }.count()
val leftToAppend = emptyEndsSize - leftEmpty
val rightToAppend = emptyEndsSize - rightEmpty
if (leftToAppend > 0) {
originIndexOffset += leftToAppend
pots.addAll(0, List(leftToAppend) { 0 })
}
if (rightToAppend > 0) {
pots.addAll(List(rightToAppend) { 0 })
}
}
fun stateAsString() = pots.joinToString("") {
if (it == 1) "#" else "."
}
fun sumPotWithPlantNumbers(): Long = pots.asSequence()
.mapIndexed { index, value -> (index - originIndexOffset) * value }
.sum()
.toLong()
}
fun String.toPlantStatesList() = map { if (it == '#') 1 else 0 }
fun main() {
val input = File("input12.txt").readText()
val initial =
"##...#......##......#.####.##.#..#..####.#.######.##..#.####...##....#.#.####.####.#..#.######.##...".toPlantStatesList()
val rules = rulesMatcher.asMap(input)
.mapKeys { it.key.toPlantStatesList() }
.mapValues { it.value.toPlantStatesList().first() }
//A
val replanterA = Replanter(initial, rules)
repeat(20) {
replanterA.nextStep()
}
println(replanterA.sumPotWithPlantNumbers())
//B
println("Computing part B")
val replanterB = Replanter(initial, rules)
//let's get to the last point before it settles
repeat(98) {
replanterB.nextStep()
}
val diffSettlesTo = 51
println("Part B magical answer is: ${replanterB.sumPotWithPlantNumbers() + (diffSettlesTo * (50000000000L - 98L))}")
}