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) { private val logger = LoggerFactory.getLogger("Day18") private val lines = input.map { it.replace(" ", "") } private fun parseGroups(line: String): Map> { var depth = 0 val groups = mutableMapOf>() val openingPars = Stack() 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>, 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> { val operands = LinkedList() val operators = LinkedList() 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>() 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() }