Advent of Code in Kotlin 2021 - Day 18
This is part 18 of a series, so if you haven't read the previous parts, start here.
I clearly didn't get the data structure right today and my code is a mess. It does the job though. Maybe I can refactor this later, but no more time right now.
fun main() {
val lines = inputLines(2021, 18)
println(part1(lines))
println(part2(lines))
}
private fun part1(lines: List<String>): Int {
var res = parse(lines.first())
lines.drop(1).forEach { line ->
res = add(res, parse(line))
res = reduce(res)
}
return getMagnitude(res)
}
private fun part2(lines: List<String>): Int {
val sublists = subListsOfSize(lines.map(::parse), 2)
val part2 = sublists.maxOf {
getMagnitude(reduce(add(it.first(), it.last())))
}
return part2
}
private fun parse(string: String): SnailfishNumber = string.map { it.toString() }
private typealias SnailfishNumber = List<String>
private fun add(a: SnailfishNumber, b: SnailfishNumber) = listOf("[") + a + "," + b + "]"
private fun reduce(a: SnailfishNumber): SnailfishNumber {
var prev = a
while (true) {
var next = explode(prev)
if (next == prev) next = split(prev)
if (next == prev) return next else prev = next
}
}
private fun explode(a: SnailfishNumber): SnailfishNumber {
var depth = 0
a.forEachIndexed { index, s ->
if (s == "[") depth++
if (s == "]") depth--
if (depth == 5) {
val explodingPair = a.subList(index, index + 5)
val leftNum = explodingPair[1].toInt()
val rightNum = explodingPair[3].toInt()
val leftPad = a.subList(0, index).toMutableList()
val leftReplaceIndex = leftPad.indexOfLast { !setOf("[", "]", ",").contains(it) }
if (leftReplaceIndex != -1) leftPad[leftReplaceIndex] = (leftPad[leftReplaceIndex].toInt() + leftNum).toString()
val rightPad = a.subList(index + 5, a.size).toMutableList()
val rightReplaceIndex = rightPad.indexOfFirst { !setOf("[", "]", ",").contains(it) }
if (rightReplaceIndex != -1) rightPad[rightReplaceIndex] = (rightPad[rightReplaceIndex].toInt() + rightNum).toString()
return leftPad + "0" + rightPad
}
}
return a
}
private fun split(a: SnailfishNumber): SnailfishNumber {
val splittableNumIndex = a.indexOfFirst { it.length > 1 }
if (splittableNumIndex == -1) return a
val splittableNum = a[splittableNumIndex].toInt()
val leftNum = splittableNum / 2
val rightNum = splittableNum / 2 + splittableNum % 2
return a.subList(0, splittableNumIndex) + "[" + leftNum.toString() + "," + rightNum.toString() + "]" + a.subList(splittableNumIndex + 1, a.size)
}
private fun getMagnitude(a: SnailfishNumber): Int {
if (a.size == 1) return a.first().toInt()
if (a.size == 5) {
return a[1].toInt() * 3 + a[3].toInt() * 2
}
var depth = 0
a.forEachIndexed { index, s ->
if (s == "[") depth++
if (s == "]") depth--
if (s == "," && depth == 1) {
return getMagnitude(a.subList(1, index)) * 3 + getMagnitude(a.subList(index + 1, a.lastIndex)) * 2
}
}
throw IllegalArgumentException("Expression is not balanced")
}
/**
* Returns a list of lists, covering all possible subLists of a given size
* subListsOfSize(listOf(1, 2, 3), 1) -> [[1], [2], [3]]
* subListsOfSize(listOf(1, 2, 3), 2) -> [[2, 1], [3, 1], [1, 2], [3, 2], [1, 3], [2, 3]]
*/
fun <T>subListsOfSize(list: List<T>, size: Int): List<List<T>> {
if (size == 1) return list.map { listOf(it) }
return list.map { subListsOfSize(list.minus(it), size - 1).map { subList -> subList.plus(it)} }.flatten()
}
Even with all this mess, that's two more stars in the bag.