diff --git a/2021/README.md b/2021/README.md index eefe460fe93a733b3721f3c237688dff5673c5ff..10d9f6a1097e9b18802d6ea59fcd3dabdb34b2c5 100644 --- a/2021/README.md +++ b/2021/README.md @@ -9,4 +9,9 @@ get to use Java's new multiline strings. Can we follow instructions? Yes we can. This isn't a particularly challenging problem. Beside using Java's new Records, I'm also using Java's new style of -switch statements. \ No newline at end of file +switch statements. + +## Day 3 + +My CSCE 231 students should be able to do this problem in their sleep -- bit +masks are cool. \ No newline at end of file diff --git a/2021/src/main/java/edu/unl/cse/bohn/year2021/Day3.java b/2021/src/main/java/edu/unl/cse/bohn/year2021/Day3.java new file mode 100644 index 0000000000000000000000000000000000000000..6bb074fc6af69a2cdac4d95dc04c9115b995949a --- /dev/null +++ b/2021/src/main/java/edu/unl/cse/bohn/year2021/Day3.java @@ -0,0 +1,109 @@ +package edu.unl.cse.bohn.year2021; + +import edu.unl.cse.bohn.Puzzle; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.BiPredicate; +import java.util.stream.Collectors; + +@SuppressWarnings("unused") +public class Day3 extends Puzzle { + private List<Integer> diagnosticReports; + private int reportLength; + private int[] ones, zeroes; + + public Day3() { + day = 3; + sampleData = """ + 00100 + 11110 + 10110 + 10111 + 10101 + 01111 + 00111 + 11100 + 10000 + 11001 + 00010 + 01010"""; + isProductionReady = true; + } + + @Override + public int computePart1(List<String> data) { + diagnosticReports = data.stream().map(report -> Integer.parseInt(report, 2)).collect(Collectors.toList()); + reportLength = data.get(0).length(); + ones = new int[]{ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0}; + zeroes = new int[]{ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0}; + for (int report : diagnosticReports) { + for (int bit = 0; bit < reportLength; bit++) { + if ((report & (1 << bit)) == 0) { + zeroes[bit]++; + } else { + ones[bit]++; + } + } + } + int gamma = 0; + int epsilon = 0; + for (int bit = 0; bit < reportLength; bit++) { + if (ones[bit] > zeroes[bit]) { + gamma |= (1 << bit); + } else { + epsilon |= (1 << bit); + } + } + return gamma * epsilon; + } + + private Integer filterReports(int[] onesArray, int[] zeroesArray, BiPredicate<Integer, Integer> predicate) { + List<Integer> workingSet = new ArrayList<>(diagnosticReports); + int bit = reportLength - 1; + int numberOfOnes = onesArray[bit]; + int numberOfZeroes = zeroesArray[bit]; + while (workingSet.size() > 1) { + int mask = 1 << bit; + if (predicate.test(numberOfOnes, numberOfZeroes)) { + workingSet = workingSet.stream() + .filter(report -> (report & mask) != 0) + .collect(Collectors.toList()); + } else { + workingSet = workingSet.stream() + .filter(report -> (report & mask) == 0) + .collect(Collectors.toList()); + } + bit--; + // Need to re-count the ones and zeroes since the old counts are now wrong + if (bit >= 0) { + numberOfOnes = numberOfZeroes = 0; + for (int report : workingSet) { + if ((report & (1 << bit)) == 0) { + numberOfZeroes++; + } else { + numberOfOnes++; + } + } + } + } + return workingSet.get(0); + } + + @Override + public int computePart2(List<String> data) { + Integer oxygenGeneratorRating = filterReports(ones, zeroes, + (numberOfOnes, numberOfZeroes) -> numberOfOnes >= numberOfZeroes); + Integer co2ScrubberRating = filterReports(ones, zeroes, + (numberOfOnes, numberOfZeroes) -> numberOfOnes < numberOfZeroes); + return oxygenGeneratorRating * co2ScrubberRating; + } +}