Last active
December 18, 2021 18:52
-
-
Save bartoszm/b25556db5130fb2cf955b726374d8bc1 to your computer and use it in GitHub Desktop.
Day 18 - ugly solution for both parts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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