diff --git a/days/src/main/kotlin/Day23.kt b/days/src/main/kotlin/Day23.kt index 1b5e296..4f0ab9f 100644 --- a/days/src/main/kotlin/Day23.kt +++ b/days/src/main/kotlin/Day23.kt @@ -9,54 +9,110 @@ import be.vandewalleh.aoc.utils.input.createDay class Day23(@Text val input: Input) { private val cups = input.value.toCharArray().map { it.toString().toInt() } - fun part1() { - var cups = this.cups - var current = cups.first() + private fun ringSequence(head: Ring.Node) = generateSequence(head) { it.next } - repeat(100) { - println("-- move ${it + 1} --") - val (newCurrent, newCups) = move(current, cups) - current = newCurrent - cups = newCups + class Ring(items: Iterable) { + + class Node(var value: T) { + lateinit var next: Node + override fun toString() = "Node($value -> ${next.value})" } - println(cups.asSequence().infinite().dropWhile { it != 1 }.drop(1).take(cups.size - 1).joinToString("")) + private var last: Node? = null + private var first: Node? = null + + init { + items.forEach { item -> + val node = Node(item) + if (first == null) first = node + last?.next = node + last = node + } + check(first != null && last != null) + last?.next = first!! + } + + fun first() = first!! } - private fun Sequence.infinite() = sequence { while (true) yieldAll(this@infinite) } + private fun Ring.Node.cached(size: Int): Array> = + arrayOfNulls>(size + 1).also { array -> + array[0] = Ring.Node(-1) + ringSequence(this).take(size).forEach { array[it.value] = it } + } as Array> - private fun move(current: Int, cups: List): Pair> { - println("cups: ${cups.joinToString(" ").replace(current.toString(), "($current)")}") - val mutableCups = cups.toMutableList() + fun part1(): String { + var currentNode = Ring(cups).first() + val cache = currentNode.cached(cups.size) + val max = cups.maxOrNull()!! - val picked = cups.asSequence().infinite().dropWhile { it != current }.drop(1).take(3).toList() - mutableCups.removeAll(picked) - println("pick up: ${picked.joinToString(", ")}") + repeat(100) { currentNode = move(max, cache, currentNode) } - fun chooseDestination(cups: List): Int { - val reordered = cups.asSequence().infinite().dropWhile { it != current }.take(mutableCups.size).toMutableList() - return reordered.filter { it < current }.maxOrNull() ?: reordered.maxOrNull()!! + return ringSequence(currentNode) + .dropWhile { it.value != 1 } + .drop(1) + .take(cups.size - 1) + .map { it.value } + .joinToString("") + } + + fun part2(): Long { + fun fillCups(): List { + val cups = ArrayList(1_000_000) + cups.addAll(this.cups) + val highest = this.cups.maxOrNull()!! + + for (i in 1..1_000_000 - cups.size) { + cups.add(highest + i) + } + + return cups } - val destinationCup = chooseDestination(mutableCups) + val size = 1_000_000 + var currentNode = Ring(fillCups()).first() + val cache = currentNode.cached(size) - val out2 = mutableListOf() - for (cup in mutableCups) { - out2.add(cup) - if (cup == destinationCup) { - out2.addAll(picked) + repeat(10_000_000) { currentNode = move(max = size, cache, currentNode) } + + val one = cache[1] + val a = one.next + val b = a.next + return a.value.toLong() * b.value.toLong() + } + + private fun move(max: Int, cache: Array>, current: Ring.Node): Ring.Node { + val a = current.next + val b = a.next + val c = b.next + + current.next = c.next + + val aa = a.value + val bb = b.value + val cc = c.value + + val destinationNode: Ring.Node + var i = current.value - 1 + while (true) { + if (i < 1) i = max + val value = cache[i].value + if (value == aa || value == bb || value == cc) { + i-- + } else { + destinationNode = cache[i] + break } } - val newCurrentCup = out2.asSequence().infinite().dropWhile { it != current }.drop(1).first() - return newCurrentCup to out2 - } - - fun part2() { + val destinationNextNode = destinationNode.next + destinationNode.next = a + c.next = destinationNextNode + return current.next } } -fun main() = with(createDay("871369452")) { +fun main() = with(createDay()) { println(part1()) println(part2()) }