diff --git a/2022/README.md b/2022/README.md
index 6c537cf04f9f76791579cb7f438ef53f559e6c39..26dc5a0d961375044400067c14a9c80d9bba1de0 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 0000000000000000000000000000000000000000..e08cb452eb2ed5d7b72695aa29cce4e9b431dae5
--- /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);
+        }
+    }
+}