Advent of Code: Day 1
Turns out I started blogging just in time for the Advent of Code, an annual series of daily challenges that run from December 1st to December 25th.
These problems remind me a lot of Project Euler, because they are not concerned with the readability or speed of your code, just the output. That means that any goals one wishes to accomplish should be set personally (there is technically a leaderboard, but I find you have to be online very late at night to catch the posts, and it is not worth the stress during the holiday season).
My goal, as you may have guessed from my previous posts, is to complete these challenges in at least Raku and to do so utilizing a functional programming paradigm. Let’s dive into day 1 and see if I can do it!
The Problem
I am not going to copy and paste the explanation of the problem here like I do for the Perl Weekly Challenge, because it is so long, and I want to encourage users to go attempt the challenge themselves! Instead, I will write a summary of what the challenge is and my solution.
Part 1
Given a file full of integers (one per line), find the single pair of integers that adds up to 2020
, then find the product of those two numbers.
Solution
See below for explanation and any implementation-specific comments.
sub MAIN($file) {
say $file.IO.lines # [1]
.combinations(2) # [2]
.grep(-> ($a, $b) { $a + $b == 2020 }) # [3]
.map(-> ($a, $b) { $a * $b }) # [4]
.head; # [5]
}
This runs as such:
$ raku main.raku input.txt
1020036
Explanation
This is fairly straight forward, and I feel Raku reads very cleanly. Basically, we read the entire file into a list (IO.lines
), then find all the pairs in that list, filter those pairs down to where $a + $b == 2020
, then multiply those two numbers together!
Specific Comments
- I’m a sucker for good IO. I feel reading/writing files in languages like Java or Scala is so cumbersome that I try to avoid it at all costs. Languages like Raku were built for text manipulation, so it makes sense that the IO is great, but I just wanted to call out how easy it is to get the lines of a file in a list.
- As I said in my previous post, I see the
combinations
feature coming back a lot in these puzzles. I love that it is built right in. grep
is familiar to most*nix
users, and it is the equivalent of afilter
in more traditional functional languages. In this case, we are filtering down to only pairs that add up to2020
.- At this point this list looks like this:
(($a, $b))
, so we still want to map over the outer list and multiply the pair together. - Since
map
returns a list, we need to grab the first item from that list for pretty printing.
Part 2
Given the same file as before, find the 3 numbers that add up to 2020
and find their product.
Solution
See below for explanation and any implementation-specific comments.
sub MAIN($file, Bool :$p2 = False) { # [1]
say $file.IO.lines
.combinations($p2 ?? 3 !! 2) # [2]
.grep(-> @combo { ([+] @combo) == 2020 }) # [3]
.map(-> @combo { [*] @combo })
.head;
}
This runs as such:
# Part 1
$ raku main.raku input.txt
1020036
# Part 2
$ raku main.raku --p2 input.txt
286977330
Explanation
Since it is basically the same problem, it only makes sense to modify the script we have already written rather than starting from scratch. Basically, everywhere where we hardcoded $a, $b
needs to be generalized to some list. In this case, we added a p2
CLI flag that allows the users to specify if they are doing part 1 or part 2. If they are doing part 2 we find trios instead of pairs, then perform the same “business logic” on that collection.
Specific Comments
- Using the
:$p2
notation says to Raku “create a command line flag called--p2
and assign it to$p2
with a default ofFalse
”. Creating command line interfaces can be kind of a pain in a lot of languages, so I am happy that is built right into the language. - This is the check to see if we are doing part 1 or part 2. Raku’s ternary operator is
condition ?? true !! false
rather than the traditionalcondition ? true : false
. - Since we have to remove all the pair hard-coding, we can generalize it as a list called
@combo
and then just find the sum of the entire combo using the[+]
meta operator. We perform a similar generalization for themap
step.
Final Thoughts
So far so good with my goal to write Raku solutions functionally! Check my GitHub to see any other solutions (and any other languages, if I get around to them). This was a fun little dip into the Advent of Code, and I am looking forward to the rest of the month!