Skip to content

Instantly share code, notes, and snippets.

@bartoszm
Last active December 18, 2021 18:52
Show Gist options
  • Save bartoszm/b25556db5130fb2cf955b726374d8bc1 to your computer and use it in GitHub Desktop.
Save bartoszm/b25556db5130fb2cf955b726374d8bc1 to your computer and use it in GitHub Desktop.
Day 18 - ugly solution for both parts
package day18
import java.nio.file.Files
import java.nio.file.Path
sealed class Node(var parent: Regular? = null) {
abstract fun tryExplode(level: Int = 0): Boolean
abstract fun trySplit() : Boolean
internal fun reduce() {
var onceMore: Boolean
do{ onceMore = tryExplode(0) || trySplit() } while(onceMore)
}
fun add(node: Node) : Node {
val sum = Regular(left = this, right = node)
sum.reduce()
return sum
}
abstract fun magnitude(): Long
}
private fun walk(start: Regular, order: (Regular) -> Sequence<Node> = { sequenceOf(it.left, it.right)}): Sequence<Value> {
return order(start).flatMap {
when(it) {
is Value -> sequenceOf(it)
is Regular -> walk(it, order)
}
}
}
fun bfs(start: Regular, order: (Regular) -> Sequence<Node> = { sequenceOf(it.left, it.right)}): Sequence<Value> {
val q = ArrayDeque<Node>()
fun search(): Sequence<Value> {
return sequence {
while(!q.isEmpty()) {
when(val v = q.removeFirst()) {
is Value -> yield(v)
is Regular -> q.addAll(order(v))
}
}
}
}
q.addAll(order(start))
return search()
}
class Regular(parent: Regular? = null, var left: Node, var right: Node) : Node(parent) {
init {
left.parent = this
right.parent = this
}
private fun climb(node: Node, selector: (Regular) -> Node): Regular? {
if(node.parent == null) return null
return if(node.parent?.let{selector(it)} == node) climb(node.parent!!, selector)
else node.parent
}
private fun rightMostValue(from: Node): Value? {
val starting = climb(from) {it.right}
return starting?.right?.let{
if(it is Value) it
// else bfs(it as Regular){ sequenceOf(it.right, it.left)}.firstOrNull()
// else bfs(it as Regular).firstOrNull()
// else walk(it as Regular){ sequenceOf(it.right, it.left)}.firstOrNull()
else walk(it as Regular).firstOrNull()
}
}
private fun leftMostValue(from: Node): Value? {
val starting = climb(from){it.left}
return starting?.left?.let{
if(it is Value) it
// else bfs(it as Regular){ sequenceOf(it.right, it.left)}.firstOrNull()
// else bfs(it as Regular).firstOrNull()
else walk(it as Regular){ sequenceOf(it.right, it.left)}.firstOrNull()
// else walk(it as Regular).firstOrNull()
}
}
override fun tryExplode(level: Int):Boolean {
fun shouldExplode(): Boolean {
val matches = sequenceOf(left, right)
.filter { it is Value }
// .filter { (it as Value).value < 10}
.count()
return 2 == matches
}
return if(level > 3 && shouldExplode()) {
val leftValue = (left as Value).value
val rightValue = (right as Value).value
parent!!.leftMostValue(this)?.let { it.value += leftValue }
parent!!.rightMostValue(this)?.let{ it.value += rightValue }
if(this == parent!!.left) { parent!!.left = Value(0 , parent) }
else { parent!!.right = Value(0, parent) }
true
} else {
left.tryExplode(level + 1) || right.tryExplode(level + 1)
}
}
override fun trySplit(): Boolean {
return left.trySplit() || right.trySplit()
}
override fun magnitude(): Long = 3 * left.magnitude() + 2 * right.magnitude()
override fun toString() = "[${left},${right}]"
}
class Value(var value: Int, root: Regular? = null) : Node(root) {
override fun tryExplode(level: Int): Boolean {
return false
}
fun split(): Node {
if(value < 10) return this
val lV = value / 2
val rV = value - lV
return Regular(left = Value(lV), right = Value(rV))
}
override fun trySplit(): Boolean {
if(value < 10 || parent == null) return false
val n = split()
if(parent!!.left == this) {
parent!!.left = n
} else {
parent!!.right = n
}
n.parent = parent
return true
}
override fun magnitude() = value.toLong()
override fun toString() = "$value"
}
object Day18_Part1 {
fun solve(nodes: List<Node>): Node {
return nodes.reduce{ acc, next -> acc.add(next) }
}
}
object Day18_Part2 {
fun run(a: String, b: String): Long {
val equation = parse(a).add(parse(b))
val res = equation.magnitude()
return res
}
fun solve(nodes: List<String>): Long {
val allResults = sequence {
nodes.forEach { a ->
nodes.forEach { b ->
if(a != b) {
yield(run(a,b))
yield(run(b,a))
}
}
}
}
return allResults.maxOrNull() ?: 0
}
}
fun parse(line: String): Node {
val stack = ArrayDeque<Node>()
line.onEach {
if(it.isDigit()) {
stack.addFirst(Value(value = it.digitToInt()))
}
if(it == ']') {
val r = stack.removeFirst()
val l = stack.removeFirst()
stack.addFirst(Regular(left = l, right = r))
}
}
require(stack.size == 1)
return stack[0]
}
fun main(args: Array<String>) {
val input = Files.readAllLines(Path.of(args[0]))
val part1 = Day18_Part1.solve(input.map { parse(it) }).magnitude()
val part2 = Day18_Part2.solve(input)
println(part1)
println(part2)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment