Advent of Code 2025: Day 1
Problem
tl;dr — A safe has a circular dial with numbers 0-99, starting at 50. You’re given a sequence of rotations:
| Instruction | Meaning |
|---|---|
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:
- Full revolutions: Every 100 ticks guarantees one pass through zero →
count += number / 100 - Partial rotation: Check if we cross the boundary (position goes negative or exceeds 99)
- Edge case: If we started at 0 and went left, we shouldn’t count that crossing — hence the
previous_position != 0check
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
| Aspect | My Solution | Gemini’s Solution |
|---|---|---|
| Approach | Mathematical (O(1) per instruction) | Simulation (O(n) per instruction) |
| Complexity | More complex logic with edge cases | Simple loop, easy to understand |
| Bug potential | Higher — I hit the previous_position bug | Lower — brute force is reliable |
| Performance | Faster for large rotation numbers | Slower 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! 🧠