From fac478b89b59c025cd4deb29ad9596ba11f073be Mon Sep 17 00:00:00 2001
From: Christopher Bohn <bohn@unl.edu>
Date: Wed, 7 Dec 2022 09:37:17 -0600
Subject: [PATCH] Completed Year 2022 Day 7 part 1

---
 2022/README.md                                |  25 +++
 .../java/edu/unl/cse/bohn/year2022/Day7.java  | 151 ++++++++++++++++++
 2 files changed, 176 insertions(+)
 create mode 100644 2022/src/main/java/edu/unl/cse/bohn/year2022/Day7.java

diff --git a/2022/README.md b/2022/README.md
index 79433ac..80c889b 100644
--- a/2022/README.md
+++ b/2022/README.md
@@ -183,5 +183,30 @@ This is straight-forward -- I just need to parameterize the helper method that l
 
 ## Day 7
 
+- [The problem](https://adventofcode.com/2022/day/7)
+- [The solution](src/main/java/edu/unl/cse/bohn/year2022/Day7.java)
+
+### Part 1
+
+The subproblems are
+- Parse the input
+- Based on the responses to commands, determine the size of each directory
+  - The size of a "leaf" directory that has no subdirectories is the sum of the sizes of its files
+  - The size of a non-leaf is the sum of the sizes of its files and the sizes of its directories
+- Determine which directories' size is at most 100000
+
+It is very tempting to create a model of the directory structure, but the concern of course is the memory usage.
+Many of these problems can be mapped to a simpler problem.
+I think the reason I'm so tempted to create a model of the directory structure is because the description of the 
+structure is coming in BFS, but determining the directory sizes would best be done DFS.
+It looks like the puzzle input has fewer than 200 directories. YOLO. So, add to the subproblems:
+- Build a model of the directory tree
+
+### Part 2
+
+...
+
+## Day 8
+
 (coming soon)
 
diff --git a/2022/src/main/java/edu/unl/cse/bohn/year2022/Day7.java b/2022/src/main/java/edu/unl/cse/bohn/year2022/Day7.java
new file mode 100644
index 0000000..52404cb
--- /dev/null
+++ b/2022/src/main/java/edu/unl/cse/bohn/year2022/Day7.java
@@ -0,0 +1,151 @@
+package edu.unl.cse.bohn.year2022;
+
+import edu.unl.cse.bohn.Puzzle;
+
+import java.util.*;
+
+@SuppressWarnings("unused")
+public class Day7 extends Puzzle {
+    public Day7(boolean isProductionReady) {
+        super(isProductionReady);
+        sampleData = """
+                $ cd /
+                $ ls
+                dir a
+                14848514 b.txt
+                8504156 c.dat
+                dir d
+                $ cd a
+                $ ls
+                dir e
+                29116 f
+                2557 g
+                62596 h.lst
+                $ cd e
+                $ ls
+                584 i
+                $ cd ..
+                $ cd ..
+                $ cd d
+                $ ls
+                4060174 j
+                8033020 d.log
+                5626152 d.ext
+                7214296 k""";
+    }
+
+    Directory rootDirectory = null;
+    public static final long THRESHOLD_DIRECTORY_SIZE = 100000;
+
+    @Override
+    public long computePart1(List<String> data) {
+        if (rootDirectory == null) {
+            rootDirectory = buildDirectoryTree(data);
+        }
+        Set<Directory> directoriesBelowThreshold = getSmallDirectories(rootDirectory);
+        return directoriesBelowThreshold.stream().mapToLong(Directory::getSize).sum();
+    }
+
+    @Override
+    public long computePart2(List<String> data) {
+        if (rootDirectory == null) {
+            rootDirectory = buildDirectoryTree(data);
+        }
+        return 0;
+    }
+
+    private Directory buildDirectoryTree(List<String> data) {
+        Directory root = null;
+        Directory workingDirectory = null;
+        for (String datum : data) {
+            if (datum.equals("$ cd /")) {
+                root = new Directory("/", null);
+                workingDirectory = root;
+            } else if (datum.equals("$ cd ..")) {
+                assert workingDirectory != null;
+                workingDirectory = workingDirectory.getParentDirectory();
+            } else if (datum.startsWith("$ cd ")) {
+                assert workingDirectory != null;
+                workingDirectory = workingDirectory.getSubdirectory(datum.substring(5));
+            } else if (datum.equals("$ ls")) {
+                System.out.print("");   // a convenient nop, since there's nothing to do
+            } else if (datum.startsWith("dir ")) {
+                assert workingDirectory != null;
+                workingDirectory.addDirectory(datum.substring(4));
+            } else if (Character.isDigit(datum.charAt(0))) {
+                String[] tokens = datum.strip().split(" ");
+                long fileSize = Long.parseLong(tokens[0]);
+                String fileName = tokens[1];
+                assert workingDirectory != null;
+                workingDirectory.addFile(fileName, fileSize);
+            } else {
+                throw new IllegalArgumentException("Unrecognized line in the data: " + datum);
+            }
+        }
+        return root;
+    }
+
+    private Set<Directory> getSmallDirectories(Directory rootOfSubtree) {
+        Set<Directory> smallDirectories = new HashSet<>();
+        if (rootOfSubtree.getSize() <= THRESHOLD_DIRECTORY_SIZE) {
+            smallDirectories.add(rootOfSubtree);
+        }
+        for (Directory subdirectory : rootOfSubtree.getSubdirectories()) {
+            smallDirectories.addAll(getSmallDirectories(subdirectory));
+        }
+        return smallDirectories;
+    }
+
+    private static class Directory {
+        String name;
+        Directory parentDirectory;
+        Map<String, Directory> subdirectories;
+        Map<String, Long> files;
+        Long cachedSize;    // we're going to cache the directory size to avoid repeatedly diving into subdirectories
+
+        public Directory(String name, Directory parentDirectory) {
+            this.name = name;
+            this.parentDirectory = parentDirectory;
+            subdirectories = new HashMap<>();
+            files = new HashMap<>();
+            cachedSize = null;
+        }
+
+        public void addDirectory(String name) {
+            subdirectories.put(name, new Directory(name, this));
+            cachedSize = null;  // any existing cached size is now invalid
+        }
+
+        public void addFile(String name, long size) {
+            files.put(name, size);
+            cachedSize = null;  // any existing cached size is now invalid
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public Directory getParentDirectory() {
+            return parentDirectory;
+        }
+
+        public Directory getSubdirectory(String name) {
+            return subdirectories.get(name);
+        }
+
+        public Collection<Directory> getSubdirectories() {
+            return subdirectories.values();
+        }
+
+        public long getSize() {
+            if (cachedSize == null) {
+                long size = subdirectories.keySet().stream()
+                        .mapToLong(directoryName -> subdirectories.get(directoryName).getSize())
+                        .sum();
+                size += files.keySet().stream().mapToLong(fileName -> files.get(fileName)).sum();
+                cachedSize = size;
+            }
+            return cachedSize;
+        }
+    }
+}
-- 
GitLab