171 lines
4.9 KiB
Kotlin
171 lines
4.9 KiB
Kotlin
package be.vandewalleh.aoc.days
|
|
|
|
import be.vandewalleh.aoc.utils.input.Day
|
|
import be.vandewalleh.aoc.utils.input.Lines
|
|
import java.util.*
|
|
import org.slf4j.Logger
|
|
import org.slf4j.LoggerFactory
|
|
|
|
private enum class Operator { Add, Multiply }
|
|
|
|
private operator fun Operator.invoke(a: Long, b: Long) = when (this) {
|
|
Operator.Add -> a + b
|
|
Operator.Multiply -> a * b
|
|
}
|
|
|
|
private inline fun Logger.debug(msg: () -> Any) {
|
|
if (isDebugEnabled) debug(msg().toString())
|
|
}
|
|
|
|
@Day(18)
|
|
class Day18(@Lines val input: List<String>) {
|
|
|
|
private val logger = LoggerFactory.getLogger("Day18")
|
|
private val lines = input.map { it.replace(" ", "") }
|
|
|
|
private fun parseGroups(line: String): Map<Int, List<IntRange>> {
|
|
var depth = 0
|
|
val groups = mutableMapOf<Int, MutableList<IntRange>>()
|
|
val openingPars = Stack<Int>()
|
|
|
|
for ((index, char) in line.withIndex()) {
|
|
when (char) {
|
|
'(' -> {
|
|
openingPars.push(index)
|
|
depth++
|
|
}
|
|
')' -> {
|
|
depth--
|
|
val group = groups.getOrPut(depth) { mutableListOf() }
|
|
group.add(openingPars.pop()..index)
|
|
}
|
|
}
|
|
}
|
|
|
|
return groups.also { logger.debug { it } }
|
|
}
|
|
|
|
private fun solveGroup(group: String, precedence: Boolean) = solveGroup(parseGroup(group), precedence)
|
|
|
|
private fun solveGroup(group: Pair<LinkedList<Long>, LinkedList<Operator>>, precedence: Boolean): Long {
|
|
val (operands, operators) = group
|
|
logger.debug { operands }
|
|
logger.debug { operators }
|
|
|
|
if (!precedence) {
|
|
for (i in operators.indices) {
|
|
val a = operands[i]
|
|
val b = operands[i + 1]
|
|
|
|
val result = operators[i](a, b)
|
|
|
|
operands[i + 1] = result
|
|
}
|
|
|
|
return operands.peekLast()
|
|
} else {
|
|
|
|
var i = 0
|
|
while (operators.any { it == Operator.Add }) {
|
|
|
|
val operator = operators[i]
|
|
|
|
if (operator == Operator.Add) {
|
|
val a = operands[i]
|
|
val b = operands[i + 1]
|
|
val res = operator(a, b)
|
|
operands.removeAt(i)
|
|
operands[i] = res
|
|
operators.removeAt(i)
|
|
} else {
|
|
i++
|
|
}
|
|
}
|
|
|
|
return operands.reduce { a, b -> Operator.Multiply(a, b) }
|
|
}
|
|
}
|
|
|
|
private fun parseGroup(group: String): Pair<LinkedList<Long>, LinkedList<Operator>> {
|
|
val operands = LinkedList<Long>()
|
|
val operators = LinkedList<Operator>()
|
|
val operand = StringBuilder()
|
|
|
|
for (char in group) {
|
|
if (char.isDigit()) {
|
|
operand.append(char)
|
|
} else {
|
|
val operator = when (char) {
|
|
'+' -> Operator.Add
|
|
'*' -> Operator.Multiply
|
|
else -> error("Unknown operator $char")
|
|
}
|
|
operators.add(operator)
|
|
operands.add(operand.toString().toLong())
|
|
operand.clear()
|
|
}
|
|
}
|
|
|
|
operands.add(operand.toString().toLong())
|
|
|
|
return Pair(operands, operators)
|
|
}
|
|
|
|
private fun solveLine(line: String, precedence: Boolean = false): Long {
|
|
var l = line
|
|
|
|
while (true) {
|
|
val groups = parseGroups(l)
|
|
if (groups.isEmpty()) break
|
|
|
|
val highestDepth = groups.keys.maxOrNull()!!
|
|
val solvableRanges = groups[highestDepth]!!
|
|
|
|
val solved = ArrayDeque<Pair<IntRange, Long>>()
|
|
|
|
for (range in solvableRanges) {
|
|
val rangeWithoutPars = range.first + 1 until range.last
|
|
val answer = solveGroup(l.substring(rangeWithoutPars), precedence)
|
|
solved.addLast(range to answer)
|
|
}
|
|
|
|
var solution = solved.removeFirst()
|
|
var inside = false
|
|
|
|
val builder = StringBuilder()
|
|
|
|
for (i in l.indices) {
|
|
if (!inside && i in solution.first) {
|
|
inside = true
|
|
builder.append(solution.second)
|
|
} else if (inside && i !in solution.first) {
|
|
inside = false
|
|
|
|
solution = if (solved.isEmpty()) -1..-1 to -1 // avoid null checks..
|
|
else solved.removeFirst()
|
|
|
|
builder.append(l[i])
|
|
} else if (i !in solution.first) {
|
|
builder.append(l[i])
|
|
}
|
|
}
|
|
|
|
l = builder.toString()
|
|
}
|
|
|
|
return solveGroup(l, precedence)
|
|
}
|
|
|
|
fun part1() = lines
|
|
.parallelStream()
|
|
.map { solveLine(it) }
|
|
.reduce { t, u -> t + u }
|
|
.get()
|
|
|
|
fun part2() = lines
|
|
.parallelStream()
|
|
.map { solveLine(it, precedence = true) }
|
|
.reduce { t, u -> t + u }
|
|
.get()
|
|
}
|