diff --git a/2021/README.md b/2021/README.md
index cd2668e400cb1f9bea7e808ca1889ffddd9aca72..969c13e4a6d968bf4245f268e2438fa682e78122 100644
--- a/2021/README.md
+++ b/2021/README.md
@@ -121,3 +121,16 @@ This looks like a job for a stack!
Update: Yes, yes it was a job for a stack. Some maps simplified matters but
were not essential.
+
+## Day 11
+
+Part 1 looks straight-forward. As I did with Day 9, the nested array will have
+a "frame" to simplify checking neighbors. One point of clarification: if an
+octopus's energy reaches 11 due to neighbors' flashing, does it still reset to
+0, or does it *actually* flash at 10, reset to 0, and then takes on the energy
+from the other neighbor (*i.e.*, I should subtract 10 from 11, not blindly
+reset to 0). Or, upon rereading the spec, we don't reset to 0 until the very end
+of the step. Well, we'll find out...
+
+Part 2, isn't any more challenging than checking whether the number of flashes
+is equal to the number of octopi.
diff --git a/2021/src/main/java/edu/unl/cse/bohn/year2021/Day11.java b/2021/src/main/java/edu/unl/cse/bohn/year2021/Day11.java
new file mode 100644
index 0000000000000000000000000000000000000000..cca4e942fe26a11dc1b49d082cbf59b424d9d86e
--- /dev/null
+++ b/2021/src/main/java/edu/unl/cse/bohn/year2021/Day11.java
@@ -0,0 +1,131 @@
+package edu.unl.cse.bohn.year2021;
+
+import edu.unl.cse.bohn.Puzzle;
+
+import java.util.Arrays;
+import java.util.List;
+
+@SuppressWarnings("unused")
+public class Day11 extends Puzzle {
+ private int[][] octopi;
+
+ @SuppressWarnings("CommentedOutCode")
+ public Day11(boolean isProductionReady) {
+ super(isProductionReady);
+// sampleData = """
+// 11111
+// 19991
+// 19191
+// 19991
+// 11111""";
+ sampleData = """
+ 5483143223
+ 2745854711
+ 5264556173
+ 6141336146
+ 6357385478
+ 4167524645
+ 2176841721
+ 6882881134
+ 4846848554
+ 5283751526""";
+ }
+
+ private void initializeOctopi(List<String> data) {
+ octopi = new int[data.size() + 2][data.get(0).length() + 2];
+ for (int[] row : octopi) {
+ Arrays.fill(row, Integer.MIN_VALUE);
+ }
+ for (int i = 0; i < data.size(); i++) {
+ String line = data.get(i);
+ for (int j = 0; j < line.length(); j++) {
+ octopi[i + 1][j + 1] = Integer.parseInt(String.valueOf(line.charAt(j)));
+ }
+ }
+ }
+
+ private void printOctopi() {
+ for (int i = 1; i < octopi.length; i++) {
+ for (int j = 1; j < octopi[i].length - 1; j++) {
+ if (octopi[i][j] < 0) {
+ System.out.print("-");
+ } else {
+ System.out.print(octopi[i][j]);
+ }
+ }
+ System.out.println();
+ }
+ System.out.println();
+ }
+
+ private long stepOctopi() {
+ boolean[][] hasFlashed = new boolean[octopi.length][octopi[0].length];
+ boolean stillFlashing = true;
+ for (boolean[] row : hasFlashed) {
+ Arrays.fill(row, false);
+ }
+ for (int i = 1; i < octopi.length-1; i++) {
+ for (int j = 1; j < octopi[i].length-1; j++) {
+ octopi[i][j]++;
+ }
+ }
+ while (stillFlashing) {
+ stillFlashing = false;
+ for (int i = 1; i < octopi.length - 1; i++) {
+ for (int j = 1; j < octopi[i].length - 1; j++) {
+ if (octopi[i][j] > 9 && !hasFlashed[i][j]) {
+ hasFlashed[i][j] = true;
+ stillFlashing = true;
+ octopi[i-1][j-1]++;
+ octopi[i-1][j]++;
+ octopi[i-1][j+1]++;
+ octopi[i][j-1]++;
+ octopi[i][j+1]++;
+ octopi[i+1][j-1]++;
+ octopi[i+1][j]++;
+ octopi[i+1][j+1]++;
+ }
+ }
+ }
+ }
+ long numberOfFlashes = 0;
+ for (int i = 0; i < octopi.length; i++) {
+ for (int j = 0; j < octopi[i].length; j++) {
+ if (octopi[i][j] < 0) { // the frame
+ octopi[i][j] = Integer.MIN_VALUE;
+ } else if (hasFlashed[i][j]) {
+ octopi[i][j] = 0;
+ numberOfFlashes++;
+ }
+ }
+ }
+ return numberOfFlashes;
+ }
+
+ @Override
+ public long computePart1(List<String> data) {
+// maximumSteps = 2;
+ int maximumSteps = 100;
+ initializeOctopi(data);
+ long totalFlashes = 0;
+// printOctopi();
+ for (int i = 0; i < maximumSteps; i++) {
+ totalFlashes += stepOctopi();
+// printOctopi();
+ }
+ return totalFlashes;
+ }
+
+ @Override
+ public long computePart2(List<String> data) {
+ initializeOctopi(data);
+ long stepNumber = 0;
+ long numberOfOctopi = (long)data.size() * (long)data.get(0).length();
+ long numberOfFlashes;
+ do {
+ numberOfFlashes = stepOctopi();
+ stepNumber++;
+ } while (numberOfFlashes < numberOfOctopi);
+ return stepNumber;
+ }
+}