It’s Advent of Code time again!
Once again, thousands of programmers around the world are rearranging their lives to solve a series of Christmas-themed puzzles. As I write this, only one puzzle has been released, but this already promises to be an interesting year.
This year, I’ll write a brief post explaining how I solved each puzzle. We start with day 1.
This year’s first puzzle was unexpectedly hard. Part 1 is a simple string manipulation problem. The input consists of a file where each line has lowercase letters and single digits. You just have to read the first and last integers on each line of a file, combine them into a string, read the resulting integer, and sum them.
```{python}
import re
from utils.utils import split_lines
= split_lines("inputs/day1.txt")
raw # No zeroes
= r"[1-9]"
naturals = [re.sub("[a-z]+", "", line) for line in raw]
stripped = sum(int(line[0] + line[-1]) for line in stripped)
part1 print(part1)
```
Part 2 reveals a nasty surprise. You no longer have to just search for digits, but written numbers. one
and 1
now both mean 1
. I created an awkward regular expression to handle this, then confidently submitted my answer. Wrong. I checked my code on the provided test case, and it was correct.
This happens to me several times each year, but rarely on day 1! I racked my mind for a pathological edge case that would break my code. Immediately, I thought of overlapping matches. By the rules, a string like eightwo
should be parsed as 82
, but a conventional pattern would match eight
, move on to w
, and fail to match any more substrings from the string. I needed an overlapping pattern to get the correct answer.
As with most problems, the answer to this one turned out to lie in a purple StackOverflow link. I tweaked my pattern, reran my code, and submitted a new, slightly higher answer. Correct!
```{python}
= ["one", "two", "three", "four", "five", "six", "seven", "eight", "nine"]
nums = len(nums)
n = "|".join(nums + [naturals])
parts = f"(?=({parts}))"
pattern = list(map(str, range(1, 1 + n)))
chars = dict(zip(nums, chars))
keys
= [re.findall(pattern, line) for line in raw]
matches = sum(
part2 int(keys.get(r[0], r[0]) + keys.get(r[-1], r[-1])) if r else 0 for r in matches
)print(part2)
```
All in all, it was a typical Day 7 or 8 puzzle, which made it a nasty surprise as a Day 1 puzzle. So Day 2 should be easy by comparison - right?