Advent of Code: 2025 Day 1 in R

Published

December 15, 2025

See the puzzle instructions here.

Part 1

Solution

We first read in the instructions. In this case it’s very simple to just read the input txt file.

input <- readLines("test_input.txt")

We then need to parse the instruction:

get_instructions <- function(steps) {
  # Get a list of all steps with characters split
  split_steps <- strsplit(steps, "")
  
  # Initialize empty vectors for direction markers and digits
  direction_vector <- character(length(steps))
  digits_vector <- integer(length(steps))
  
  # Iterate over the split strings
  for (i in seq_along(split_steps)) {
    # Extract the direction and assign it to the vector
    direction_vector[i] <- split_steps[[i]][1]
    
    # Get the remaining characters and convert them to integer,
    # then assign to the vector
    digits <- split_steps[[i]][2:length(split_steps[[i]])]
    digits_vector[i] <- as.integer(paste0(digits, collapse = ""))
  }
  
  # Return a dataframe with the steps
  data.frame(direction = direction_vector, digits = digits_vector)
}

Next, we define a function to move the dial. It is a circular dial from 0 to 99, which means that it will roll back over to 0 when it passes 99. To account for this, we need to take the modulo of 100.

move_dial <- function(position, instruction) {
  if (instruction$direction == "L") {
    return((position - instruction$digits) %% 100)
  } else if (instruction$direction == "R") {
    return((position + instruction$digits) %% 100)
  } else {
    stop("Invalid input")
  }
}

I’ll create a function to iterate over the rows of the dataframe and execute move_dial(), logging each time the dial lands on 0.

solve_part_1 <- function(input) {
  # Start at position = 50
  position <- 50
  
  # Initialize a variable for the number of times we land on 0
  lands_on_zero <- 0
  
  # Parse the input
  instructions <- get_instructions(input)
  
  # Iterate over the rows
  for (i in seq_len(nrow(instructions))) {
    position <- move_dial(position, instructions[i, ])
    if (position == 0) {
      lands_on_zero <- lands_on_zero + 1
    }
  }
  
  lands_on_zero
}

We can then test it out:

test_input <- readLines("test_input.txt")
solve_part_1(test_input)
[1] 3

That’s what we’re supposed to get! Now for the real data

real_input <- readLines("input.txt")
solve_part_1(real_input)
[1] 1177

And that’s the correct answer!

Part 2

Solution

I’m going to attempt a brute force solution and hope it isn’t dreadfully slow. Instead of moving the dial all x steps at once, I will use a loop to increment it one step at a time for the prescribed number of steps, and any time it lands on zero I will add to the running total.

move_dial <- function(position, instructions) {
  
  # Initialize a variable to count when it hits zero
  zero_count <- 0
  
  # First iterate over all rows of the instructions dataframe
  for (i in seq_len(nrow(instructions))) {
    # Then iterate again, this time counting up to the number listed in `digits`
    for (j in seq_len(instructions$digits[i])) {
      # If "L", subtract, if "R", add, still modulo 100
      if (instructions$direction[i] == "L") {
        position <- (position - 1) %% 100
      } else if (instructions$direction[i] == "R") {
        position <- (position + 1) %% 100
      } else {
        stop("Invalid input")
      }
      
      # If it is ever equal to 0, increment the counter
      if (position == 0) {
        zero_count <- zero_count + 1
      }
    }
  }
  
  zero_count
}

# Put it together with `get_instructions()`
solve_part_2 <- function(input) {
  instructions <- get_instructions(input)
  
  move_dial(50, instructions)
}

Let’s try it with the test input:

solve_part_2(test_input)
[1] 6

That’s correct!

Now let’s see how slow it goes with the full real input:

solve_part_2(real_input)
[1] 6768

That’s correct, and not all that slow! Challenge complete, declare victory.