From 89a24af457d301da2a9274b97e51b8c6684356f7 Mon Sep 17 00:00:00 2001
From: Christopher Bohn <bohn@unl.edu>
Date: Wed, 15 Dec 2021 08:51:40 -0600
Subject: [PATCH] Day 12 complete
---
2021/README.md | 14 ++
.../java/edu/unl/cse/bohn/year2021/Day12.java | 167 ++++++++++++++++++
2 files changed, 181 insertions(+)
create mode 100644 2021/src/main/java/edu/unl/cse/bohn/year2021/Day12.java
diff --git a/2021/README.md b/2021/README.md
index 969c13e..6669c6d 100644
--- a/2021/README.md
+++ b/2021/README.md
@@ -134,3 +134,17 @@ 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.
+
+# Day 12
+
+For part 1, I hope there aren't any big caves directly connected to other big
+caves, because that would introduce a cycle. If we only wanted the shortest
+path then I could use a BFS, and cycles wouldn't be a problem. But we want all
+paths, and I think cycle detection is probably beyond the scope of Advent of
+Code, especially before the halfway point. For part 1 I'm just going to build
+the path tree. I have a sneaking suspicion I really should build the cave graph
+for part 2, but we shall see...
+
+Good news! I was wrong about needing to build the cave graph for part 2. I just
+need to specialize my `Cave` inner class to change the rules for adding
+children.
diff --git a/2021/src/main/java/edu/unl/cse/bohn/year2021/Day12.java b/2021/src/main/java/edu/unl/cse/bohn/year2021/Day12.java
new file mode 100644
index 0000000..5fb1915
--- /dev/null
+++ b/2021/src/main/java/edu/unl/cse/bohn/year2021/Day12.java
@@ -0,0 +1,167 @@
+package edu.unl.cse.bohn.year2021;
+
+import edu.unl.cse.bohn.Puzzle;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+@SuppressWarnings("unused")
+public class Day12 extends Puzzle {
+ private Map<String, Set<String>> adjacencyMap;
+
+ @SuppressWarnings("CommentedOutCode")
+ public Day12(boolean isProductionReady) {
+ super(isProductionReady);
+ sampleData = """
+ start-A
+ start-b
+ A-c
+ A-b
+ b-d
+ A-end
+ b-end""";
+// sampleData = """
+// dc-end
+// HN-start
+// start-kj
+// dc-start
+// dc-HN
+// LN-dc
+// HN-end
+// kj-sa
+// kj-HN
+// kj-dc""";
+// sampleData = """
+// fs-end
+// he-DX
+// fs-he
+// start-DX
+// pj-DX
+// end-zg
+// zg-sl
+// zg-pj
+// pj-he
+// RW-he
+// fs-DX
+// pj-RW
+// zg-RW
+// start-pj
+// he-WI
+// zg-he
+// pj-fs
+// start-RW""";
+ }
+
+ private void buildMap(List<String> data) {
+ adjacencyMap = new HashMap<>();
+ for (String line : data) {
+ String[] termini = line.split("-");
+ if (!adjacencyMap.containsKey(termini[0])) {
+ adjacencyMap.put(termini[0], new HashSet<>());
+ }
+ adjacencyMap.get(termini[0]).add(termini[1]);
+ if (!adjacencyMap.containsKey(termini[1])) {
+ adjacencyMap.put(termini[1], new HashSet<>());
+ }
+ adjacencyMap.get(termini[1]).add(termini[0]);
+ }
+ adjacencyMap = Map.copyOf(adjacencyMap); // make it unmodifiable
+ }
+
+ @Override
+ public long computePart1(List<String> data) {
+ buildMap(data);
+ Cave root = new Cave("start", null, new HashSet<>(), adjacencyMap);
+// for (Cave cave : Cave.exits) {
+// System.out.println(cave);
+// }
+ return Cave.exits.size();
+ }
+
+ @Override
+ public long computePart2(List<String> data) {
+ RevisitableCave root = new RevisitableCave("start", null, new HashSet<>(), adjacencyMap, false);
+ return RevisitableCave.exits.size();
+ }
+
+ private static class Cave {
+ public static final Set<Cave> exits = new HashSet<>();
+
+ protected String caveName;
+ protected Cave parent;
+ protected Set<String> visitedSmallCaves;
+ protected List<? extends Cave> children;
+
+ private Cave() {
+ // this constructor exists to keep Java happy wrt the subclass whose public constructor has different
+ // parameters than the parent class's constructor
+ caveName = null;
+ parent = null;
+ visitedSmallCaves = null;
+ children = null;
+ }
+
+ public Cave(String caveName, Cave parent, Set<String> visitedSmallCaves,
+ Map<String, Set<String>> adjacencyMap) {
+ this.caveName = caveName;
+ this.parent = parent;
+ this.visitedSmallCaves = visitedSmallCaves;
+ if (caveName.equals(caveName.toLowerCase())) {
+ this.visitedSmallCaves.add(caveName);
+ }
+ if (caveName.equals("end")) {
+ exits.add(this);
+ children = null;
+ } else {
+ children = adjacencyMap.get(caveName).stream()
+ .filter(neighbor -> !visitedSmallCaves.contains(neighbor))
+ .map(neighbor -> new Cave(neighbor, this, new HashSet<>(visitedSmallCaves), adjacencyMap))
+ .toList();
+ }
+ }
+
+ public String toString() {
+ StringBuilder stringBuilder = new StringBuilder(caveName);
+ if (!caveName.equals("start")) {
+ stringBuilder.insert(0, parent.toString() + " - ");
+ }
+ return stringBuilder.toString();
+ }
+ }
+
+ private static class RevisitableCave extends Cave {
+ public static final Set<Cave> exits = new HashSet<>();
+
+ public RevisitableCave(String caveName, Cave parent, Set<String> visitedSmallCaves,
+ Map<String, Set<String>> adjacencyMap, boolean haveVisitedSmallCaveTwice) {
+ this.caveName = caveName;
+ this.parent = parent;
+ this.visitedSmallCaves = visitedSmallCaves;
+ final boolean updatedHaveVisitedSmallCaveTwice;
+ if (caveName.equals(caveName.toLowerCase())) {
+ if (visitedSmallCaves.contains(caveName)) {
+ updatedHaveVisitedSmallCaveTwice = true;
+ } else {
+ updatedHaveVisitedSmallCaveTwice = haveVisitedSmallCaveTwice;
+ }
+ this.visitedSmallCaves.add(caveName);
+ } else {
+ updatedHaveVisitedSmallCaveTwice = haveVisitedSmallCaveTwice;
+ }
+ if (caveName.equals("end")) {
+ exits.add(this);
+ children = null;
+ } else {
+ children = adjacencyMap.get(caveName).stream()
+ .filter(neighbor -> !neighbor.equals("start"))
+ .filter(neighbor -> !(updatedHaveVisitedSmallCaveTwice && visitedSmallCaves.contains(neighbor)))
+ .map(neighbor -> new RevisitableCave(neighbor, this,
+ new HashSet<>(visitedSmallCaves), adjacencyMap, updatedHaveVisitedSmallCaveTwice))
+ .toList();
+ }
+ }
+ }
+}
--
GitLab