diff --git a/src/main/kotlin/no/rodland/advent_2023/Day14.kt b/src/main/kotlin/no/rodland/advent_2023/Day14.kt index 1f6b3d29..2dc69283 100644 --- a/src/main/kotlin/no/rodland/advent_2023/Day14.kt +++ b/src/main/kotlin/no/rodland/advent_2023/Day14.kt @@ -5,25 +5,52 @@ import no.rodland.advent.Day // template generated: 14/12/2023 // Fredrik Rødland 2023 -class Day14(val input: List) : Day> { +class Day14(val input: List) : Day> { private val grid = input.parse() override fun partOne(): Int { - return grid.map { line -> - val collapsed = collapsed(line) - val weight = collapsed.foldIndexed(0) { index: Int, acc: Int, c: Char -> - if (c == 'O') { - acc + (line.length - index) - } else { - acc - } - } - weight - }.sum() + return grid.rotateLeft().tilt().load() + } + override fun partTwo(): Int { + var cycle = grid.rotateLeft() + val seenStateIn = mutableMapOf(cycle to 0) + val number = 1_000_000_000 + // loop through until we see a map we've already seen and cycle (a few times) forward until we + // would have reached 1_000_000_000 + for (i in 1..number) { + cycle = cycle.cycle() + val lastSeen = seenStateIn.getOrPut(cycle) { i } + if (lastSeen != i) { + val mod = i - lastSeen + val diff = number - i + val times = diff % mod + repeat(times) { cycle = cycle.cycle() } + break + } + } + return cycle.load() } + private fun List.rev() = map { it.reversed() } + private fun List.load() = sumOf { it.load() } + private fun List.cycle() = tilt().rotateLeft().rev() + .tilt().reversed().rotateLeft().rev() + .tilt().reversed().rotateLeft().rev() + .tilt().reversed().rotateLeft().asReversed().rev() + + private fun List.tilt() = map { line -> collapsed(line) } + + private fun String.load() = + foldIndexed(0) { index: Int, acc: Int, c: Char -> + if (c == 'O') { + acc + (length - index) + } else { + acc + } + } + fun collapsed(line: String): String { var previousLine = line var newLine = hopOne(line) @@ -34,7 +61,7 @@ class Day14(val input: List) : Day> { return newLine } - fun hopOne(line: String): String { + private fun hopOne(line: String): String { val array = line.toCharArray() line.forEachIndexed { idx, c -> if (line[idx] == '.' && (idx + 1) < line.length && line[idx + 1] != '#') { @@ -46,18 +73,20 @@ class Day14(val input: List) : Day> { return array.joinToString("") } - override fun partTwo(): Long { - return 2 + override fun List.parse(): List = this + + private fun List.rotateLeft(): List { + return indices.reversed().map { j -> indices.map { i -> get(i)[j] }.joinToString("") } } - override fun List.parse(): List { - val width = first().length - val height = size - return (0.. - (0.. - get(i)[j] - }.joinToString("") + @Suppress("unused") + private fun List.print(str: String? = null): List { + if (str != null) { + println(str) } + forEach { println(it) } + println() + return this } override val day = "14".toInt() diff --git a/src/test/kotlin/no/rodland/advent_2023/Day14Test.kt b/src/test/kotlin/no/rodland/advent_2023/Day14Test.kt index 6e359f7b..3c81648b 100644 --- a/src/test/kotlin/no/rodland/advent_2023/Day14Test.kt +++ b/src/test/kotlin/no/rodland/advent_2023/Day14Test.kt @@ -16,9 +16,9 @@ internal class Day14Test { private val test14 = "2023/input_14_test.txt".readFile() private val resultTestOne = 136 - private val resultTestTwo = 2L + private val resultTestTwo = 64 private val resultOne = 108144 - private val resultTwo = 2L + private val resultTwo = 108404 val test = defaultTestSuiteParseOnInit( Day14(data14), @@ -29,6 +29,8 @@ internal class Day14Test { resultTwo, { Day14(data14) }, { Day14(test14) }, + numTestPart1 = 300, + numTestPart2 = 1, ) @Nested @@ -76,6 +78,18 @@ internal class Day14Test { } + @Nested + inner class `Only tests` { + @Test + fun `14,1,test`() { + report(test.testPart1) + } + @Test + fun `14,2,test`() { + report(test.testPart2) + } + + } @Nested inner class `Part 1` { @@ -85,6 +99,7 @@ internal class Day14Test { } @Test + @Slow(320) fun `14,1,live,1`() { report(test.livePart1) } @@ -98,6 +113,7 @@ internal class Day14Test { } @Test + @Slow(490) fun `14,2,live,1`() { report(test.livePart2) }