~macaptain/advent-of-code

0ebe6569603d9f2853f62ca9133645a75b78b984 — Michael Captain 1 year, 7 months ago 037666e
Complete 2019 Day 16 Part B
2 files changed, 37 insertions(+), 16 deletions(-)

M src/main/advent2019/Day16.kt
M src/test/advent2019/Day16KtTest.kt
M src/main/advent2019/Day16.kt => src/main/advent2019/Day16.kt +35 -14
@@ 1,26 1,47 @@
package advent2019

import common.countFrom
import common.repeat
import kotlin.math.absoluteValue

fun pattern(n: Int): Sequence<Int> {
    val basePattern = listOf(0, 1, 0, -1)
    return countFrom(0).map { basePattern[(it / n) % 4] }.drop(1)
}

fun solve16a(s: String, phases: Int = 100): String {
    val data = s.map { it.digitToInt() }
    val seq = generateSequence(data) { phase ->
        (1..data.count()).map {
            phase.asSequence().zip(pattern(it)).sumOf { (a, b) -> a * b }.let { n ->
                (n % 10).absoluteValue
            }
    val signal = s.map { it.digitToInt() }
    val seq = generateSequence(signal) { phase ->
        (1..signal.count()).map { n ->
            (phase.foldIndexed(0) { i, sum, d ->
                sum + when (((i + 1) / n) % 4) {
                    1 -> d
                    3 -> -d
                    else -> 0
                }
            } % 10).absoluteValue
        }
    }.elementAt(phases)
    return seq.take(8).joinToString("")
    }
    return seq.elementAt(phases).take(8).joinToString("")
}

fun solve16b(s: String, phases: Int = 100): String {
    val signal = s.map { it.digitToInt() }.asSequence().repeat().take(s.count() * 10_000).toList()
    val messageOffset = s.take(7).toInt()
    // digits earlier in the signal don't affect digits later in the signal, so we can drop to the point we're interested in
    val offsetSignal = signal.drop(messageOffset)
    assert(offsetSignal.count() <= signal.count() / 2) {
        "if the offset puts us in the first half of the signal, " +
                "we can't treat the pattern matrix as upper triangular with alls 1s, " +
                "so the optimisation used to solve this problem won't work"
    }
    val seq = generateSequence(offsetSignal) { phase ->
        // past a certain point, n becomes sufficiently large that all that matters is whether when the index is divided
        // by n whether you get 0 or 1, i.e. whether you fall into the 0s or the 1s part of the pattern.
        // in fact, the 1s start coming in when the index is exactly n, the applied matrix is upper triangular with
        // all 1s. So applying the dot product from the bottom lines upwards, each digit of the next phase is the
        // units digit of the running sum backwards.
        phase.reversed().runningReduce { a, b -> a + b }.reversed().map { (it % 10).absoluteValue }
    }
    return seq.elementAt(phases).take(8).joinToString("")
}

fun main() {
    val input = input("day16").first()
    println(solve16a(input)) // 27229269
    println(solve16b(input)) // 26857164
}
\ No newline at end of file

M src/test/advent2019/Day16KtTest.kt => src/test/advent2019/Day16KtTest.kt +2 -2
@@ 16,10 16,10 @@ internal class Day16KtTest {
        assertEquals("52432133", solve16a("69317163492948606335995924319873"))
    }

    /*@Test
    @Test
    fun solve16b() {
        assertEquals("84462026", solve16b("03036732577212944063491565474664"))
        assertEquals("78725270", solve16b("02935109699940807407585447034323"))
        assertEquals("53553731", solve16b("03081770884921959731165446850517"))
    }*/
    }
}
\ No newline at end of file