Day 11: Plutonian Pebbles

Megathread guidelines

  • Keep top level comments as only solutions, if you want to say something other than a solution put it in a new post. (replies to comments can be whatever)
  • You can send code in code blocks by using three backticks, the code, and then three backticks or use something such as https://topaz.github.io/paste/ if you prefer sending it through a URL

FAQ

  • the_beber@lemm.ee
    link
    fedilink
    arrow-up
    1
    ·
    9 days ago

    Kotlin

    Gone mathematical.

    Also overflowing indices screwed with me a bit.

    Here's the code:
    import kotlin.math.floor
    import kotlin.math.log
    import kotlin.math.pow
    import kotlin.time.DurationUnit
    
    fun main() {
        fun part1(input: List<String>): Long = Day11Solver(input).solve(25)
    
        fun part2(input: List<String>): Long = Day11Solver(input).solve(75)
    
        val testInput = readInput("Day11_test")
        check(part1(testInput) == 55312L)
        //check(part2(testInput) == 0L)  No test output available.
    
        val input = readInput("Day11")
        part1(input).println()
        part2(input).println()
    
        timeTrials("Part 1", unit = DurationUnit.MICROSECONDS) { part1(input) }
        timeTrials("Part 2", repetitions = 1000) { part2(input) }
    }
    
    class Day11Solver(input: List<String>) {
        private val parsedInput = input[0].split(' ').map { it.toLong() }
    
        /*
         * i ∈ ℕ₀ ∪ {-1}, φᵢ: ℕ₀ → ℕ shall be the function mapping the amount of stones generated to the amount of steps
         * taken with a stone of starting number i.
         *
         * Furthermore, ѱ: ℕ₀ → ℕ₀ ⨯ (ℕ₀ ∪ {-1}) shall be the function mapping an index to two new indices after a step.
         *
         *         ⎧ (1, -1)       if i = 0
         * ѱ(i) := ⎨ (a, b)        if ⌊lg(i)⌋ + 1 ∈ 2 ℕ    with a := i/(10^((⌊lg(i)⌋ + 1) / 2)), b := i - 10^((⌊lg(i)⌋ + 1) / 2) * a
         *         ⎩ (2024 i, -1)  otherwise
         *
         *          ⎧ 0                      if i = -1
         * φᵢ(n) := ⎨ 1                      if n = 0
         *          ⎩ φₖ(n - 1) + φₗ(n - 1)  otherwise    with (k, l) := ѱ(i)
         *
         * With that φᵢ(n) is a sum with n up to 2ⁿ summands, that are either 0 or 1.
         */
        private val cacheIndices = mutableMapOf<Long, Pair<Long, Long>>()  // Cache the next indices for going from φᵢ(n) to φₖ(n - 1) + φₗ(n - 1).
        private val cacheValues = mutableMapOf<Pair<Long, Int>, Long>()  // Also cache already calculated φᵢ(n)
    
        fun calculatePsi(i: Long): Pair<Long, Long> = cacheIndices.getOrPut(i) {
            if(i == -1L) throw IllegalArgumentException("Advancement made: How did we get here?")
            else if (i == 0L) 1L to -1L
            else {
                val amountOfDigits = (floor(log(i.toDouble(), 10.0)) + 1)
    
                if (amountOfDigits.toLong() % 2 == 0L) {
                    // Split digits at the midpoint.
                    val a = floor(i / 10.0.pow(amountOfDigits / 2))
                    val b = i - a * 10.0.pow(amountOfDigits / 2)
                    a.toLong() to b.toLong()
                } else {
                    2024 * i to -1L
                }
            }
        }
    
        fun calculatePhi(i: Long, n: Int): Long = cacheValues.getOrPut(i to n) {
            if (i == -1L) 0L
            else if (n == 0) 1L
            else {
                val (k, l) = calculatePsi(i)
                calculatePhi(k, n - 1) + calculatePhi(l, n - 1)
            }
        }
    
        fun solve(steps: Int): Long = parsedInput.sumOf {
            val debug = calculatePhi(it, steps)
            debug
        }
    }
    
    

    Try it out here.

    And this is the full repo.