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))}")
}