Advent of Code 2023 - Day 4 ¶
I'll just stop adding "Part 1" to the title...
This puzzle was way too easy, compared to yesterday's. Again, I won't do part 2 due to lack of time - that's probably going to be the same for the following posts, too - but part 1 was trivial.
In this puzzle, we're asked to find the size of the set intersection of two lists of numbers. After that, we need to compute the following:
The first match makes the card worth one point and each match after the first doubles the point value of that card.
According to the explanation, the result for the example should be 13. Here's the solution:
1: (defconst example-input 2: "Card 1: 41 48 83 86 17 | 83 86 6 31 17 9 48 53 3: Card 2: 13 32 20 16 61 | 61 30 68 82 17 32 24 19 4: Card 3: 1 21 53 59 44 | 69 82 63 72 16 21 14 1 5: Card 4: 41 92 73 84 69 | 59 84 76 51 58 5 54 83 6: Card 5: 87 83 26 28 32 | 88 30 70 12 93 22 82 36 7: Card 6: 31 18 13 56 72 | 74 77 10 23 35 67 36 11") 8: 9: (cl-defun parse-input (data) 10: (cl-loop for card in (split-string data "\n") 11: for (name scores) = (split-string card ": +") 12: for nums = (split-string scores " +") 13: collect (-map #'read nums))) 14: 15: (let ((scores (--map (-partition-by #'numberp it) (parse-input example-input)))) 16: (cl-loop for (winning _ haves) in scores 17: for win-expt = (1- (length (-intersection winning haves))) 18: when (>= win-expt 0) ; b/c `expt' works with negative numbers 19: sum (expt 2 win-expt)))
13
It works! 🙂
Interesting points:
- We use
read
instead ofstring-to-number
on line 13 because the separator (|
) is a valid symbol, so we don't need to special-case it. - The multiplication described in the task can be written as exponentiation
with base 2, with exponent being one less than the number of winning numbers
we have. However,
expt
works with negative exponents properly (instead of throwing an error or returning 0), so we need to filter out cases where none of the numbers we have are "winning". Otherwise, we'd get(expt 2 -1) == 0.5
, which would be accumulated and we'd get the wrong result. - As previously,
split-string
is the workhorse for parsing, andcl-loop
along with a few functions fromdash
(esp.-partition-by
and obviously-intersection
) came in handy.
Part 2 is a little more interesting, but unfortunately, I have to go to work, so maybe next time…