Try an interactive version of this dialog: Sign up at solve.it.com, click Upload, and pass this URL.
from aocd import get_data
inp = get_data(year=2025, day=1)
len(inp), len(inp.splitlines()), len(inp.splitlines(0)), inp[:100]
d = 50
positions = []
for move in moves:
d = (d+move[1] * (-1 if move[0]=='L' else 1))%100
positions.append(d)
positions
Please put this together into a solve function that takes a string and outputs the number of 0s in positions
Here's a solve function that brings it all together:
def solve(s):
d = 50
positions = []
for line in s.splitlines():
direction, amount = line[0], int(line[1:])
d = (d + amount * (-1 if direction == 'L' else 1)) % 100
positions.append(d)
return positions.count(0)
This takes your input string, parses each move, updates the dial position, and returns how many times it lands on 0. Want to test it with your samp and inp data?
You did really well! Here's what I noticed:
Strengths:
- You broke down the problem methodically with a clear plan before coding
- You tested each piece incrementally (parsing a single move, then applying it, then looping through all moves)
- Your code is clean and readable
- You verified with the sample data before running on the full input
Potential improvements:
- You could avoid storing all positions if you only need the count of zeros. Instead of appending to a list, you could just increment a counter when
d == 0. This would be more memory-efficient for large inputs. - Minor style point: you could use
1 if direction == 'R' else -1instead of the double negative with 'L', though your version works perfectly fine
The incremental testing approach you used is excellent problem-solving practice. Did this problem come from Advent of Code?
--- Part Two --- You're sure that's the right password, but the door won't open. You knock, but nobody answers. You build a snowman while you think.
As you're rolling the snowballs for your snowman, you find another security document that must have fallen into the snow:
"Due to newer security protocols, please use password method 0x434C49434B until further notice."
You remember from the training seminar that "method 0x434C49434B" means you're actually supposed to count the number of times any click causes the dial to point at 0, regardless of whether it happens during a rotation or at the end of one.
Following the same rotations as in the above example, the dial points at zero a few extra times during its rotations:
The dial starts by pointing at 50. The dial is rotated L68 to point at 82; during this rotation, it points at 0 once. The dial is rotated L30 to point at 52. The dial is rotated R48 to point at 0. The dial is rotated L5 to point at 95. The dial is rotated R60 to point at 55; during this rotation, it points at 0 once. The dial is rotated L55 to point at 0. The dial is rotated L1 to point at 99. The dial is rotated L99 to point at 0. The dial is rotated R14 to point at 14. The dial is rotated L82 to point at 32; during this rotation, it points at 0 once. In this example, the dial points at 0 three times at the end of a rotation, plus three more times during a rotation. So, in this example, the new password would be 6.
Be careful: if the dial were pointing at 50, a single rotation like R1000 would cause the dial to point at 0 ten times before returning back to 50!
Using password method 0x434C49434B, what is the password to open the door?
moves = [(o[0], int(o[1:])) for o in samp.splitlines()]
ds = [m[1] for m in moves]
max(ds), min(ds)
d = 50
positions = [50]
for move in moves:
d = (d+move[1] * (-1 if move[0]=='L' else 1))
positions.append(d)
positions
def check(s, e): return sum(i%100==0 for i in range(s, e, -1 if (s>e) else 1))
check(50, -18)
moves = [(o[0], int(o[1:])) for o in inp.splitlines()]
d = 50
positions = [50]
for move in moves:
d = (d+move[1] * (-1 if move[0]=='L' else 1))
positions.append(d)
jumps = list(zip(positions[:-1], positions[1:]))
sum([check(*m) for m in jumps])
Nice work getting the right answer! There are definitely other approaches. Here are a few alternatives:
1. Math-based approach: Instead of checking every number in the range, you could calculate how many multiples of 100 are crossed. For a jump from s to e, you could use floor division to count crossings directly.
2. Modulo tracking: Work in modulo 100 space from the start, but track when you "wrap around" zero (going from 99→0 or 0→99).
3. Simplify your range check: Your check function iterates through every value. You could make it more efficient by calculating the count mathematically.
Which of these sounds most interesting to explore? Or would you like to try optimizing your current approach first?
Your current check function uses range to generate every single value between s and e, then checks if each is divisible by 100. That's potentially thousands of iterations per jump!
Instead, you can calculate directly: how many multiples of 100 exist between two numbers?
Think about it this way: if you're going from 50 to -18, you cross 0 (which is 0×100). If you go from -5 to 55, you also cross 0. If you go from 50 to 250, you'd cross 100 and 200.
Can you think of a way to calculate how many multiples of 100 are between s and e without iterating through every number?
Hint: Consider using floor division (//) on the start and end positions.
Sure! Here's a more efficient version:
def check(s, e):
if s > e:
s, e = e, s
return e // 100 - s // 100
This calculates how many multiples of 100 are crossed by using floor division. The difference between e // 100 and s // 100 gives you the count directly.
Try it with your test cases and see if it gives the same results!