From 6e55cf34261bc308750a5da755e1c8ae730235c0 Mon Sep 17 00:00:00 2001
From: Christopher Bohn <bohn@unl.edu>
Date: Mon, 13 Dec 2021 09:17:59 -0600
Subject: [PATCH] Day 8 complete
---
2021/README.md | 30 ++++
.../java/edu/unl/cse/bohn/year2021/Day8.java | 146 ++++++++++++++++++
2 files changed, 176 insertions(+)
create mode 100644 2021/src/main/java/edu/unl/cse/bohn/year2021/Day8.java
diff --git a/2021/README.md b/2021/README.md
index 49a7860..8b64c0b 100644
--- a/2021/README.md
+++ b/2021/README.md
@@ -55,3 +55,33 @@ work.
Part 2 looks a bit more interesting. We *could* go for an
O(num_crabs * max_distance^2) solution, or we could recognize what Euler
recognized: ∑_{i=1}^{n}i = n(n+1)/2.
+
+### Day 8
+
+Seven-segment displays are cool. This has the potential to be a pain. Part 1
+looks okay -- examining only the output patterns, count the number of patterns
+of length 2, 3, 4, or 7.
+
+For part 2, I think set operations are the way to go. For simplicity in this
+explanation, I'm going to use numerals to depict the sets of illuminated segments.
+- Those we know from length alone:
+ - 1 is the only digit of length 2
+ - 7 is the only digit of length 3
+ - 4 is the only digit of length 4
+ - 8 is the only digit of length 7
+- 7\1 gives us the top segment
+- Considering the length-6 digits:
+ - 9 is the only one for which 4\digit = ø, giving us the lower-left segment
+ - 6 is the only one for which 1\digit ≠ ø, giving us the upper-right segment
+ - 0 remains, giving us the central segment
+- (9\7)\4 gives us the bottom segment
+- 1\{upper-right segment} gives us the lower-right segment
+- The upper-left segment remains
+- Considering the length-5 digits:
+ - 3 is the only digit for which digit U 1 = digit
+ - 5 is the only remaining digit for which digit U 4 = 9
+ - 2 remains
+
+It turns out we can ascertain the digits without needing to record the segments.
+That's nifty.
+
diff --git a/2021/src/main/java/edu/unl/cse/bohn/year2021/Day8.java b/2021/src/main/java/edu/unl/cse/bohn/year2021/Day8.java
new file mode 100644
index 0000000..a961402
--- /dev/null
+++ b/2021/src/main/java/edu/unl/cse/bohn/year2021/Day8.java
@@ -0,0 +1,146 @@
+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;
+import java.util.stream.Collectors;
+
+@SuppressWarnings("unused")
+public class Day8 extends Puzzle {
+ @SuppressWarnings("SpellCheckingInspection")
+ public Day8(boolean isProductionReady) {
+ super(isProductionReady);
+// sampleData = "acedgfb cdfbe gcdfa fbcad dab cefabd cdfgeb eafb cagedb ab | cdfeb fcadb cdfeb cdbaf";
+ sampleData = """
+ be cfbegad cbdgef fgaecd cgeb fdcge agebfd fecdb fabcd edb | fdgacbe cefdb cefbgd gcbe
+ edbfga begcd cbg gc gcadebf fbgde acbgfd abcde gfcbed gfec | fcgedb cgb dgebacf gc
+ fgaebd cg bdaec gdafb agbcfd gdcbef bgcad gfac gcb cdgabef | cg cg fdcagb cbg
+ fbegcd cbd adcefb dageb afcb bc aefdc ecdab fgdeca fcdbega | efabcd cedba gadfec cb
+ aecbfdg fbg gf bafeg dbefa fcge gcbea fcaegb dgceab fcbdga | gecf egdcabf bgf bfgea
+ fgeab ca afcebg bdacfeg cfaedg gcfdb baec bfadeg bafgc acf | gebdcfa ecba ca fadegcb
+ dbcfg fgd bdegcaf fgec aegbdf ecdfab fbedc dacgb gdcebf gf | cefg dcbef fcge gbcadfe
+ bdfegc cbegaf gecbf dfcage bdacg ed bedf ced adcbefg gebcd | ed bcgafe cdgba cbgef
+ egadfb cdbfeg cegd fecab cgb gbdefca cg fgcdab egfdb bfceg | gbdfcae bgc cg cgb
+ gcafb gcf dcaebfg ecagb gf abcdeg gaef cafbge fdbac fegbdc | fgae cfgab fg bagce""";
+ }
+
+ @Override
+ public long computePart1(List<String> data) {
+ Set<Integer> suitableDigitLengths = Set.of(2, 3, 4, 7);
+ long numberOfMatchingDigits = 0;
+ for (String line : data) {
+ String[] halves = line.split(" \\| ");
+ for (String digit : halves[1].split(" ")) {
+ if (suitableDigitLengths.contains(digit.length())) {
+ numberOfMatchingDigits++;
+ }
+ }
+ }
+ return numberOfMatchingDigits;
+ }
+
+ private String sortSegments(String segments) {
+ return segments.chars()
+ .sorted()
+ .collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append)
+ .toString();
+ }
+
+ private Set<Character> stringToSet(String segments) {
+ return new HashSet<>(segments.chars().mapToObj(c -> (char)c).toList());
+ }
+
+ @Override
+ public long computePart2(List<String> data) {
+ long sumOfValues = 0L;
+ for(String line:data) {
+ Map<String, Long> numerals = new HashMap<>();
+ String[] halves = line.split(" \\| ");
+ for (String segmentPattern : halves[0].split(" ")) {
+ numerals.put(sortSegments(segmentPattern), null);
+ }
+ Set<String> segmentPatterns = numerals.keySet();
+
+ // Identify which patterns correspond to which digits. See the README for the reasoning.
+ // First the patterns whose length are unique
+ String one = segmentPatterns.stream().filter(pattern -> pattern.length() == 2).findAny().orElseThrow();
+ numerals.put(one, 1L);
+ String seven = segmentPatterns.stream().filter(pattern -> pattern.length() == 3).findAny().orElseThrow();
+ numerals.put(seven, 7L);
+ String four = segmentPatterns.stream().filter(pattern -> pattern.length() == 4).findAny().orElseThrow();
+ numerals.put(four, 4L);
+ String eight = segmentPatterns.stream().filter(pattern -> pattern.length() == 7).findAny().orElseThrow();
+ numerals.put(eight, 8L);
+ // Then the patterns of length 6
+ Set<String> candidates = segmentPatterns.stream()
+ .filter(pattern -> pattern.length() == 6)
+ .collect(Collectors.toSet());
+ String nine = "";
+ for (String candidate : candidates) {
+ Set<Character> candidateSet = stringToSet(candidate);
+ Set<Character> copyOfFour = new HashSet<>(stringToSet(four));
+ copyOfFour.removeAll(candidateSet);
+ if (copyOfFour.isEmpty()) {
+ nine = candidate;
+ numerals.put(nine, 9L);
+ }
+ }
+ candidates.remove(nine);
+ String six = "";
+ for (String candidate : candidates) {
+ Set<Character> candidateSet = stringToSet(candidate);
+ Set<Character> copyOfOne = new HashSet<>(stringToSet(one));
+ copyOfOne.removeAll(candidateSet);
+ if (!copyOfOne.isEmpty()) {
+ six = candidate;
+ numerals.put(six, 6L);
+ }
+ }
+ candidates.remove(six);
+ String zero = candidates.stream().findAny().orElseThrow();
+ numerals.put(zero, 0L);
+ // The remaining patterns are all length 5
+ candidates = segmentPatterns.stream()
+ .filter(pattern -> pattern.length() == 5)
+ .collect(Collectors.toSet());
+ String three = "";
+ for (String candidate : candidates) {
+ Set<Character> candidateSet = stringToSet(candidate);
+ Set<Character> copyOfCandidateSet = new HashSet<>(candidateSet);
+ candidateSet.addAll(stringToSet(one));
+ if (candidateSet.equals(copyOfCandidateSet)) {
+ three = candidate;
+ numerals.put(three, 3L);
+ }
+ }
+ candidates.remove(three);
+ String five = "";
+ for (String candidate : candidates) {
+ Set<Character> candidateSet = stringToSet(candidate);
+ candidateSet.addAll(stringToSet(four));
+ if (candidateSet.equals(stringToSet(nine))) {
+ five = candidate;
+ numerals.put(five, 5L);
+ }
+ }
+ candidates.remove(five);
+ String two = candidates.stream().findAny().orElseThrow();
+ numerals.put(two, 2L);
+
+ // Now determine the output value
+ long lineValue = 0L;
+ long magnitude = 1000L;
+ for (String displayDigit : halves[1].split(" ")) {
+ long digitValue = numerals.get(sortSegments(displayDigit));
+ lineValue += magnitude * digitValue;
+ magnitude /= 10;
+ }
+ sumOfValues += lineValue;
+ }
+ return sumOfValues;
+ }
+}
--
GitLab