As is typical for AoC, Day 1 is a trivial challenge.
We’re told we’re performing a sonar sweep of the ocean floor, to map the depth. We need to figure out how quickly the depth increases.
I wrote two different solutions to this problem.
The goal for part 1 is to parse a list of depth figures, and count how many times the depth increases.
The code expects the input file to be stored in a file called
and for this file to be in a folder called
input, which lives in the same place as the script.
I can alternative between sample input and the real input by commenting out the appropriate
We then use the
with open context manager to open the file, read its entire contents,
split the file into separate lines, and store these lines in a list called
Note that each item in depths is a string.
Also of note: I’m using the Python logging module, rather than simply
print() statements. The advantage of the logging module is that we explicitly specify the “level” for any given logging statement.
Any logging statements below the specified threshold will not be suppressed. Here’s a demo of how to use the logging module:
# initialise the Root logger # It will print the time (including milliseconds), the logging level, and the logger name logging.basicConfig(format="%(asctime)s.%(msecs)03d:%(levelname)s:%(name)s:\t%(message)s", datefmt='%H:%M:%S') # now get a named instance of the logger for our application logger = logging.getLogger(__name__) # Set the logging threshold of our logging instance to DEBUG logger.setLevel(logging.DEBUG) # print some stuff logger.debug("We'll see this.") logger.info("And this.") # now set threshold to INFO; everything below INFO is now suppressed logger.setLevel(logging.INFO) # print some more stuff logger.debug("We will NOT see this.") logger.info("But we will see this.")
The output from the above looks like this:
09:08:46.929:DEBUG:__main__: We'll see this. 09:08:46.929:INFO:__main__: And this 09:08:46.929:INFO:__main__: But we will see this.
The next thing to note is that I’m using the
time module, in order to time the overall execution of the program.
I capture the time
t1, then run the
main() function (which runs everything), then capture time
Finally, subtract the time
t1, and report the difference. The output looks like this:
2022-01-05 20:25:01.365:INFO:__main__: There are 2000 measurements 2022-01-05 20:25:01.365:INFO:__main__: Depth increases 1451 times 2022-01-05 20:25:01.365:INFO:__main__: Execution time: 0.0025 seconds
The input data looks something like this:
199 200 208 210 200
This is a simple problem. We want to look at each number, and see if it’s bigger than the previous number.
I want to be able to work with a list of numbers, not a list of strings.
So I convert the list of
str to a list of
int using the
This function takes two parameters: first, a function we want to apply, and secondly,
a collection we want to apply the funtion to. So in this case, we’re applying the
function against every member of
depths. The result is a new list, which is now made up of int values.
Now we iterate through each depth in the list, starting at 1, and compare each depth (i) to the previous depth
depth[i] is greater than
depth[i-1], we increment our counter.
# Part 1 increase_counter = 0 for i in range(1, len(depths)): if depths[i] > depths[i-1]: increase_counter += 1
Now the challenge is a tiny bit harder. Rather than comparing each number to the last, we now need to compare a sliding window of 3 numbers to the previous 3 numbers.
# Part 2 measurements_window_sz = 3 three_measurements = deque(depths[0:measurements_window_sz], maxlen=measurements_window_sz) last_three_sum = sum(three_measurements) increase_counter = 0 for i in range(measurements_window_sz, len(depths)): three_measurements.append(depths[i]) current_three_sum = sum(three_measurements) if current_three_sum > last_three_sum: increase_counter += 1 last_three_sum = current_three_sum logger.info("Depth increases %d times", increase_counter)
For this part, I’m using a
deque (pronounced “deck”); it’s short for “double-ended queue”.
A cool thing about the deque is that we can tell it to only keep the last n items that were added to it.
We do this with
maxlen. So here I create a deque using the first three values from
and tell the deque to only ever keep 3 values.
sum() to add up all the numbers in the
Now we iterate through
depths, starting at the 4th item. With each iteration,
we add the new number to the deque. The deque automatically bins the oldest number,
so it only ever retains the last three numbers. We
sum() the deque again, and compare to the previous sum.
Each time the sum is greater than the previous, we increase the counter.
The output for both parts looks like this:
2022-01-06 23:16:51.650:INFO:__main__: There are 2000 measurements 2022-01-06 23:16:51.650:INFO:__main__: Depth increases 1451 times 2022-01-06 23:16:51.650:INFO:__main__: Depth increases 1395 times 2022-01-06 23:16:51.650:INFO:__main__: Execution time: 0.0450 seconds
Here I’m using Numpy, a data science package that is awesome for manipulating arrays of data. It makes for much shorter code in a problem like this.
If you don’t have Numpy installed, the easiest way to install it is with pip:
py -m pip install numpy
The rest of the setup is the same as before.
import logging import os import time import numpy as np SCRIPT_DIR = os.path.dirname(__file__) INPUT_FILE = "input/input.txt" # INPUT_FILE = "input/sample_input.txt" logging.basicConfig(format="%(asctime)s.%(msecs)03d:%(levelname)s:%(name)s:\t%(message)s", datefmt='%Y-%m-%d %H:%M:%S') logger = logging.getLogger(__name__) logger.setLevel(logging.INFO) def main(): input_file = os.path.join(SCRIPT_DIR, INPUT_FILE) depths = np.loadtxt(input_file) # Part 1 # sum count of where depth n is greater than depth n-1, where n starts at 1, to the last. increase_count = (depths[1:] > depths[:-1]).sum() logger.info("Part 1: Depth increases %d times", increase_count) # Part 2 window_sz = 3 # sum count of where n > n-3, where n starts at 3, to the last. increase_count = (depths[window_sz:] > depths[:-window_sz]).sum() logger.info("Part 2:Depth increases %d times", increase_count)
Rather than reading the file and splitting it, we load the input file directly into a numpy array. And now we can apply our comparison for every member of the array in a single line! The line below means: compare every item in the array, starting at n, with every item starting at n-1 (and ending one before the end), and count how many times the first is greater than the second.
increase_count = (depths[1:] > depths[:-1]).sum()
So that’s it for Part 1!
For Part 2, we can basically use the same solution, once we realise that:
x[n+3] + x[n+2] + x[n+1] > x[n+2] + x[n+1] + x[n] = x[n+3] > x[n]
(This is simply cancelling redundant terms on both sides.)
So now we can use the same Numpy solution, but comparing n+3 to n each time, rather than n+1 to n.
And that’s it!