Henry Heim

Advent of Code 2025 - Python - Day 1

I'd like to run through Advent of Code 2025 using Python. I find Python very easy to use for short programs like these, and I think it'll be good at getting out of my way and letting me solve the problem.

In the future, I'd like to go through these same problems with more adventurous languages. But first, Python.

Problem - Part 1

We have a safe with a dial and numbers 0-99. The puzzle input is a sequence of Left (towards lower numbers) or Right (towards higher numbers) rotations, along with a distance indicating how many values the dial should be rotated in that direction. The dial wraps from 99 to 0. Each value is called a "click".

For Part 1, the password is the number of times the dial is left pointing at 0 after any rotation in the sequence. That is, after each rotation instruction in the puzzle input, we should check if the dial is pointing to zero and if so increment a counter. The final value of the counter, after exhausting the puzzle input, is the password.

The sample puzzle input is:

L68
L30
R48
L5
R60
L55
L1
L99
R14
L82

Solution - Part 1

This problem seems very straightforward. I've always found Python great at text parsing, so we can jump right in:

# Read input file
lines = []
with open ("data/1.long", "r") as f:
    lines = [line.strip() for line in f.readlines()]

# Parse command from each line
commands = []
for line in lines:
    dir = line[0]
    val = int(line[1:])
    commands.append((dir, val))

# Process
sum = 50
zeros = 0
for command in commands:
    dir, val = command
    if dir == "R":
        sum += val
    elif dir == "L":
        sum -= val

    # Wrap
    while sum < 0 or sum > 99:
        if sum < 0:
            sum += 100
        elif sum > 99:
            sum -= 100

    # Detect if we ended on zero
    if sum == 0:
        zeros += 1

# Print answer
print(f"Result: {zeros}")

Very straightforward. The only tricky thing here is to recognize that you need to add/subtract 100 from the sum to rollover, not 99, because there are 100 total possible values (0-99). This code gives us the correct answer.

This snippet also shows why I thnk Python is so great for text parsing - you can do simple stripping operations in a comprehension at the same time you read the data in the first place. After the initial comprehension, lines has value:

['L68', 'L30', 'R48', 'L5', 'R60', 'L55', 'L1', 'L99', 'R14', 'L82']

I also thought the code would be simpler if I had separate, specific values for the direction and the value. The first loop (for line in lines) separates out the two and puts them in a new list made of tuples, which looks like:

[('L', 68), ('L', 30), ('R', 48), ('L', 5), ('R', 60), ('L', 55), ('L', 1), ('L', 99), ('R', 14), ('L', 82)]

This helpfully has the value already as an integer and the direction is clear (and I don't have to string index again later). I'm sure there's a fancy way to get this all done in the initial list comprehension, when readin the file initially, but as I can't quickly think how to do it I'm going to assume that it would be hard for me to understand that code later. KISS.

Problem - Part 2

Unfortunately, this isn't the code to the safe. Instead of tracking how many times our dial ended by pointing at zero, we instead need to track how many times our dial pointed at zero in total, including when it was just passing by on its way to a different value.

The approach I used in Part 1 doesn't seem like it'll fit very well - while I could just try to track how many times I rollover, it seems like there are a lot of edge cases where I'm ending on zero or starting on zero or both. It seems much easier to leverage the fact that computers are fast and just brute-force the counting:

def add(sum, clicks, zeros):
    for _ in range(clicks):
        sum += 1
        if sum > 99:
            sum = 0

        if sum == 0:
            zeros += 1

    return sum, zeros

def sub(sum, clicks, zeros):
    for _ in range(clicks):
        sum -= 1
        if sum < 0:
            sum = 99

        if sum == 0:
            zeros += 1

    return sum, zeros

# Read input file
lines = []
with open ("data/1.long", "r") as f:
    lines = [line.strip() for line in f.readlines()]

# Parse command from each line
commands = []
for line in lines:
    dir = line[0]
    val = int(line[1:])
    commands.append((dir, val))

# Process
sum = 50
zeros = 0
for command in commands:
    dir, val = command
    if dir == "R":
        sum, zeros = add(sum, val, zeros)
    elif dir == "L":
        sum, zeros = sub(sum, val, zeros)

# Print answer
print(f"Result: {zeros}")

This code is nearly the same as before, but instead of simply adding val, we call an add() function which handles rollover logic for us. By tracking the dial's movement one click at a time, we can be sure to do rollover in a way that matches how you actually turn a dial (you don't turn it to -120 then mentally convert that to 31, or whatever, the dial just goes from 0 to 99). This removes the whole class of issues caused by inputs that have vals of more than 100.

A tricky bit here is to check for rollover and increment the zero counter separately. When adding these are the same (going from 99 to 0 is wrapping and is also hitting zero), but when subtracting these are not (first you get to zero, and one click later you wrap).

I could have made a single addition function and passed positve or negative val, but this approach seemed easier.

This code gives us the correct answer to Part 2. AoC typically starts off trivial and gets quite difficult towards the end. I'm looking forward to seeing what comes next!