In order to participate in these challenges, you will need to register with the Advent of Code site, so that you can retrieve your own datasets for the puzzles. When you get an answer, you will need to submit it to the Advent of Code site to determine whether the answer is correct. I have already completed the 2025 set using python, so I will know when my SPL generates the correct result.
Each day's puzzle is split into two parts; part one is usually easier than part two, and you cannot normally reach part two until you have successfully completed part one. Day 1 is about opening a safe using a dial. Please visit the website for full details of the puzzle.
This article contains spoilers!
In fact, the whole article is a spoiler as it contains solutions to the puzzle. If you are trying to solve the puzzle yourself and just want some pointers to get you started, stop reading when you have enough, and return if you get stuck again, or just want to compare your solution to mine!
As with all the Advent of Code puzzles, the description of the problem is aided by some example input; in this case, the input is a series of left and right turns of the dial. The aim of the puzzle is to determine, for your own dataset, how many times the dial stops at zero.
The first thing to do is initialise your data. One way to do this is to save it to a csv file and use inputlookup to load it. Alternative, you could just use makeresults (as I have done here), and set a field to the data:
| makeresults
| fields - _time
| eval _raw="R50
L68
L30
R48
L5
R60
L55
L1
L99
R14
L82"
Since the puzzle says that the dial starts at 50, I have included "R50" as the first line of the input data.
The next step is to break the data up into separate events (if you have loaded from a csv file, your data may already be in separate events.)
| rex max_match=0 "(?<line>\S+)"
| fields - _raw
| mvexpand line
Using our good friend rex, parse the data to get the direction and number of clicks. Use the direction to convert the count to a signed integer.
| rex field=line "(?<direction>\w)(?<count>\d+)"
| eval clicks=count%100
| eval clicks=clicks*if(direction="L",-1,1)
| table direction count clicks
Note the use of modulus 100. This is not strictly speaking necessary as we use modulus later on, and while the sample data does not have any large number, it is almost certain that your real data will (this is often the nature of the Advent of Code puzzles).
We now have a set of movements for the dial which we can run through looking for zeroes. The simplest way to do this is with streamstats to get a rolling sum of all the values, and count how many times we end up with zero.
| streamstats sum(clicks) as dial
| eval dial=dial%100
| stats sum(eval(if(dial=0,1,0))) as zeroes
This time the modulus 100 is required.
The second part of the puzzle requires that we count the number of times the dial points at zero, not just stopping there. This sounds simple; if the movement is more than 100 clicks, include the number of 100 clicks there are in the movement as well.
Using the same approach as for Part One, this time find the number of multiples of 100 there are in the movement, before taking the modulus.
| rex max_match=0 "(?<line>\S+)"
| fields - _raw
| mvexpand line
| rex field=line "(?<direction>\w)(?<count>\d+)"
| eval spins=floor(abs(count)/100)
| eval clicks=count%100
| eval clicks=clicks*if(direction="L",-1,1)
| streamstats sum(clicks) as dial
Now we can add the number of spins to the number of times the dial is pointing to zero.
| eval dial=dial%100
| eval zeroes=if(dial=0, 1, 0)+spins
| stats sum(zeroes) as allzeroes
Well, not quite. It turns out that, the total comes out as being too low. So, what is going on? Which ones are we missing?
When the dial turns passes zero, we are not currently counting it. So, how do we pick up on this. Well, if we are turning the dial to the right and the number we end up on is less than the previous position, we must have passed through zero (in a clockwise direction). Similarly, if we are turning the dial to the left and the number we end up on is greater than the previous position, we must have passed through zero (in a counter-clockwise direction).
| streamstats sum(clicks) as dial
| eval dial=dial%100
| streamstats window=1 current=f last(dial) as previous
| eval zeroes=if(dial=0, 1, if(direction="L", if(dial > previous, 1, 0), if(dial < previous, 1, 0)))
| eval zeroes=zeroes+spins
| stats sum(zeroes) as allzeroes
Now we are getting a number that is too high! We must be double counting some situations. Consider, when turning the dial to the left, when the previous position is pointing at zero. The previous position has been counted as a zero (from the first part of the if), and the new position will be counted as having passed through zero - which it did not! To fix this, we can tweak the if function to take the previous position into account.
| streamstats window=1 current=f last(dial) as previous
| eval zeroes=if(dial=0, 1, if(direction="L", if(dial > previous and previous != 0, 1, 0), if(dial < previous, 1, 0)))
| eval zeroes=zeroes+spins
| stats sum(zeroes) as allzeroes
The devil is in the detail! A common theme to the Advent of Code puzzles is that, hidden in your real data, there are gotchas which are put there to try and trip you up.
Have questions or thoughts? Comment on this article or in Slack #puzzles channel. Whichever you prefer.
Want first dibs on fresh content? Follow this quick guide to subscribe and get updates instantly.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.