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
+ }
0 commit comments