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