package be.vandewalleh.aoc.days import be.vandewalleh.aoc.utils.input.Day import be.vandewalleh.aoc.utils.input.Text @Day(23) class Day23(@Text val input: String) { private val cups = input.toCharArray().map { it.toString().toInt() } private fun ringSequence(head: Ring.Node) = generateSequence(head) { it.next } class Ring(items: Iterable) { class Node(var value: T) { lateinit var next: Node override fun toString() = "Node($value -> ${next.value})" } 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 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> fun part1(): String { var currentNode = Ring(cups).first() val cache = currentNode.cached(cups.size) val max = cups.maxOrNull()!! repeat(100) { currentNode = move(max, cache, currentNode) } 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 size = 1_000_000 var currentNode = Ring(fillCups()).first() val cache = currentNode.cached(size) 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 destinationNextNode = destinationNode.next destinationNode.next = a c.next = destinationNextNode return current.next } }