From 988450b44a0928bf14b6bef26ce17b631a46c311 Mon Sep 17 00:00:00 2001 From: gergo Date: Sun, 11 Dec 2022 20:23:25 +0100 Subject: [PATCH] day 11 --- src/main/kotlin/me/gergo/Aoc11.kt | 102 ++++++++++++++++++++++++++++++ src/main/resources/input11.txt | 55 ++++++++++++++++ 2 files changed, 157 insertions(+) create mode 100644 src/main/kotlin/me/gergo/Aoc11.kt create mode 100644 src/main/resources/input11.txt diff --git a/src/main/kotlin/me/gergo/Aoc11.kt b/src/main/kotlin/me/gergo/Aoc11.kt new file mode 100644 index 0000000..baf7290 --- /dev/null +++ b/src/main/kotlin/me/gergo/Aoc11.kt @@ -0,0 +1,102 @@ +package me.gergo + +import java.io.File +import java.util.* + +private const val Rounds = 10000 + +/** + * Note: this only has Part Two, as I did Part One with a naiive solution that probably takes all the memory in the world, multiplying horribly large + * BigDecimals :) The trick in this one is to remember but delay the computation for as long as possible. As we're only really interested in whether + * the result at the end of a long chain of operations is divisible by N, we can modulo N per every operation - but we must do that for each monkey + * individually, which means we must remember the computation chain (Items.operations in this implementation)! + */ +fun main() { + val monkeys = File("src/main/resources/input11.txt").readText().split("\n\n") + .map { it.split("\n") } + .map(::parseMonkey) + + + for (i in 1..Rounds) { + for (monkey in monkeys) { + monkey.inspectAndThrowAll(monkeys) + } + } + + for (monkey in monkeys) { + println("Monkey ${monkey.name} inspected items ${monkey.inspections} times") + } + + val result = monkeys.map(Monkey::inspections).sortedDescending().take(2).reduce(Long::times) + println("Level of monkey business: $result") +} + +private class Monkey(val name: Int, val items: Queue, val operation: Operation, val divisor: Int, val throwToTrue: Int, val throwToFalse: Int) { + var inspections = 0L + + fun inspectAndThrowAll(monkeys: List) { + while (true) { + val item = items.poll() ?: return + item.operations.add(operation) + inspections++ + val throwTo = if (item.divisibleBy(divisor)) throwToTrue else throwToFalse + monkeys[throwTo].catch(item) + } + } + + fun catch(item: Item) { + items.add(item) + } +} + +private data class Item(val value: Int, val operations: MutableList) { + fun divisibleBy(divisor: Int): Boolean { + var current = value + for (operation in operations) { + current = when (operation) { + is Mul -> (current * operation.multiplier) % divisor + is Add -> (current + operation.add) % divisor + Square -> (current * current) % divisor + } + } + return current % divisor == 0 + } +} + +private sealed interface Operation +private data class Mul(val multiplier: Int) : Operation +private data class Add(val add: Int) : Operation +private object Square : Operation + +private fun parseMonkey(lines: List): Monkey { + val name = lines[0].replace(Regex("\\D+"), "").toInt() + val items = lines[1].substring(" Starting items: ".length).split(", ").map { Item(it.toInt(), mutableListOf()) } + val operation = parseOperation(lines[2].substring(" Operation: new = ".length)) + val divisor = lines[3].substring(" Test: divisible by ".length).toInt() + val throwToTrue = lines[4].substring(" If true: throw to monkey ".length).toInt() + val throwToFalse = lines[5].substring(" If false: throw to monkey ".length).toInt() + return Monkey( + name, + items.toCollection(LinkedList()), + operation, + divisor, + throwToTrue, + throwToFalse + ) +} + +private fun parseOperation(value: String): Operation { + val mr = Regex("(\\w+) ([*+]) (\\w+)").matchEntire(value)!! + assert(mr.groupValues[1] == "old") + val op = mr.groupValues[2] + val opB = mr.groupValues[3] + return if (op == "*") { + if (opB == "old") { + Square + } else { + Mul(opB.toInt()) + } + } else { + Add(opB.toInt()) + } +} diff --git a/src/main/resources/input11.txt b/src/main/resources/input11.txt new file mode 100644 index 0000000..78b36cc --- /dev/null +++ b/src/main/resources/input11.txt @@ -0,0 +1,55 @@ +Monkey 0: + Starting items: 89, 84, 88, 78, 70 + Operation: new = old * 5 + Test: divisible by 7 + If true: throw to monkey 6 + If false: throw to monkey 7 + +Monkey 1: + Starting items: 76, 62, 61, 54, 69, 60, 85 + Operation: new = old + 1 + Test: divisible by 17 + If true: throw to monkey 0 + If false: throw to monkey 6 + +Monkey 2: + Starting items: 83, 89, 53 + Operation: new = old + 8 + Test: divisible by 11 + If true: throw to monkey 5 + If false: throw to monkey 3 + +Monkey 3: + Starting items: 95, 94, 85, 57 + Operation: new = old + 4 + Test: divisible by 13 + If true: throw to monkey 0 + If false: throw to monkey 1 + +Monkey 4: + Starting items: 82, 98 + Operation: new = old + 7 + Test: divisible by 19 + If true: throw to monkey 5 + If false: throw to monkey 2 + +Monkey 5: + Starting items: 69 + Operation: new = old + 2 + Test: divisible by 2 + If true: throw to monkey 1 + If false: throw to monkey 3 + +Monkey 6: + Starting items: 82, 70, 58, 87, 59, 99, 92, 65 + Operation: new = old * 11 + Test: divisible by 5 + If true: throw to monkey 7 + If false: throw to monkey 4 + +Monkey 7: + Starting items: 91, 53, 96, 98, 68, 82 + Operation: new = old * old + Test: divisible by 3 + If true: throw to monkey 4 + If false: throw to monkey 2