Skip to content

Commit 088a3c2

Browse files
committed
Day 11
1 parent 4b56955 commit 088a3c2

File tree

2 files changed

+216
-0
lines changed

2 files changed

+216
-0
lines changed

src/test/java/com/macasaet/Day11.java

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
package com.macasaet;
2+
3+
import org.junit.jupiter.api.Test;
4+
5+
import java.math.BigInteger;
6+
import java.util.Arrays;
7+
import java.util.Comparator;
8+
import java.util.List;
9+
import java.util.Objects;
10+
import java.util.concurrent.atomic.AtomicReference;
11+
import java.util.function.BiFunction;
12+
import java.util.function.Function;
13+
import java.util.stream.Collectors;
14+
import java.util.stream.StreamSupport;
15+
16+
/**
17+
* --- Day 11: Monkey in the Middle ---
18+
* <a href="https://adventofcode.com/2022/day/11">https://adventofcode.com/2022/day/11</a>
19+
*/
20+
public class Day11 {
21+
22+
public enum Operator implements BiFunction<BigInteger, BigInteger, BigInteger> {
23+
ADD {
24+
public BigInteger apply(BigInteger x, BigInteger y) {
25+
return x.add(y);
26+
}
27+
},
28+
MULTIPLY {
29+
public BigInteger apply(BigInteger x, BigInteger y) {
30+
return x.multiply(y);
31+
}
32+
};
33+
34+
public static Operator parse(final String string) {
35+
return switch(string) {
36+
case "*" -> MULTIPLY;
37+
case "+" -> ADD;
38+
default -> throw new IllegalArgumentException("Invalid operator: " + string);
39+
};
40+
}
41+
}
42+
43+
public record Operation(Operator operator,
44+
Function<BigInteger, BigInteger> lValueSupplier,
45+
Function<BigInteger, BigInteger> rValueSupplier) implements Function<BigInteger, BigInteger> {
46+
47+
public BigInteger apply(final BigInteger oldValue) {
48+
Objects.requireNonNull(oldValue);
49+
final var lValue = lValueSupplier.apply(oldValue);
50+
final var rValue = rValueSupplier.apply(oldValue);
51+
return operator.apply(lValue, rValue);
52+
}
53+
54+
public static Operation parse(String line) {
55+
line = line.strip();
56+
if(!line.trim().startsWith("Operation:")) {
57+
throw new IllegalArgumentException("Not an operation: " + line);
58+
}
59+
final var components = line.split(" ");
60+
final var lValueExpression = components[3];
61+
final Function<BigInteger, BigInteger> lValueSupplier = "old".equalsIgnoreCase(lValueExpression)
62+
? old -> old
63+
: ignored -> new BigInteger(lValueExpression);
64+
final var operator = Operator.parse(components[4]);
65+
final var rValueExpression = components[5];
66+
final Function<BigInteger, BigInteger> rValueSupplier = "old".equalsIgnoreCase(rValueExpression)
67+
? old -> old
68+
: ignored -> new BigInteger(rValueExpression);
69+
return new Operation(operator, lValueSupplier, rValueSupplier);
70+
}
71+
}
72+
73+
/**
74+
* An observation of how a single monkey behaves
75+
*
76+
* @param id the monkey's unique identifier
77+
* @param items your worry level for each belonging currently held by this monkey
78+
* @param operation a function describing how your worry level changes when the monkey inspects the item
79+
* @param divisor used by the monkey to evaluate your worry level and decide what to do with the item
80+
* @param targetIfTrue the ID of the monkey who will receive the item should the test pass
81+
* @param targetIfFalse the ID of the monkey who will receive the item should the test fail
82+
* @param itemsInspected the total number of times this monkey has inspected an item
83+
*/
84+
public record Monkey(int id, List<BigInteger> items, Operation operation, BigInteger divisor, int targetIfTrue, int targetIfFalse, AtomicReference<BigInteger> itemsInspected) {
85+
public static Monkey parse(final String block) {
86+
final var lines = block.split("\n");
87+
final var id = Integer.parseInt(lines[0].replaceAll("[^0-9]", ""));
88+
final var startingItems = Arrays.stream(lines[1].strip()
89+
.replaceAll("^Starting items: ", "")
90+
.split(", "))
91+
.map(item -> new BigInteger(item))
92+
.collect(Collectors.toList()); // must be mutable
93+
final var operation = Operation.parse(lines[2]);
94+
final var divisor = new BigInteger(lines[3].replaceAll("[^0-9]", ""));
95+
final var targetIfTrue = Integer.parseInt(lines[4].replaceAll("[^0-9]", ""));
96+
final var targetIfFalse = Integer.parseInt(lines[5].replaceAll("[^0-9]", ""));
97+
return new Monkey(id, startingItems, operation, divisor, targetIfTrue, targetIfFalse, new AtomicReference<>(BigInteger.ZERO));
98+
}
99+
100+
public BigInteger countItemsInspected() {
101+
return itemsInspected.get();
102+
}
103+
104+
public Throw inspectItem(BigInteger reliefFactor) {
105+
// this assumes monkeys can throw items to themselves
106+
if(items.isEmpty()) {
107+
return null;
108+
}
109+
var worryLevel = items().remove(0);
110+
worryLevel = operation().apply(worryLevel);
111+
worryLevel = worryLevel.divide(reliefFactor);
112+
final var target = worryLevel.mod(divisor()).equals(BigInteger.ZERO)
113+
? targetIfTrue()
114+
: targetIfFalse();
115+
itemsInspected().updateAndGet(old -> old.add(BigInteger.ONE));
116+
return new Throw(target, worryLevel);
117+
}
118+
119+
public List<Throw> inspectItems(Function<BigInteger, BigInteger> worryUpdater) {
120+
// this assumes monkeys cannot throw items to themselves
121+
final var result = items().stream().map(worryLevel -> {
122+
worryLevel = operation().apply(worryLevel);
123+
worryLevel = worryUpdater.apply(worryLevel);
124+
final var target = worryLevel.mod(divisor()).equals(BigInteger.ZERO)
125+
? targetIfTrue()
126+
: targetIfFalse();
127+
return new Throw(target, worryLevel);
128+
}).toList();
129+
itemsInspected().updateAndGet(old -> old.add(BigInteger.valueOf(result.size())));
130+
items().clear();
131+
return result;
132+
}
133+
134+
}
135+
136+
public record Throw(int target, BigInteger itemWorryLevel) {
137+
}
138+
139+
protected List<Monkey> getInput() {
140+
final var input = StreamSupport
141+
.stream(new LineSpliterator("day-11.txt"),
142+
false).collect(Collectors.joining("\n"));
143+
return Arrays.stream(input.split("\n\n"))
144+
.map(Monkey::parse)
145+
.toList();
146+
}
147+
148+
@Test
149+
public final void part1() {
150+
final var monkeys = getInput();
151+
final Function<BigInteger, BigInteger> worryUpdater = worryLevel -> worryLevel.divide(BigInteger.valueOf(3));
152+
for(int i = 20; --i >= 0; ) {
153+
for(final var monkey : monkeys) {
154+
for(final var toss : monkey.inspectItems(worryUpdater)) {
155+
monkeys.get(toss.target()).items().add(toss.itemWorryLevel());
156+
}
157+
}
158+
}
159+
final var result = monkeys.stream()
160+
.map(Monkey::countItemsInspected)
161+
.sorted(Comparator.reverseOrder())
162+
.limit(2)
163+
.reduce(BigInteger::multiply)
164+
.get();
165+
System.out.println("Part 1: " + result);
166+
}
167+
168+
@Test
169+
public final void part2() {
170+
final var monkeys = getInput();
171+
final var productOfDivisors = monkeys.stream().map(Monkey::divisor).reduce(BigInteger::multiply).get();
172+
final Function<BigInteger, BigInteger> worryUpdater = worryLevel -> worryLevel.mod(productOfDivisors);
173+
for(int i = 10_000; --i >= 0; ) {
174+
for(final var monkey : monkeys) {
175+
for(final var toss : monkey.inspectItems(worryUpdater)) {
176+
monkeys.get(toss.target()).items().add(toss.itemWorryLevel());
177+
}
178+
}
179+
}
180+
final var result = monkeys.stream()
181+
.map(Monkey::countItemsInspected)
182+
.sorted(Comparator.reverseOrder())
183+
.limit(2)
184+
.reduce(BigInteger::multiply)
185+
.get();
186+
System.out.println("Part 2: " + result);
187+
}
188+
189+
}

src/test/resources/sample/day-11.txt

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
Monkey 0:
2+
Starting items: 79, 98
3+
Operation: new = old * 19
4+
Test: divisible by 23
5+
If true: throw to monkey 2
6+
If false: throw to monkey 3
7+
8+
Monkey 1:
9+
Starting items: 54, 65, 75, 74
10+
Operation: new = old + 6
11+
Test: divisible by 19
12+
If true: throw to monkey 2
13+
If false: throw to monkey 0
14+
15+
Monkey 2:
16+
Starting items: 79, 60, 97
17+
Operation: new = old * old
18+
Test: divisible by 13
19+
If true: throw to monkey 1
20+
If false: throw to monkey 3
21+
22+
Monkey 3:
23+
Starting items: 74
24+
Operation: new = old + 3
25+
Test: divisible by 17
26+
If true: throw to monkey 0
27+
If false: throw to monkey 1

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy