How to solve Advent of Code 2024 - Day 3 with Python
It's the most festive time of the year - Advent of Code! The highlight of every coder's December. πβοΈπ»π
But before we get right into the first day and my solution for this day's puzzle, here is the info on how I approach this whole event (where my code is, what editor I use, etc):
Day 3 Puzzle
On my blog here, I will go through the puzzle and my way of solving it. If you want, you can jump directly to the code solution here:
GitHub .py script
Here is the challenge, if you want to read the full puzzle:
Advent of Code 2024 - Day 3
We visit another location you might know from previous Advent of Code events... Oh... π€― Is it bad that I didn't realize the theme of this year until now? Oops... π
You probably already knew this, but: We are looking for the Chief Historian in all the historical locations of past adventures. ποΈ
Cool, now that I've finally caught up:
Today's location is the North Pole Toboggan Rental Shop from 2020 Day 2. In typical IT worker fashion, we get asked if we can solve their computer problems. They have some corrupted memory, and we get to enjoy the joy of using regex to solve it. π
Part 1
Day 3 gives you a sample of the corrupted input:
# Example input
xmul(2,4)%&mul[3,7]!@^do_not_mul(5,5)+mul(32,64]then(mul(11,8)mul(8,5))
We need to extract the mul(number,number)
parts and ignore all the corrupted ones (like mul(number,number]
).
Solution Approach:
Regex. Regular Expressions. Sadly, I don't see much sense in resisting that. It has to be done and is probably what the puzzle is aiming at.
Key Code Snippets:
My Regex pattern:
pattern = re.compile(r"mul\(\d{1,3},\d{1,3}\)")
Created on regex101.com, which has a database of references and a live debugging mode - though I absolutely support just asking an AI of choice. You should read about roughly what regular expressions are, but personally, I have rarely needed them and in the few cases I do, I just let something generate them for me. π€·ββοΈ
Full Solution for Part 1:
Really, after the regular expression you just extract the numbers and multiply them as instructed:
def solve_part_1(mode: str = "example"):
file_lines = read_input_file(day=3, part=1, mode=mode)
pattern = re.compile(r"mul\(\d{1,3},\d{1,3}\)")
matches = []
for line in file_lines:
matches.extend(pattern.findall(line))
multiplication_sum = 0
for match in matches:
left, right = read_two_ints_from_string(match)
multiplication_sum += left * right
return multiplication_sum
Part 2
Here the challenge is added that instructions like do()
and don't()
decide which multiplications are considered for the final result.
- add search for
do()
anddon't()
to the regular expression - create a current state boolean
mul_active
- change
mul_active
state whenever we encounter ado()
ordon't()
in the pattern matches - only multiply and add to total (as in part 1) when the
mul_active
state is true
Full Solution for Part 2:
def solve_part_2(mode: str = "example"):
file_lines = read_input_file(day=3, part=2, mode=mode)
pattern = re.compile(r"mul\(\d{1,3},\d{1,3}\)|do\(\)|don't\(\)")
matches = []
for line in file_lines:
matches.extend(pattern.findall(line))
multiplication_sum = 0
mul_active = True
for match in matches:
if match == "do()":
mul_active = True
elif match == "don't()":
mul_active = False
else:
left, right = read_two_ints_from_string(match)
if mul_active:
multiplication_sum += left * right
return multiplication_sum
Conclusion
Regular expressions. Probably nice for new coders to be exposed to this concept. Personally I hate using them in code since I find them highly unreadable - like badly written code - and I forget what they were meant for immediately after creating them. π
Writing my own parsing felt even worse though, so here we are. π