From 000fb434a7f46a3b744dc20ea89b455364df9983 Mon Sep 17 00:00:00 2001
From: Christopher Bohn <bohn@unl.edu>
Date: Sun, 12 Dec 2021 09:30:36 -0600
Subject: [PATCH] Day 3 complete

---
 2021/README.md                                |   7 +-
 .../java/edu/unl/cse/bohn/year2021/Day3.java  | 109 ++++++++++++++++++
 2 files changed, 115 insertions(+), 1 deletion(-)
 create mode 100644 2021/src/main/java/edu/unl/cse/bohn/year2021/Day3.java

diff --git a/2021/README.md b/2021/README.md
index eefe460..10d9f6a 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 0000000..6bb074f
--- /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;
+    }
+}
-- 
GitLab