From 637d3eeb18bc7884252a0560862734ae8d43daf6 Mon Sep 17 00:00:00 2001 From: Christopher Bohn <bohn@unl.edu> Date: Sun, 12 Dec 2021 13:15:57 -0600 Subject: [PATCH] Day 4 complete --- 2021/README.md | 11 +- .../java/edu/unl/cse/bohn/year2021/Day1.java | 3 +- .../java/edu/unl/cse/bohn/year2021/Day2.java | 3 +- .../java/edu/unl/cse/bohn/year2021/Day3.java | 7 +- .../java/edu/unl/cse/bohn/year2021/Day4.java | 152 ++++++++++++++++++ 5 files changed, 166 insertions(+), 10 deletions(-) create mode 100644 2021/src/main/java/edu/unl/cse/bohn/year2021/Day4.java diff --git a/2021/README.md b/2021/README.md index 10d9f6a..9f4b90a 100644 --- a/2021/README.md +++ b/2021/README.md @@ -1,4 +1,4 @@ -# 2021 Advent of Coding Solutions +# 2021 Advent of Code Solutions ## Day 1 @@ -14,4 +14,11 @@ 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 +masks are cool. + +## Day 4 + +This problem is a little more interesting since there are two stringly types in +the data, and one of them (the Bingo cards) requires a delimiter between +instances. Defining a custom class to represent Bingo cards simplifies the +post-parsing problem. \ No newline at end of file diff --git a/2021/src/main/java/edu/unl/cse/bohn/year2021/Day1.java b/2021/src/main/java/edu/unl/cse/bohn/year2021/Day1.java index 65aaee9..a19ed99 100644 --- a/2021/src/main/java/edu/unl/cse/bohn/year2021/Day1.java +++ b/2021/src/main/java/edu/unl/cse/bohn/year2021/Day1.java @@ -4,7 +4,6 @@ import edu.unl.cse.bohn.Puzzle; import java.util.ArrayList; import java.util.List; -import java.util.stream.Collectors; @SuppressWarnings("unused") public class Day1 extends Puzzle { @@ -40,7 +39,7 @@ public class Day1 extends Puzzle { @Override public int computePart1(List<String> data) { - depths = data.stream().map(Integer::parseInt).collect(Collectors.toList()); + depths = data.stream().map(Integer::parseInt).toList(); return countIncreases(depths); } diff --git a/2021/src/main/java/edu/unl/cse/bohn/year2021/Day2.java b/2021/src/main/java/edu/unl/cse/bohn/year2021/Day2.java index c0a5fc1..43ef3f3 100644 --- a/2021/src/main/java/edu/unl/cse/bohn/year2021/Day2.java +++ b/2021/src/main/java/edu/unl/cse/bohn/year2021/Day2.java @@ -3,7 +3,6 @@ package edu.unl.cse.bohn.year2021; import edu.unl.cse.bohn.Puzzle; import java.util.List; -import java.util.stream.Collectors; @SuppressWarnings("unused") public class Day2 extends Puzzle { @@ -35,7 +34,7 @@ public class Day2 extends Puzzle { .map(instruction -> new NavigationInstruction( instruction.split(" ")[0], Integer.parseInt(instruction.split(" ")[1]))) - .collect(Collectors.toList()); + .toList(); range = 0; depth = 0; for (NavigationInstruction instruction : navigationInstructions) { 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 index 6bb074f..292ead5 100644 --- a/2021/src/main/java/edu/unl/cse/bohn/year2021/Day3.java +++ b/2021/src/main/java/edu/unl/cse/bohn/year2021/Day3.java @@ -5,7 +5,6 @@ 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 { @@ -33,7 +32,7 @@ public class Day3 extends Puzzle { @Override public int computePart1(List<String> data) { - diagnosticReports = data.stream().map(report -> Integer.parseInt(report, 2)).collect(Collectors.toList()); + diagnosticReports = data.stream().map(report -> Integer.parseInt(report, 2)).toList(); reportLength = data.get(0).length(); ones = new int[]{ 0, 0, 0, 0, 0, 0, 0, 0, @@ -76,11 +75,11 @@ public class Day3 extends Puzzle { if (predicate.test(numberOfOnes, numberOfZeroes)) { workingSet = workingSet.stream() .filter(report -> (report & mask) != 0) - .collect(Collectors.toList()); + .toList(); } else { workingSet = workingSet.stream() .filter(report -> (report & mask) == 0) - .collect(Collectors.toList()); + .toList(); } bit--; // Need to re-count the ones and zeroes since the old counts are now wrong diff --git a/2021/src/main/java/edu/unl/cse/bohn/year2021/Day4.java b/2021/src/main/java/edu/unl/cse/bohn/year2021/Day4.java new file mode 100644 index 0000000..27b1ee3 --- /dev/null +++ b/2021/src/main/java/edu/unl/cse/bohn/year2021/Day4.java @@ -0,0 +1,152 @@ +package edu.unl.cse.bohn.year2021; + +import edu.unl.cse.bohn.Puzzle; + +import java.util.Arrays; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Objects; + +@SuppressWarnings("unused") +public class Day4 extends Puzzle { + private List<Integer> numbers; + private List<BingoCard> cards; + + public Day4() { + day = 4; + sampleData = """ + 7,4,9,5,11,17,23,2,0,14,21,24,10,16,13,6,15,25,12,22,18,20,8,19,3,26,1 + + 22 13 17 11 0 + 8 2 23 4 24 + 21 9 14 16 7 + 6 10 3 18 5 + 1 12 20 15 19 + + 3 15 0 2 22 + 9 18 13 17 5 + 19 8 7 25 23 + 20 11 10 24 4 + 14 21 16 12 6 + + 14 21 17 24 4 + 10 16 15 9 19 + 18 8 23 26 20 + 22 11 13 6 5 + 2 0 12 3 7"""; + isProductionReady = true; + } + + private void parseData(List<String> data) { + cards = new LinkedList<>(); + int[][] card = null; + int startingLine = 0; + for (int lineNumber = 0; lineNumber < data.size(); lineNumber++) { + String line = data.get(lineNumber); + if (lineNumber == 0) { + numbers = Arrays.stream(line.split(",")).map(Integer::parseInt).toList(); + } else { + if (line.isBlank()) { + if (lineNumber != 1) { + cards.add(new BingoCard(Objects.requireNonNull(card))); + } + card = null; + } else { + int[] row = new int[0]; + try { + row = Arrays.stream(line.trim().split("\s+")).mapToInt(Integer::parseInt).toArray(); + } catch (NumberFormatException e) { + System.err.println("oops"); + } + if (card == null) { + startingLine = lineNumber; + // We're assuming square Bingo cards + card = new int[row.length][row.length]; + } + card[lineNumber - startingLine] = row; + } + } + } + cards.add(new BingoCard(Objects.requireNonNull(card))); + } + + @Override + public int computePart1(List<String> data) { + parseData(data); + Iterator<Integer> numberIterator = numbers.iterator(); + while (cards.stream().noneMatch(BingoCard::hasBingo) && numberIterator.hasNext()) { + int number = numberIterator.next(); + for (BingoCard card : cards) { + card.markCard(number); + } + } + BingoCard winningCard = cards.stream().filter(BingoCard::hasBingo).findAny().orElseThrow(); + return winningCard.score(); + } + + @Override + public int computePart2(List<String> data) { + Iterator<Integer> numberIterator = numbers.iterator(); + while ((cards.stream().filter(BingoCard::hasBingo).count() < cards.size() - 1) && numberIterator.hasNext()) { + int number = numberIterator.next(); + for (BingoCard card : cards) { + card.markCard(number); + } + } + BingoCard losingCard = cards.stream().filter(card -> !card.hasBingo()).findAny().orElseThrow(); + while (!losingCard.hasBingo() && numberIterator.hasNext()) { + losingCard.markCard(numberIterator.next()); + } + return losingCard.score(); + } + + public static class BingoCard { + private final int[][] numbers; + private final boolean[][] markers; + private int lastNumberCalled = -1; + + public BingoCard(int[][] cardNumbers) { + markers = new boolean[cardNumbers.length][cardNumbers[0].length]; + for (boolean[] row : markers) { + Arrays.fill(row, false); + } + numbers = Arrays.stream(cardNumbers).map(int[]::clone).toArray(int[][]::new); + } + + public void markCard(int number) { + lastNumberCalled = number; + for (int i = 0; i < numbers.length; i++) { + for (int j = 0; j < numbers[i].length; j++) { + if (numbers[i][j] == number) { + markers[i][j] = true; + } + } + } + } + + public boolean hasBingo() { + boolean bingo = false; + // We're going to assume square Bingo cards + for (int i = 0; i < markers.length; i++) { + int rowMarkerCount = 0, columnMarkerCount = 0; + for (int j = 0; j < markers.length; j++) { + rowMarkerCount += markers[i][j] ? 1 : 0; + columnMarkerCount += markers[j][i] ? 1 : 0; + } + bingo = bingo || (rowMarkerCount == markers.length) || (columnMarkerCount == markers.length); + } + return bingo; + } + + public int score() { + int rawScore = 0; + for (int i = 0; i < numbers.length; i++) { + for (int j = 0; j < numbers[i].length; j++) { + rawScore += markers[i][j] ? 0 : numbers[i][j]; + } + } + return rawScore * lastNumberCalled; + } + } +} -- GitLab