From ea526372b3cea0adf738a6fa02810fc985f9fdf0 Mon Sep 17 00:00:00 2001 From: Christopher Bohn <bohn@unl.edu> Date: Sun, 11 Dec 2022 11:15:45 -0600 Subject: [PATCH] Completed Year 2022 Day 11 part 1. Still working on part 2. --- 2022/README.md | 67 +++++ .../java/edu/unl/cse/bohn/year2022/Day11.java | 242 ++++++++++++++++++ 2 files changed, 309 insertions(+) create mode 100644 2022/src/main/java/edu/unl/cse/bohn/year2022/Day11.java diff --git a/2022/README.md b/2022/README.md index 6c537cf..26dc5a0 100644 --- a/2022/README.md +++ b/2022/README.md @@ -333,5 +333,72 @@ I'm revisiting the signal strength code. ## Day 11 +- [The problem](https://adventofcode.com/2022/day/11) +- [The solution](src/main/java/edu/unl/cse/bohn/year2022/Day11.java) + +Oof. This is a long description. Probably because the input is so verbose, and then you have the exmaple. + +### Part 1 + +The subproblems are + +- Parse the input to obtain, for each monkey, its + - starting items -- not only the value (worry level), but also the order + - relationship between the new and old worry levels + - after examining the input, all operations appear to be addition or multiplication, and the first operand is + always the old value + - test operation and the true/false target monkeys + - after examining the input, all tests are whether the worry level is divisible by some integer + - +- Determine which monkey will throw an item next +- Reduce an item's worry level immediately before it is thrown +- Track how many (probably non-unique) items a monkey inspects across the many rounds +- Determine the two most active monkeys after 20 rounds + +### Part 2 + +The part 2 description seems to be forecasting that the `Monkey` class will be used and modified again in the coming +day(s). +If that happens, then I'll move `Monkey` to be an outer class instead of an inner class. +For now, I just need to modify it so that the boredom modification is optional. + +### Refactoring + +Since parts 1 & 2 differ only in whether to use the boredom modification and the number of rounds, +I can create a parameterized method. + +### Possible misinterpretation + +It looks like part 2 isn't saying to abandon the divisor *now* to replace the modifier later; +I guess we're supposed to figure out the new modifier for of part 2. +That is more interesting, to say the least. + +Both the description and my code (without a boredom modifier) agree that after 1 round: +``` +Monkey 0 inspected items 2 times. +Monkey 1 inspected items 4 times. +Monkey 2 inspected items 3 times. +Monkey 3 inspected items 6 times. +``` +but after 20, the description says +``` +Monkey 0 inspected items 99 times. +Monkey 1 inspected items 97 times. +Monkey 2 inspected items 8 times. +Monkey 3 inspected items 103 times. +``` +but my code (without a boredom modifier) says +``` +Monkey 0 inspected items 96 times. +Monkey 1 inspected items 100 times. +Monkey 2 inspected items 7 times. +Monkey 3 inspected items 103 times. +``` + +I wonder if this suggests the final pre-throw modifier is history based; that is, no modifier on the first inspection, +but then the monkey gets a little bored at something it's seen before. + +## Day 12 + (coming soon) diff --git a/2022/src/main/java/edu/unl/cse/bohn/year2022/Day11.java b/2022/src/main/java/edu/unl/cse/bohn/year2022/Day11.java new file mode 100644 index 0000000..e08cb45 --- /dev/null +++ b/2022/src/main/java/edu/unl/cse/bohn/year2022/Day11.java @@ -0,0 +1,242 @@ +package edu.unl.cse.bohn.year2022; + +import edu.unl.cse.bohn.Puzzle; + +import java.util.*; +import java.util.function.UnaryOperator; +import java.util.stream.Collectors; + +@SuppressWarnings("unused") +public class Day11 extends Puzzle { + + public Day11(boolean isProductionReady) { + super(isProductionReady); + sampleData = """ + Monkey 0: + Starting items: 79, 98 + Operation: new = old * 19 + Test: divisible by 23 + If true: throw to monkey 2 + If false: throw to monkey 3 + + Monkey 1: + Starting items: 54, 65, 75, 74 + Operation: new = old + 6 + Test: divisible by 19 + If true: throw to monkey 2 + If false: throw to monkey 0 + + Monkey 2: + Starting items: 79, 60, 97 + Operation: new = old * old + Test: divisible by 13 + If true: throw to monkey 1 + If false: throw to monkey 3 + + Monkey 3: + Starting items: 74 + Operation: new = old + 3 + Test: divisible by 17 + If true: throw to monkey 0 + If false: throw to monkey 1"""; + } + + @Override + public long computePart1(List<String> data) { + //noinspection MagicNumber + return measureMonkeyBusiness(data, 20, true); + } + + @Override + public long computePart2(List<String> data) { + measureMonkeyBusiness(data, 1, false); + System.out.println(); + //noinspection MagicNumber + return measureMonkeyBusiness(data, 20, false); + } + + private static long measureMonkeyBusiness(List<String> data, int numberOfRounds, boolean useBoredomModifier) { + List<Monkey> monkeys = new LinkedList<>(); + Monkey.clearMonkeys(); + Monkey.setTerse(); + if (useBoredomModifier) { + Monkey.setBoredomModification(); + } else { + Monkey.clearBoredomModification(); + } + int monkeyNumber = 0; + while (monkeyNumber * 7 < data.size()) { + monkeys.add(Monkey.createMonkey(data.subList(monkeyNumber * 7, monkeyNumber * 7 + 6))); + monkeyNumber++; + } + for (int i = 0; i < numberOfRounds; i++) { + for (Monkey monkey : monkeys) { + monkey.throwItems(); + } + } +// Monkey.printMonkeys(); + Monkey.setVerbose(); + LinkedList<Integer> inspectionCounts = new LinkedList<>(); + for (Monkey monkey: monkeys) { + inspectionCounts.add(monkey.getNumberOfInspections()); + } + inspectionCounts.sort(Integer::compareTo); + return (long) inspectionCounts.removeLast() * (long) inspectionCounts.removeLast(); + } + + private static class Monkey { + public static final int BOREDOM_DIVISOR = 3; + private static boolean useBoredomModifier = true; + private static boolean verbose = false; + private static final List<Monkey> monkeys = new LinkedList<>(); + + private final int monkeyNumber; + private final List<Integer> items; + private final UnaryOperator<Integer> worryLevelModifier; + private final int testDivisor; + private final Map<Boolean, Integer> targetMonkeys; + private int inspectionCount; + + public static void setVerbose() { + verbose = true; + } + + public static void setTerse() { + verbose = false; + } + + public static void setBoredomModification() { + useBoredomModifier = true; + } + + public static void clearBoredomModification() { + useBoredomModifier = false; + } + + public static void printMonkeys() { + for (Monkey monkey : monkeys) { + System.out.println("Monkey " + monkey.getMonkeyNumber() + ": " + monkey.getItems()); + } + } + + public static Monkey createMonkey(List<String> description) { + // >Monkey 0: + int intendedMonkeyNumber = Integer.parseInt(description.get(0).strip().split(" ")[1].split(":")[0]); + // > Starting items: 79, 98 + String[] itemStrings = description.get(1).split(":")[1].strip().split(", "); + List<Integer> items = Arrays.stream(itemStrings) + .map(Integer::parseInt) + .collect(Collectors.toCollection(LinkedList::new)); + // > Operation: new = old * 19 + String operator = description.get(2).strip().split(" ")[4]; + String operand = description.get(2).strip().split(" ")[5]; + UnaryOperator<Integer> worryLevelModifier = switch (operator) { + case "+" -> operand.equals("old") ? (n -> n + n) : (n -> n + Integer.parseInt(operand)); + case "*" -> operand.equals("old") ? (n -> n * n) : (n -> n * Integer.parseInt(operand)); + default -> throw new UnsupportedOperationException("Unhandled operator: " + operator); + }; + // > Test: divisible by 23 + int testDivisor = Integer.parseInt(description.get(3).strip().split(" ")[3]); + // > If true: throw to monkey 2 + Map<Boolean, Integer> targetMonkeys = new HashMap<>(); + targetMonkeys.put(true, Integer.parseInt(description.get(4).strip().split(" ")[5])); + // > If false: throw to monkey 3 + targetMonkeys.put(false, Integer.parseInt(description.get(5).strip().split(" ")[5])); + Monkey monkey = new Monkey(items, worryLevelModifier, testDivisor, targetMonkeys); + if (monkey.getMonkeyNumber() != intendedMonkeyNumber) { + System.err.println("[WARNING] expected to create monkey " + intendedMonkeyNumber + + " but created monkey " + monkey.getMonkeyNumber()); + } + if (monkey.getMonkeyNumber() != monkeys.size()) { + System.err.println("[WARNING] monkey " + monkey.getMonkeyNumber() + " will be placed in position " + + monkeys.size() + " in the list."); + } + if (verbose) { + System.out.println(monkey); + } + monkeys.add(monkey); + return monkey; + } + + public static void clearMonkeys() { + monkeys.clear(); + } + + private Monkey(List<Integer> items, UnaryOperator<Integer> worryLevelModifier, + int testDivisor, Map<Boolean, Integer> targetMonkeys) { + this.monkeyNumber = monkeys.size(); + this.items = items; + this.worryLevelModifier = worryLevelModifier; + this.testDivisor = testDivisor; + this.targetMonkeys = targetMonkeys; + this.inspectionCount = 0; + } + + private int getMonkeyNumber() { + return monkeyNumber; + } + + private List<Integer> getItems() { + return Collections.unmodifiableList(items); + } + + public int getNumberOfInspections() { + if (verbose) { + System.out.println("Monkey " + monkeyNumber + " inspected items " + inspectionCount + " times."); + } + return inspectionCount; + } + + public void throwItems() { + if (verbose) { + System.out.println("Monkey: " + monkeyNumber); + } + items.forEach(this::throwItem); + items.clear(); // probably should remove each item in `throwItem()` but that could screw with the iterator + } + + private void throwItem(Integer item) { + inspectionCount++; + int worryLevel = item; + if (verbose) { + System.out.println(" Monkey inspects an item with a worry level of " + worryLevel + "."); + } + worryLevel = worryLevelModifier.apply(worryLevel); + if (verbose) { + System.out.println(" Worry level is modified by (" + worryLevelModifier + + ") to " + worryLevel + "."); + } + worryLevel = useBoredomModifier ? worryLevel / BOREDOM_DIVISOR : worryLevel; + boolean worryLevelIsDivisibleByTestDivisor = worryLevel % testDivisor == 0; + if (verbose) { + if (useBoredomModifier) { + System.out.println(" Monkey gets bored with item. Worry level is divided by " + + BOREDOM_DIVISOR + " to " + worryLevel + "."); + } else { + System.out.println(" Monkey doesn't get bored with item. Worry level remains " + + worryLevel + "."); + } + System.out.println(" Current worry level is " + + (worryLevelIsDivisibleByTestDivisor ? "" : "not ") + + "divisible by " + testDivisor + "."); + System.out.println(" Item with worry level " + worryLevel + + " is thrown to monkey " + targetMonkeys.get(worryLevelIsDivisibleByTestDivisor) + "."); + } + monkeys.get(targetMonkeys.get(worryLevelIsDivisibleByTestDivisor)).receiveItem(worryLevel); + } + + private void receiveItem(Integer item) { + items.add(item); + } + + @Override + public String toString() { + return "Monkey " + monkeyNumber + + System.lineSeparator() + " Items: " + items + + System.lineSeparator() + " Operation: " + worryLevelModifier + + System.lineSeparator() + " Test: divisible by " + testDivisor + + System.lineSeparator() + " If true: throw to monkey " + targetMonkeys.get(true) + + System.lineSeparator() + " If false: throw to monkey " + targetMonkeys.get(false); + } + } +} -- GitLab