|
| 1 | +// Advent of Code 2024, Day 07. |
| 2 | +// By Sebastian Raaphorst, 2024. |
| 3 | + |
| 4 | +package day07 |
| 5 | + |
| 6 | +import common.day |
| 7 | +import common.readInput |
| 8 | +import java.math.BigInteger |
| 9 | + |
| 10 | +data class Equation(val total: BigInteger, val numbers: List<BigInteger>) { |
| 11 | + /** |
| 12 | + * The numbers list is evaluated left-to-right, and entries can be joined with + or *, |
| 13 | + * or if allowConcat is true, they can also be concatenated. |
| 14 | + */ |
| 15 | + private fun canBeMade(allowConcat: Boolean): Boolean { |
| 16 | + fun aux(currTotal: BigInteger = numbers.first(), |
| 17 | + remainingNumbers: List<BigInteger> = numbers.drop(1)): Boolean { |
| 18 | + // currTotal can only ever increase, so if we already passed total, there is no |
| 19 | + // reason to continue. |
| 20 | + if (currTotal > total) return false |
| 21 | + |
| 22 | + // If we have used up all the numbers and have achieved total, success. |
| 23 | + if (remainingNumbers.isEmpty()) return currTotal == total |
| 24 | + |
| 25 | + // Calculate all of the possible extensions. |
| 26 | + val nextNumber = remainingNumbers.first() |
| 27 | + val nextRemainingNumbers = remainingNumbers.drop(1) |
| 28 | + return aux(currTotal + nextNumber, nextRemainingNumbers) || |
| 29 | + aux(currTotal * nextNumber, nextRemainingNumbers) || |
| 30 | + (allowConcat && aux("$currTotal$nextNumber".toBigInteger(), nextRemainingNumbers)) |
| 31 | + } |
| 32 | + return aux() |
| 33 | + } |
| 34 | + |
| 35 | + fun canBeMadeWithPlusTimes() = |
| 36 | + canBeMade(false) |
| 37 | + |
| 38 | + fun canBeMadeWithPlusTimesConcat() = |
| 39 | + canBeMade(true) |
| 40 | + |
| 41 | + companion object { |
| 42 | + fun parseLine(input: String): Equation { |
| 43 | + val (total, remaining) = input.split(":") |
| 44 | + val numbers = remaining |
| 45 | + .trim() |
| 46 | + .split(Regex("""\s+""")) |
| 47 | + .map(String::toBigInteger) |
| 48 | + return Equation(total.toBigInteger(), numbers) |
| 49 | + } |
| 50 | + } |
| 51 | +} |
| 52 | + |
| 53 | +fun parse(input: String): List<Equation> = |
| 54 | + input.trim().lines().map(Equation::parseLine) |
| 55 | + |
| 56 | +fun answer1(equations: List<Equation>): BigInteger = |
| 57 | + equations.filter(Equation::canBeMadeWithPlusTimes).sumOf(Equation::total) |
| 58 | + |
| 59 | +fun answer2(equations: List<Equation>): BigInteger = |
| 60 | + equations.filter(Equation::canBeMadeWithPlusTimesConcat).sumOf(Equation::total) |
| 61 | + |
| 62 | + |
| 63 | +fun main() { |
| 64 | + val input = readInput({}::class.day()).trim() |
| 65 | + val equations = parse(input) |
| 66 | + |
| 67 | + println("--- Day 7: Bridge Repair ---") |
| 68 | + |
| 69 | + // Part 1: 2437272016585 |
| 70 | + println("Part 1: ${answer1(equations)}") |
| 71 | + |
| 72 | + // Part 2: 162987117690649 |
| 73 | + println("Part 2: ${answer2(equations)}") |
| 74 | +} |
0 commit comments