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