Skip to content
Snippets Groups Projects
Commit b552498b authored by Christopher Bohn's avatar Christopher Bohn :thinking:
Browse files

Completed Year 2022 Day 11

parent ea526372
No related branches found
No related tags found
No related merge requests found
......@@ -367,27 +367,33 @@ For now, I just need to modify it so that the boredom modification is optional.
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
### There's more to it than that
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.
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 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.
......@@ -395,8 +401,79 @@ 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.
#### Thought #1
I wonder if what we're seeing 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. The obvious thing to try is to have
no modification the first time that the monkey inspects an item (preserving the correct result from round 1) and then
divide by 3 if the monkey has seen it before.
No, that isn't it, either. After 20 rounds:
```
Monkey 0 inspected items 96 times.
Monkey 1 inspected items 100 times.
Monkey 2 inspected items 10 times.
Monkey 3 inspected items 102 times.
```
#### Thought #2
It doesn't seem likely that the AOC creator would want us to blindly guess at possible modifications.
![Winnie the Pooh, thinking](https://media.tenor.com/BdcwU6gstRoAAAAM/pooh-think.gif)
Maybe the answer to whether there is *no* modifier in part 2 is "both."
!["Why not both?" meme](https://media.tenor.com/odyVsZbC-OYAAAAM/why-not-both-why-not.gif)
Perhaps the hint is in the test for which monkey will receive the item -- it's a test for whether the worry level is
divisible by some value.
We can keep the worry levels down to manageable values while also preserving the outcome of this test *for any given
inspection* by assigning the worry level to the remainder of that notional division.
Then the remainder is some other value between 0 and the original worry level, which will be the same as the modulus
operator's result (and this remainder will also produce the same modulus result).
How will this affect subsequent throws? All operations are multiplication or addition.
Suppose the original value is $$i = n \times testDivisor + remainder$$ for some value $$n$$.
In the case of addition, $$i + j = n \times testDivisor + remainder + j$$ and the resulting modulus is unchanged.
In the case of multiplication, if we view it as repeated addition then the same argument holds.
Either I'm barking up the wrong tree, or we were seeing overflow errors earlier.
```
== After round 20 ==
Monkey 0 inspected items 95 times.
Monkey 1 inspected items 101 times.
Monkey 2 inspected items 9 times.
Monkey 3 inspected items 99 times.
```
Shazbot.
### Thought #3
I'm sure I'm on the right track.
I think I was off-base with my answer to how it will affect subsequent throws.
The problem is that taking the remainder of $$i \div testDivisor_x$$ might not preserve the modulus of
$$i \div testDivisor_y$$.
We need to use a divisor that would preserve both -- that is, the divisor must be a multiple of both $$testDivisor_x$$
and $$testDivisor_y$$.
Okay, *seriously!?* How does Java not have a least common multiple function?
And let's make the worry levels `long` instead of `int` just to give us a little more room to grow.
```
== After round 20 ==
Monkey 0 inspected items 99 times.
Monkey 1 inspected items 97 times.
Monkey 2 inspected items 8 times.
Monkey 3 inspected items 103 times.
```
***Shack!***
## Day 12
......
......@@ -43,22 +43,37 @@ public class Day11 extends Puzzle {
@Override
public long computePart1(List<String> data) {
Monkey.setTerse();
//noinspection MagicNumber
return measureMonkeyBusiness(data, 20, true);
}
@Override
public long computePart2(List<String> data) {
Monkey.setTerse();
System.out.println("== After round 1 ==");
measureMonkeyBusiness(data, 1, false);
System.out.println();
System.out.println("== After round 20 ==");
// Monkey.setVerbose();
Monkey.setTerse();
//noinspection MagicNumber
measureMonkeyBusiness(data, 20, false);
System.out.println();
System.out.println("== After round 1000 ==");
Monkey.setTerse();
// //noinspection MagicNumber
measureMonkeyBusiness(data, 1000, false);
System.out.println();
System.out.println("== After round 10000 ==");
Monkey.setTerse();
//noinspection MagicNumber
return measureMonkeyBusiness(data, 20, false);
return measureMonkeyBusiness(data, 10000, 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 {
......@@ -76,12 +91,12 @@ public class Day11 extends Puzzle {
}
// Monkey.printMonkeys();
Monkey.setVerbose();
LinkedList<Integer> inspectionCounts = new LinkedList<>();
LinkedList<Long> inspectionCounts = new LinkedList<>();
for (Monkey monkey : monkeys) {
inspectionCounts.add(monkey.getNumberOfInspections());
}
inspectionCounts.sort(Integer::compareTo);
return (long) inspectionCounts.removeLast() * (long) inspectionCounts.removeLast();
inspectionCounts.sort(Long::compareTo);
return inspectionCounts.removeLast() * inspectionCounts.removeLast();
}
private static class Monkey {
......@@ -89,13 +104,14 @@ public class Day11 extends Puzzle {
private static boolean useBoredomModifier = true;
private static boolean verbose = false;
private static final List<Monkey> monkeys = new LinkedList<>();
private static int lcmDivisor;
private final int monkeyNumber;
private final List<Integer> items;
private final UnaryOperator<Integer> worryLevelModifier;
private final List<Long> items;
private final UnaryOperator<Long> worryLevelModifier;
private final int testDivisor;
private final Map<Boolean, Integer> targetMonkeys;
private int inspectionCount;
private long inspectionCount;
public static void setVerbose() {
verbose = true;
......@@ -113,6 +129,26 @@ public class Day11 extends Puzzle {
useBoredomModifier = false;
}
private static int lcm(List<Integer> factors) {
return lcm(factors, 0);
}
private static int lcm(List<Integer> factors, int index) {
// modified from https://www.geeksforgeeks.org/lcm-of-given-array-elements/
// lcm(a,b) = (a*b/gcd(a,b))
if (index == factors.size() - 1) {
return factors.get(index);
}
int currentFactor = factors.get(index);
int lcmOfRemainingFactors = lcm(factors, index + 1);
return (currentFactor * lcmOfRemainingFactors / gcd(currentFactor, lcmOfRemainingFactors));
}
private static int gcd(int m, int n) {
// modified from https://www.geeksforgeeks.org/lcm-of-given-array-elements/
return n == 0 ? m : gcd(n, m % n);
}
public static void printMonkeys() {
for (Monkey monkey : monkeys) {
System.out.println("Monkey " + monkey.getMonkeyNumber() + ": " + monkey.getItems());
......@@ -124,13 +160,13 @@ public class Day11 extends Puzzle {
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)
List<Long> items = Arrays.stream(itemStrings)
.map(Long::parseLong)
.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) {
UnaryOperator<Long> 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);
......@@ -155,6 +191,8 @@ public class Day11 extends Puzzle {
System.out.println(monkey);
}
monkeys.add(monkey);
List<Integer> testDivisors = monkeys.stream().map(m -> m.testDivisor).toList();
lcmDivisor = lcm(testDivisors);
return monkey;
}
......@@ -162,7 +200,7 @@ public class Day11 extends Puzzle {
monkeys.clear();
}
private Monkey(List<Integer> items, UnaryOperator<Integer> worryLevelModifier,
private Monkey(List<Long> items, UnaryOperator<Long> worryLevelModifier,
int testDivisor, Map<Boolean, Integer> targetMonkeys) {
this.monkeyNumber = monkeys.size();
this.items = items;
......@@ -176,11 +214,11 @@ public class Day11 extends Puzzle {
return monkeyNumber;
}
private List<Integer> getItems() {
private List<Long> getItems() {
return Collections.unmodifiableList(items);
}
public int getNumberOfInspections() {
public long getNumberOfInspections() {
if (verbose) {
System.out.println("Monkey " + monkeyNumber + " inspected items " + inspectionCount + " times.");
}
......@@ -195,9 +233,9 @@ public class Day11 extends Puzzle {
items.clear(); // probably should remove each item in `throwItem()` but that could screw with the iterator
}
private void throwItem(Integer item) {
private void throwItem(Long item) {
inspectionCount++;
int worryLevel = item;
long worryLevel = item;
if (verbose) {
System.out.println(" Monkey inspects an item with a worry level of " + worryLevel + ".");
}
......@@ -206,16 +244,9 @@ public class Day11 extends Puzzle {
System.out.println(" Worry level is modified by (" + worryLevelModifier
+ ") to " + worryLevel + ".");
}
worryLevel = useBoredomModifier ? worryLevel / BOREDOM_DIVISOR : worryLevel;
worryLevel = modifyWorryLevel(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 + ".");
......@@ -225,7 +256,24 @@ public class Day11 extends Puzzle {
monkeys.get(targetMonkeys.get(worryLevelIsDivisibleByTestDivisor)).receiveItem(worryLevel);
}
private void receiveItem(Integer item) {
private long modifyWorryLevel(long worryLevel) {
if (useBoredomModifier) {
worryLevel = worryLevel / BOREDOM_DIVISOR;
if (verbose) {
System.out.println(" Monkey gets bored with item. Worry level is divided by "
+ BOREDOM_DIVISOR + " to " + worryLevel + ".");
}
} else {
worryLevel = worryLevel % lcmDivisor;
if (verbose) {
System.out.println(" Monkey doesn't get bored with item. Worry level is regulated (modulated)"
+ " by " + lcmDivisor + " to " + worryLevel + ".");
}
}
return worryLevel;
}
private void receiveItem(Long item) {
items.add(item);
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment