Skip to content
Snippets Groups Projects
Commit 89a24af4 authored by Christopher Bohn's avatar Christopher Bohn :thinking:
Browse files

Day 12 complete

parent 187c435a
Branches
No related tags found
No related merge requests found
......@@ -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.
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();
}
}
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment