Tell you what: if you can help me with something quick, I’ll let you borrow my boat and you can go visit the gardener. I got all these scratchcards as a gift, but I can’t figure out what I’ve won."
Solution in Java
Full source can be found: in GitHub
Part 1
I created a class Card
to represent the scratch cards from the assignment. Each card has a score()
representing the amount of points of the card.
1record Card(int hand, List<Integer> winningNumbers, List<Integer> yourNumbers) {
2 public long score() {
3 return (long) Math.pow(2, numberMatches() - 1);
4 }
5
6 public long numberMatches() {
7 return yourNumbers.stream()
8 .filter(winningNumbers::contains)
9 .count();
10 }
11}
Then creating the algorithm to compute the total amount of winning points can be done by streaming over each card and summing the score
.
1public void part1() {
2 var answer = inputLoader.splitOnNewLine()
3 .map(this::parseCard)
4 .mapToLong(Card::score)
5 .sum();
6 validator.part1(answer);
7}
8
9private Card parseCard(String line) {
10 var cardSplit = line.indexOf(":");
11 var drawSplit = line.indexOf("|");
12 // extract the card number, the winning numbers, and your numbers
13 var cardNumber = Integer.parseInt(line.substring(5, cardSplit).trim());
14
15 var winningNumbers = Arrays.stream(line.substring(cardSplit + 1, drawSplit)
16 .split("\\s"))
17 .filter(number -> !number.isBlank())
18 .map(nr -> Integer.parseInt(nr.trim()))
19 .toList();
20 var myNumbers = Arrays.stream(line.substring(drawSplit + 1).split("\\s"))
21 .filter(number -> !number.isBlank())
22 .map(nr -> Integer.parseInt(nr.trim()))
23 .toList();
24
25 return new Card(
26 cardNumber,
27 winningNumbers,
28 myNumbers);
29}
Part 2
To solve the second part I added a wrapper class CountedCards
to keep track of the amount of winning points per card.
1static class CountedCards {
2 private final Card card;
3 private int count;
4
5 public CountedCards(Card card) {
6 this.card = card;
7 this.count = 1;
8 }
9
10 public void increment(int amount) {
11 this.count += amount;
12 }
13}
Then the algorithm for part 2 can be adjusted to loop over all the cards, ignoring all cards that have no matches. If the card wins then the following X
cards get duplicated (by increasing their count).
The answer can then be found by counting all the cards we have.
1public void part2() {
2 var cardInput = inputLoader.splitOnNewLine()
3 .map(this::parseCard)
4 .map(CountedCards::new)
5 .toList();
6
7 for (var i = 0; i < cardInput.size(); i++) {
8 var card = cardInput.get(i);
9 if (card.card.numberMatches() > 0) {
10 IntStream.range(i + 1, (int) (i + card.card.numberMatches() + 1))
11 .forEach(index -> cardInput.get(index).increment(card.count));
12 }
13 }
14
15 var answer = cardInput.stream()
16 .mapToLong(card -> card.count)
17 .sum();
18 validator.part2(answer);
19}