kennysliding

December 1, 2025 Transmission_ID: aoc-2025

Advent of Code 2025: Day 1

rust advent of code ai

Problem

Read it here

tl;dr — A safe has a circular dial with numbers 0-99, starting at 50. You’re given a sequence of rotations:

InstructionMeaning
L<n>Rotate left (toward lower numbers) by n
R<n>Rotate right (toward higher numbers) by n

The dial wraps around (0 and 99 are adjacent).

Part 1: Count how many times the dial lands on 0 after each rotation.

Part 2: Count how many times the dial passes through or lands on 0 during the entire rotation process — every tick counts!


My Solution (Handcrafted)

Part 1

Part 1 was straightforward — just track the current position and check if it lands on 0 after each instruction:

pub fn solve_1(filename: &str) -> i32 {
    let mut current_position = 50;
    let mut count = 0;
    for line in Self::read_input(filename) {
        let (direction, number) = line.split_at(1);
        let number = number.parse::<i32>().unwrap();
        match direction {
            "L" => current_position = (current_position - number + 100) % 100,
            "R" => current_position = (current_position + number) % 100,
            _ => panic!("Invalid direction: {}", direction.to_string()),
        }
        if current_position == 0 {
            count += 1;
        }
    }
    count
}

The key insight is handling the modular arithmetic correctly — for left rotations, we add 100 before taking the modulo to avoid negative numbers.

Part 2

Part 2 required counting every time we pass through zero, not just when we land on it. My approach uses math instead of simulation:

pub fn solve_2(filename: &str) -> i32 {
    let mut current_position = 50;
    let mut count = 0 as i32;
    for line in Self::read_input(filename) {
        let (direction, number) = line.split_at(1);
        let mut number = number.parse::<i32>().unwrap();

        let previous_position = current_position;

        // take into account every time the dial passes through 0
        count += number / 100;
        number = number % 100;

        match direction {
            "L" => current_position = current_position - number,
            "R" => current_position = current_position + number,
            _ => panic!("Invalid direction: {}", direction.to_string()),
        }

        if current_position == 0 {
            count += 1;
            continue;
        }

        if current_position < 0 {
            if previous_position != 0 {
                count += 1;
            }
            current_position = (current_position + 100) % 100;
            continue;
        }

        if current_position > 99 {
            count += 1;
            current_position = current_position % 100;
            continue;
        }
    }
    count
}

The logic:

  1. Full revolutions: Every 100 ticks guarantees one pass through zero → count += number / 100
  2. Partial rotation: Check if we cross the boundary (position goes negative or exceeds 99)
  3. Edge case: If we started at 0 and went left, we shouldn’t count that crossing — hence the previous_position != 0 check

The Bug That Cost Me 5 Minutes 🐛

I initially overcounted because when checking if current_position < 0, I always added 1. But if we start at position 0 and rotate left, we don’t actually cross zero — we just leave it. The fix was tracking previous_position and only counting when previous_position != 0.


Gemini’s Solution

I prompted Gemini with the problem and a basic code structure. It one-shotted both parts in about 30 seconds total.

Gemini Part 1

Essentially the same approach as mine:

match direction {
    'R' => {
        current_pos = (current_pos + amount) % dial_size;
    }
    'L' => {
        current_pos = (current_pos - amount) % dial_size;
        if current_pos < 0 {
            current_pos += dial_size;
        }
    }
    _ => { /* ... */ }
}

if current_pos == 0 {
    zero_visits += 1;
}

Gemini Part 2

Here’s where it gets interesting. Gemini used a simulation approach with a loop:

// 1. Calculate full revolutions.
zero_visits += amount / dial_size;

// 2. Simulate the remaining ticks.
let remaining_steps = amount % dial_size;

for _ in 0..remaining_steps {
    match direction {
        'R' => {
            current_pos = (current_pos + 1) % dial_size;
        }
        'L' => {
            current_pos = (current_pos - 1 + dial_size) % dial_size;
        }
        _ => { /* ... */ }
    }

    if current_pos == 0 {
        zero_visits += 1;
    }
}

It’s more straightforward — loop through each remaining tick and check if we hit zero. No edge cases to worry about!


Comparison: Handcrafted vs AI

AspectMy SolutionGemini’s Solution
ApproachMathematical (O(1) per instruction)Simulation (O(n) per instruction)
ComplexityMore complex logic with edge casesSimple loop, easy to understand
Bug potentialHigher — I hit the previous_position bugLower — brute force is reliable
PerformanceFaster for large rotation numbersSlower but negligible for this input

Reflection

  • AI solution is more straightforward and simply uses a loop to simulate the rotation process tick by tick
  • My solution is more “clever” with math but introduced a subtle bug that took 5 minutes to debug
  • For competitive programming, the simulation approach is often better — it’s faster to write and less error-prone
  • The mathematical approach only wins when performance is critical (e.g., rotations in the millions)

Time spent: ~15 minutes total (including the 5-minute bug hunt)

Lesson learned: Sometimes the “dumb” solution is the smart choice! 🧠