Yesterday’s quest to find the Chief Historian didn’t succeed so it’s time to continue the search. While we’re looking for him at the Red-Nosed Reindeer nuclear fusion/fission plant, engineers come to seek our aid.
We need to analyse reports from the Red-Nosed reactor. Our input data is a list of reports, one report per line:
Reading input
Each line contains numbers separated by a whitespace. We’ll split the lines by that whitespace and cast each number into an integer:
If you’re new to Python, you have witnessed a list comprehension. These are like little for loops in one line. But even more than just a for loop, they return a new list.
This list comprehension above achieves the same result as this for loop:
This form of list comprehension is equivalent to a map
function (which also exists in Python but pythonistas tend to prefer list comprehensions).
You can also do an equivalent of a filter
:
List comprehensions are a somewhat divisive topic, especially with people who move to Python from an another language. I like them, especially for simple cases where it’s either a map
, filter
or a combination of the two. Once you start to do more or combine multiple for loops into one comprehension, the readability often suffers.
Part 1: Are these reports good?
The engineers explain to us that a report is safe if it satisfies two rules:
- The levels are either all increasing or all decreasing.
- Any two adjacent levels differ by at least one and at most three.
To check for the first one, I wrote a Santa’s little helper function to check the sign of a difference of two numbers:
Then I wrote a function to check if an individual report is safe:
Here we use the wonderful method itertools.pairwise which turns a list into pairs of two.
To show how that method works, let’s take a look at an easier example:
It’s a nice way to traverse a list two items at a time without having to worry about indices and when to stop and so on. I’ve been using it quite often in Advent of Code - so much that it deserved a spot on my 2022 Prep for Advent of Code blog post.
To provide an answer to the part 1, we need to count how many reports are safe:
We could have used a list comprehension’s sibling generator comprehension here:
But in this case I chose the earlier because I find it a tad bit easier to follow.
⭐️ Third star achieved!
Part 2: Problem Dampener
The reactor is more fault-tolerant than we first thought. It’s okay with a single level (number) in a report being incorrect.
So we need to calculate the amount of safe reports again but this time take into account the problem dampener feature.
To do that, we wrap the safe check into a new function. If the report is safe right out of the box, we return True
like we did in part 1.
If it is unsafe, we need to check if there is a safe configuration by ignoring one level at a time until a safe one is found.
is called list slicing. Square brackets ([ ]
) after a list can do quite a lot in Python.
- You can get a value by index with
report[index]
- You can get everything until an index (not including the indexed item) with
report[:index]
- You can get everything from index onward with
report[index:]
- You can get everything between two indices with
report[start:end]
- You can get every nth item with
report[::2]
To calculate the final amount of safe reports for part 2, we do the same as in part 1 but with our new dampener solution:
⭐️ Four out of four!