diff --git a/2022/README.md b/2022/README.md
index de75d511cbfa258c10c9c54381fadfd32c8e3139..6d1f3b647376bb810dbbeb6e4d7bac6bd0d89d58 100644
--- a/2022/README.md
+++ b/2022/README.md
@@ -218,5 +218,17 @@ it will serve both parts.
 
 ## Day 8
 
+### Part 1
+
+The subproblems are
+- Determine whether a tree is visible
+  - From the right or left
+  - From the top or bottom (solution space -- this may be easier if I add a subproblem of rotating the matrix)
+- Count the number of visible trees
+
+### Part 2
+
+## Day 9
+
 (coming soon)
 
diff --git a/2022/src/main/java/edu/unl/cse/bohn/year2022/Day8.java b/2022/src/main/java/edu/unl/cse/bohn/year2022/Day8.java
new file mode 100644
index 0000000000000000000000000000000000000000..ef732038718f39c1a6e3bb65d9f826042c7cfe80
--- /dev/null
+++ b/2022/src/main/java/edu/unl/cse/bohn/year2022/Day8.java
@@ -0,0 +1,87 @@
+package edu.unl.cse.bohn.year2022;
+
+import edu.unl.cse.bohn.Puzzle;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.IntStream;
+
+@SuppressWarnings("unused")
+public class Day8 extends Puzzle {
+    public Day8(boolean isProductionReady) {
+        super(isProductionReady);
+        sampleData = """
+                30373
+                25512
+                65332
+                33549
+                35390""";
+    }
+
+    @Override
+    public long computePart1(List<String> data) {
+        boolean[][] visibleTrees = computeTreeVisibility(data);
+        long numberOfVisibleTrees = 0;
+        for (boolean[] row : visibleTrees) {
+            numberOfVisibleTrees += IntStream.range(0, row.length).map(i -> row[i] ? 1 : 0).sum();
+        }
+        return numberOfVisibleTrees;
+    }
+
+    @Override
+    public long computePart2(List<String> data) {
+        return 0;
+    }
+
+    private boolean[][] computeTreeVisibility(List<String> data) {
+        // Initially assume each tree is not visible; if it is the tallest tree from any direction, it is visible.
+        boolean[][] visibleTrees = new boolean[data.size()][data.get(0).length()];
+        // Put the tree heights in a form that will be easy to use and manipulate.
+        int[][] treeHeights = new int[data.size()][data.get(0).length()];
+        for (int i = 0; i < visibleTrees.length; i++) {
+            Arrays.fill(visibleTrees[i], false);
+            treeHeights[i] = data.get(i).chars().map(c -> c - '0').toArray();
+        }
+        int[][] rotatedTreeHeights = flipMatrixAlongDiagonal(treeHeights);
+        // Is each tree visible from left or right?
+        for (int i = 0; i < visibleTrees.length; i++) {
+            List<Integer> row = Arrays.stream(treeHeights[i]).boxed().toList();
+            for (int j = 0; j < visibleTrees[i].length; j++) {
+                if (j == 0 || j == visibleTrees[i].length - 1) {
+                    visibleTrees[i][j] = true;
+                } else {
+                    visibleTrees[i][j] = visibleTrees[i][j]
+                            || (treeHeights[i][j] > row.subList(0, j).stream().max(Integer::compareTo).orElseThrow())
+                            || (treeHeights[i][j] > row.subList(j + 1,
+                            treeHeights[i].length).stream().max(Integer::compareTo).orElseThrow());
+                }
+            }
+        }
+        // Is each tree visible from top or bottom?
+        for (int j = 0; j < visibleTrees[0].length; j++) {
+            List<Integer> column = Arrays.stream(rotatedTreeHeights[j]).boxed().toList();
+            for (int i = 0; i < visibleTrees.length; i++) {
+                if (i == 0 || i == visibleTrees.length - 1) {
+                    visibleTrees[i][j] = true;
+                } else {
+                    visibleTrees[i][j] = visibleTrees[i][j]
+                            || (rotatedTreeHeights[j][i] > column.subList(0, i).stream()
+                                .max(Integer::compareTo).orElseThrow())
+                            || (rotatedTreeHeights[j][i] > column.subList(i + 1, treeHeights.length).stream()
+                                .max(Integer::compareTo).orElseThrow());
+                }
+            }
+        }
+        return visibleTrees;
+    }
+
+    private int[][] flipMatrixAlongDiagonal(int[][] matrix) {
+        int[][] rotatedMatrix = new int[matrix[0].length][matrix.length];
+        for (int i = 0; i < rotatedMatrix.length; i++) {
+            for (int j = 0; j < rotatedMatrix[i].length; j++) {
+                rotatedMatrix[i][j] = matrix[j][i];
+            }
+        }
+        return rotatedMatrix;
+    }
+}