Advent of Code 2025 Day 1: Secret Entrance
from advent import parse, atom, Callable
input: list[tuple[str, int]] = [(line[0], int(line[1:])) for line in parse(1, atom)]
part1 = lambda dial, _: 1 if dial == 0 else 0
part2 = lambda _, clicks: clicks
def solve(part: Callable[[int, int], int], count: int = 0, dial: int = 50) -> int:
for dir, n in input:
clicks, dial = divmod(dial + (n if dir == 'R' else -n), 100)
count += part(dial, abs(clicks))
return count
assert solve(part1) == 1154
assert solve(part2) == 6819
Input Parsing
The input for Day 1 consists of rotation instructions for a safe. The first character is either L or R, and is followed
by an integer indicating how many clicks to rotate. For example: R37 or L872. parse(1, atom)
provides the following:
----------------------------------------------------------------------------------------------------
app/day01.txt ➜ 19048 chars, 4629 lines; first 3 lines:
----------------------------------------------------------------------------------------------------
R49
R27
R22
----------------------------------------------------------------------------------------------------
parse(1) ➜ 4629 entries:
----------------------------------------------------------------------------------------------------
('R49', 'R27', 'R22', 'R5', 'R6', 'R10', 'L13', 'L37', 'R17', 'R6', 'L ... L20', 'R7', 'L34', 'L15')
----------------------------------------------------------------------------------------------------
We'll use a list comprehension to create a list of 2-tuples containing a direction and the number of clicks,
such as [('R', 49), ('L', 13), ...]
[(line[0], int(line[1:])) for line in parse(1, atom)]
Solution
The key concept for today's puzzle is modular arithmetic. There are 100 numbers on the dial of the safe, so we'll be doing modulo 100 arithmetic to wrap around from 99 to 0 when rotating right, or 0 to 99 when rotating left. We'll also need the remainder to know how many times we've wrapped around.
The divmod built-in Python function allows us to compute a // b and a % b with a single call, and we
need both of those results.
For each of the rotation instructions, we add/subtract the indicated number from the current dial
position, and call divmod(<result>, 100). The absolute value of the first result (quotient) is the number of clicks after which the
position was at zero, and the second result (position mod 100) is the new current position of the dial.
For part 1, we only care about whether the new position is zero. For part 2, we want the number of
clicks after which zero was displayed. These two different needs are encapsulated in the two
lambda functions, part1 and part2.
The Fine Print
I haven't told you the full story though! There is a subtlety that needs to be handled to have an accurate solution. Neither my sample input, nor the real input, exercised these edge cases, so I took advantage of that fact to simplify my code. However, for left rotations, if our starting position is zero, and our ending position is non-zero, we need to add 1 to our quotient, and if our starting position is non-zero, and our ending position is zero, we need to subtract 1 from our quotient. Here is the code to handle these edge cases
