Advent of Code 2022
Advent of Code is a series of small programming-based challenges released through December by Eric Wastl. I will be tackling each challenge as they are released and publishing a post for each.
I will be solving all the challenges in Python, even if a simpler language choice (shell, awk, etc.) is appropriate. I haven’t written a lot of Python in 2022 so this is an exercise in oiling the rusty parts.
Day 1: Calorie Counting
View puzzle page »
Skip to the code?
If you’d like to read my solution first, the code is available on GitHub.
The puzzle input is an unordered array of arrays (herein, subarrays), delimited by an empty line. Each subarray is new line delimited and represents the items in each individual elf’s inventory and their caloric value.
1000 2000 3000 4000 5000 6000 7000 8000 9000 10000
Puzzle 1 is to identify the highest cumulative calories in the elf’s inventories.
The simplest approach to this puzzle would seem to be a loop which consumes the unordered array in sequence, tots up the total of each subarray, and evaluate which total is largest.
Since we are only concerned with which total is largest, we don’t need to retain the sum of each inventory, we only need to compare it to the current highest. Then we can either discard if
total < current_highest or replace it if it’s greater.
We need to read the lines from the first subarray, sum each subsequent line together in a buffer until we hit an empty line, store the total (as the maximum), wipe the buffer, and then repeat. With each reptition, we need to compare the buffered total with the current maximum and, if it’s greater, then replace it. Once we complete the array of subarrays, we return the maximum.
Reading the file and getting an iterable is simple enough. Python’s
with gives us a context manager which handles opening and closing the file object. It also gives us a definable variable to call I/O functions against, like
readlines returns all lines from the file as an iterable which, in this case, will be an list of string elements. On lines with numbers, we’ll get something like
'1234\n' and on the empty lines just
'\n'. We also want a couple of integer variables to store sum output.
With each iteration, we want to check if the line is empty. If it’s not, then add the integer from the line to
cumulative_total. The line, as hinted at above, is a string with a trailing
\n so we can’t just add it to the total. Python is helpful when it comes to type coercion so we can just call the
int function on our line and then use addition assignment to add to our cumulative total.
If the line is empty, then we have come to the end of a subarray and need to reset
cumulative_total to zero. We also want to compare our cumulative total to the highest previously seen total before we reset.
This check is acheivable in many different ways but using built-in functions is always a good idea as these are typically well-optimised. Here we can call on
max to return the highest value in any iterable or sequence of arguments.
Once the loop has concluded, we can print out
highest_total to get the answer to puzzle 1.
Puzzle 2 asks us to identify the cumulative total of the three highest totals. There are a couple of ways to approach this including some potentially complicated but elegant use of
bisect. I’m just gonna sort a list though.
We already have the algorithm to total the subarrays and now we need to retain the values instead of discarding them. Let’s use a list instead of a tuple because we need the object to be mutable.
We start off very similarly, swapping out the
highest_total integer for a list called
array_totals. When we reach the end of a subarray, instead of determining if it’s the new maximum and discarding it, we append it to the list.
Now we have an unsorted list of the totals of each subarray. We could use a combination of
list.pop to get to the top 3 for something a little elegant but I’m just going to sort the list, which will arrange the integer elements in the list from lowest to highest. Once sorted, we can slice the 3 right-most elements of the list and sum them.
We have to sort the list independently of slicing it because the return value of
None, not the list. We know the highest values are right-most in the list so we can slice out the needed values with
-3: and sum them together to get the answer to puzzle 2.
View the code on GitHub.