Exploring and solving Day 1 of the 2025 Advent of Code using R.
Author
Griffin Judy
Published
December 1, 2025
Intro
Hello everyone! This year, I will be attempting to complete the Advent of Code (AOC) and posting the code on my website. For those unfamiliar, the Advent of Code is a fun series of coding puzzles in the style of an advent calendar. The puzzles vary in difficulty throughout the month and are language-agnostic. Daily completions will be posted in the Blog, and the completed compilation will be posted in Projects. I am a data analyst (if you couldn’t tell by the R), not a “true” programmer, so there will be room for improvement! Practice and improvement are the goal.
The AOC posts will be long, with an emphasis on explanation and understanding. They are not a tutorial, and are more of a glimpse into how I work and think through coding problems.
Part 1
Prompt
Part 1 has the elves rotating a dial on a safe to receive a password. The dial has 0-99 on it. The direction of the rotation is denoted by an “L” or “R” present before the number of rotations. For example, “R11” would be “turn to the right 11” and “L15” would be “turn to the left 15”. There are no negative numbers on a dial, so moving 1 to the left (L1) at 0 would rollover to 99. Likewise, moving one to the right (R1) at 99 would rollover to 0. The dial starts on 50. The final answer will be the number of times that the dial lands on exactly 0 after rotating.
Planning
There are 3 challenges to address before attempting to solve the problem:
Read in the text file []
Separate the Left (L) and Right (R) designation from the numbers []
Account for rollover []
Executing
Challenge 1
This should all be doable using base R. Step 1 is reading in the text file. The text file has each rotation as its own line. It looks like:
Example AOC Data
R1
L55
R17
R13
L15
After some Googling, I learned about the readLines function in base R. It reads in a text file line-by-line and assigns the data into a vector. It seems to work perfectly!
#Using PATH instead of the actual path for privacy. #If following along, use the location of the data here e.g. //local//data.txtROTATIONS <-readLines(PATH)#head gives a glimpse of the first 6 head(ROTATIONS)
[1] "R1" "R25" "R50" "R10" "L20" "L14"
#length tells us the size of the vectorlength(ROTATIONS)
[1] 4577
Challenge 1 addressed!
Read in the text file [X]
Challenge 2
Next, rotation direction needs to be separated from rotation quantity. This can be done in a few ways, but practicing base R is always useful. The substr function separates the direction and the amount.
#this is done in base R but it is probably easier to use the stringr package#earlier I wrote that I thought it was doable in base R so we use substrdir <-substr(ROTATIONS[5], 1, 1)dir
[1] "L"
#using as.numeric to ensure it is a double instead of a char amt <-as.numeric(substr(ROTATIONS[5], 2, nchar(ROTATIONS[5])))amt
[1] 20
#check type if desired#typeof(amt)#lefts need to be treated as negative since L20 is the same as subtracting 20if(dir =="L"){ amt <- (amt *-1) }#it's negativeamt
[1] -20
Challenge 2 is addressed!
Separate the Left (L) and Right (R) designation from the numbers [X]
Challenge 3
Accounting for the rollover is a simple but important step. If the mathematical result of the rotation (not the rotation itself!) is negative, the negative number should be added to multiples of 100 until it is positive. For example, L1 at 0 would result in -1, but the dial is round so it rolls over to 99. 100 + -1 = 99. This pattern continues; starting at 10, L220 results in -210. 300 + -210 = 90.
while(amt <0){ amt <- amt +100}
The right rollover is easier. 99 R1 is 100, but it needs to be 0, so if the amount is greater than 99, 100 should be subtracted from it. For example, starting at 90, R20 results in 110. 110 - 100 = 10. This pattern continues.
As a quick note, this is not the most efficient way to do this, but it works. You could reduce the number of function calls by getting the amt / 100, then multiplying the rounded quotient by 100 and subtracting that from amt. The more efficient way looks messier, and I want to practice while loops in R, so it is done this way! It took a few failed submission attempts to realize that the rotation amount itself is not limited to 1-99, leading to the dial having a final value of >99 or <0 after only adding 100 or -100. Transforming what was previously an if statement into a while statement fixed it!
while(amt >99){ amt <- amt -100}
Challenge 3 is addressed!
Read in the text file [X]
Separate the Left (L) and Right (R) designation from the numbers [X]
Account for rollover [X]
Combining
Now we can combine everything for the final code! It is important to remember that the prompt wants us to count the number of times the dial lands on exactly 0.
#what the mathematical outcome of the dial would bedial <-50#initiate the count of zeros (what we are looking for) at 0zeros <-0for(i in1:length(ROTATIONS)){#separate everything dir <-substr(ROTATIONS[i], 1, 1) amt <-as.numeric(substr(ROTATIONS[i], 2, nchar(ROTATIONS[i])))#make left turns negativeif(dir =="L"){ amt <- (amt *-1) }#mathematically move the dial dial <- (dial + amt)#left rolloverwhile(dial <0){ dial <- (dial +100) }#right rolloverwhile(dial >99){ dial <- (dial -100) }#count the 0sif (dial ==0){ zeros <- (zeros +1) }#print(dial)}zeros
[1] 1139
Part 2
Prompt
Part 2 has us count the number of times the dial passes or lands on zero. This took me a few hours and a lot of debugging to solve. I also had to look at hints on the Advent of Code subreddit. Adding zeros <- (zeros + 1) to the while loops causes zero to be counted twice when landing on it. There is probably a more elegant solution to this problem, but I wanted to keep as much code as possible.
#what the mathematical outcome of the dial would bedial <-50#initiate the count of zeros (what we are looking for) at 0zeros <-0#the starting dial for check when starting at 0start_dial <-0for(i in1:length(ROTATIONS)){#separate everything dir <-substr(ROTATIONS[i], 1, 1) amt <-as.numeric(substr(ROTATIONS[i], 2, nchar(ROTATIONS[i])))# Store the starting position start_dial <- dial# make left turns negativeif(dir =="L"){ amt <- (amt *-1) }# mathematically move the dial dial <- (dial + amt)# Track if 0 was carried carry <-FALSEif(dial <0){# Special handling when starting from 0if(start_dial ==0){#floor rounds down, opposite of ceiling zeros <- zeros +floor(abs(dial) /100) } else { zeros <- zeros +floor(abs(dial) /100) +1 } carry <-TRUE } elseif(dial >99){ zeros <- zeros +floor(dial /100) carry <-TRUE }#left rolloverwhile(dial <0){ dial <- (dial +100) }#if#right rolloverwhile(dial >99){ dial <- (dial -100) }#if# Only count landing on 0 if we didn't carryif(dial ==0&& carry ==FALSE){ zeros <- zeros +1 }}zeros
[1] 6684
Conclusion
I had fun completing Part 1. The prompt helped me visualize and understand what was occurring. Part 2 was significantly more difficult, and I needed help to finish the problem. In the future, I should build more test cases to expedite finding issues. Thank you for reading!