An Advent Calendar for Nerds
If you aren’t familiar, Advent of Code is a fun take on the traditional advent calendar but instead of a tiny, cheap pieces of chocolate you get increasingly difficult coding puzzles to solve. There’s also usually a narrative around aiding the Elves in their endeavours to ensure Christmas runs smoothly and Santa is able to deliver all the presents without a hitch - they sure do get themselves in a pickle!
This will be my first time truly documenting the event as I go; hopefully that encourages me to keep it up and also keep the code clean! There’s only 12 days this year, Eric has decided to ease his operational burden - I can’t honestly blame him as he puts a lot of effort into this each year. And I can’t honestly say that I’ve ever completed all 25… I tend to run out of steam and have very little time to spare at this time of year!
What Language to Use?
I like to use this event as an excuse to use a programming language that I wouldn’t normally use. It’s fun to find different solutions to the problems than what you may have otherwise done if you stuck to what you already know. The challenges generally only require minimal standard libraries too, so it’s just a great way to play around with a language in a “pure” sort of way.
I’ve used Go and Rust multiple times in the past and I’ve also had a go with Dart. That’s not a lot of variety really now that I look back - but at least each year it tends to end the dry spell of personal projects and kick start something new regardless, so I’m forever grateful for the inspiration to learn.
This year I’ve decided to use Python. It’s not something I’ve used a lot, and can see it being useful to get more intimate with it considering my other projects in machine learning and how prevalent it is in that arena.
Preparation
Although I don’t intend to use AI assistance for the daily challenges (it’s not in the spirit of the event), I will first bootstrap myself an initial project setup that I’ll use to house the challenges. This is something I started to do in previous years by hand (last year, for reasons unknown, I even had a CI/CD pipeline producing each challenge as a container app), however modern tools make this even easier.
There’s usually a fair bit of input parsing to do, I’ll take that as it comes and assess AI usage case by case. It can be another interesting problem to address but it’s also not the real problem that is meant to be solved.
Part 1: The Password
So the Elves have gone corporate; all their additional bureaucracy has meant they don’t have enough time to decorate for christmas. They need me to sort this out by the 12th of December - I’d better get started.
The new security protocols that have been implemented are a pain. I have a safe with a dial numbered from 0 to 99, currently pointing at 50. The password to enter the North Pole base is the number of times that an instruction will leave the dial pointing at exactly 0. Seems simple enough.
Already I think I’m trying to overcomplicate this by thinking about circular buffers and shifting arrays, but really I think it’s much simpler than that.
The input is in the format:
| |
Which can easily translate to - and + for L and R respectively which effectively gives me:
| |
To implement this I’ve made use of a fun little feature of Python called list comprehension:
| |
Now I have a nice straightforward mathematical operation to perform for each instruction: subtract to rotate left, add to rotate right.
The dial is circular in the range of 0-99, which means any overflow outside of that range will carry over from the opposite end of our range - mod is perfect for this as it gives us that remainder that will carry over so we can use that to wrap.
| |
And then putting this all together:
| |
Voila! I just need to increment the counter after each operation if the selected value is 0. I can enter the password to gain access to the North Pole and begin decorating!
Oh, the password is wrong!? I’m sure it’s correct so what on earth is going on…
Part 2: Password Method 0x434C49434B
Of course, how could I forget about password method 0x434C49434B! I need to count how many times the dial clicks on 0 during the entire instruction set.
The logic of applying the rotational operations doesn’t change - it’s just a change to how I count the results. Instead of just checking the current dial position after each operation, I need to calculate if the dial was shifted enough in either direction to click over 0.
There’s a couple of gotchas here:
- starting on
0doesn’t count (it was already clicked when it was initially selected) - a single operation can involve many rotations, i.e. pass 0 multiple times
So I’ll start with the easiest part, full rotations are simply how many times 100 goes into the full operation. It’s the counterpart to what I originally did with mod to work out how to move the dial ignoring full rotations, op / 100.
| |
I’m not sure what the most pythonic solution to this is; but as the result needed to be converted from a float I ended up using the built in int function as well as abs to account for L rotations without subtracting them from my counter.
Edit: I did find a more pythonic way: integer (or floor) division using
//. This removes the need forint(...)conversion. However I found it gave incorrect results in this solution. I believe the way python handles rounding for//division with negative values may be something to do with that.
Next is figuring out if we moved the dial just enough to tick over 0, not forgetting this can be in either direction!
| |
If the operation was positive you’d expect the new dial position to be greater than before, so I check if the new dial position is less than the old position as that indicates that it has wrapped back around and therefore clicked past 0. Likewise for negative operations in the other direction. I only need to consider partial rotations for this as the previous calculation has already taken care of complete rotations.
I still also need to count when the new dial position is exactly 0 as before, but only if the previous condition wasn’t already met - we can’t click 0 twice.
| |
Remember I said there was a couple of gotchas? Yeah, it’s easy to miss even knowing what they are. There is one slight issue remaining with the above code, if the dial starts at position 0 it shouldn’t click for it, a simple fix.
So the end result:
| |
Onwards into the North Pole I go!
As always, full code is available at github.com/lordmoocow/aoc25.
Day 1 done! Python worked well so far for this kind of problem - the list comprehensions are nice touch and built-in functions for common mathematical functions like abs and conversion with int kept things concise. I’m curious to see how it holds up as the puzzles get more complex. Eleven more days to go!