Den 18. byl tak trochu variací na “The Game of Life”, kde druhou část jsem již vyřešil s trochou matematické magie 🙂 .

import com.ichipsea.kotlin.matrix.*
import com.svetylkovo.adventofcode2018.day18.Acre.*
import com.svetylkovo.adventofcode2018.toNonSeparatedPrettyString
import com.svetylkovo.adventofcode2018.trimmedNonBlankLines
import java.io.File

enum class Acre(val sign: Char) {
    GROUND('.'), TREE('|'), LUMBERYARD('#');

    override fun toString() = "$sign"
}

fun parseAcre(sign: Char) = when (sign) {
    TREE.sign -> TREE
    LUMBERYARD.sign -> LUMBERYARD
    else -> GROUND
}

class Area(var area: Matrix<Acre>) {

    fun nextMinute() {
        area = area.mapIndexed { x, y, acre -> getMutatedAcre(x, y) }
    }

    private fun getMutatedAcre(x: Int, y: Int): Acre {
        val adjacent = getAdjacent(x, y)
        val acre = area[x, y]

        return when (acre) {
            GROUND -> if (adjacent.count { it == TREE } >= 3) TREE else acre
            TREE -> if (adjacent.count { it == LUMBERYARD } >= 3) LUMBERYARD else acre
            LUMBERYARD -> if (adjacent.count { it == LUMBERYARD } > 0 && adjacent.count { it == TREE } > 0) acre else GROUND
        }
    }

    fun getAdjacent(origX: Int, origY: Int): List<Acre> {
        return (origX - 1..origX + 1)
            .asSequence()
            .filter { it in 0 until area.cols }
            .map { x ->
                (origY - 1..origY + 1)
                    .asSequence()
                    .filter { it in 0 until area.rows }
                    .filterNot { y -> x == origX && y == origY }
                    .map { y -> area[x, y] }
            }.flatten().toList()
    }

    fun count(acre: Acre) = area.toList().count { it == acre }

    fun getResourceValue() = count(TREE) * count(LUMBERYARD)

    override fun toString() = area.toNonSeparatedPrettyString()
}


fun parseArea(input: String): Area {
    val lines = input.trimmedNonBlankLines()

    val width = lines[0].length
    val height = lines.size

    val matrix = lines
        .flatMap { it.map(::parseAcre) }
        .toMatrix(width, height)

    return Area(matrix)
}


fun main() {
    val input = File("input18.txt").readText()

    //A
    val areaA = parseArea(input)

    repeat(10) { areaA.nextMinute() }

    println(areaA.getResourceValue())

    //B
    val areaB = parseArea(input)

    val magicCache = mutableMapOf<Int, Int>()
    val magicPeriodConstant = 28
    val magicDiff = 610

    repeat(magicDiff - 1) { areaB.nextMinute() }

    repeat(magicPeriodConstant) {
        areaB.nextMinute()
        magicCache += it to areaB.getResourceValue()
    }

    println(magicCache[(1000000000 - magicDiff) % magicPeriodConstant])

}