My 2025 Solutions

Day 1

Part 1

input <- readLines("2025/01-input") |>
  stringr::str_replace_all(c("L" = "-", "R" = "")) |>
  as.integer()

sum(cumsum(c(50, input)) %% 100 == 0)
0.003 sec elapsed

Part 2

input <- readLines("2025/01-input") |>
  stringr::str_replace_all(c("L" = "-", "R" = "")) |>
  as.integer()

zeros <- 0
position <- 50

for (value in input) {
  for (j in seq_len(abs(value))) {
    position <- position + sign(value)

    position <- position %% 100

    if (position == 0) {
      zeros <- zeros + 1
    }
  }
}

zeros
0.05 sec elapsed

Day 2

Part 1

library(stringr)

input <- readLines("2025/02-input")

input |>
  str_split(",") |>
  lapply(str_split, "-") |>
  unlist(recursive = FALSE) |>
  lapply(\(x) seq(x[1], x[2])) |>
  unlist() |>
  as.character() |>
  str_subset("^(\\d+)(\\1)$") |>
  as.numeric() |>
  sum()
0.791 sec elapsed

Part 2

library(stringr)

input <- readLines("2025/02-input")

input |>
  str_split(",") |>
  lapply(str_split, "-") |>
  unlist(recursive = FALSE) |>
  lapply(\(x) seq(x[1], x[2])) |>
  unlist() |>
  as.character() |>
  str_subset("^(\\d+)(\\1)+$") |>
  as.numeric() |>
  sum()
0.735 sec elapsed

Day 3

Part 1

f <- function(x) {
  repeat {
    if (length(x) == 2) {
      break
    }

    if (x[1] < x[2]) {
      x <- x[-1]
    } else if (x[2] > x[3]) {
      x <- x[-3]
    } else {
      x <- x[-2]
    }
  }

  sum(x * c(10, 1))
}

readLines("2025/03-input") |>
  strsplit("") |>
  lapply(as.numeric) |>
  lapply(f) |>
  unlist() |>
  sum()
0.025 sec elapsed

Part 2

f <- function(x) {
  repeat {
    if (length(x) == 12) {
      break
    }

    diff <- diff(x)

    if (all(diff <= 0)) {
      x <- head(x, -1)
      next
    }

    x <- x[-min(which(diff > 0))]
  }

  as.numeric(paste0(x, collapse = ""))
}

options(digits = 20)

readLines("2025/03-input") |>
  strsplit("") |>
  lapply(as.numeric) |>
  lapply(f) |>
  unlist() |>
  sum()
0.095 sec elapsed

Day 4

Part 1

input <- readLines("2025/04-input")
tokens <- strsplit(input, "")
token_lengths <- lengths(tokens)
mat <- matrix(nrow = length(input), ncol = max(token_lengths))

for (i in seq_along(input)) {
  mat[i, seq_len(token_lengths[i])] <- tokens[[i]]
}

around <- function(x, y) {
  x_max <- nrow(mat)
  y_max <- ncol(mat)

  xs <- x + c(-1, 0, 1)
  ys <- y + c(-1, 0, 1)

  xs <- xs[xs > 0 & xs <= x_max]
  ys <- ys[ys > 0 & ys <= y_max]

  mat[xs, ys]
}

neighbors <- function(x, y) {
  sum(around(x, y) == "@") - (mat[x, y] == "@")
}

matches <- mat
matches[] <- 0

for (row in seq_len(nrow(mat))) {
  for (col in seq_len(ncol(mat))) {
    matches[row, col] <- neighbors(row, col)
  }
}

sum(matches < 4 & mat == "@")
0.062 sec elapsed

Part 2

input <- readLines("2025/04-input")
tokens <- strsplit(input, "")
token_lengths <- lengths(tokens)
mat <- matrix(nrow = length(input), ncol = max(token_lengths))

for (i in seq_along(input)) {
  mat[i, seq_len(token_lengths[i])] <- tokens[[i]]
}

before <- sum(mat == "@")

around <- function(x, y) {
  x_max <- nrow(mat)
  y_max <- ncol(mat)

  xs <- x + c(-1, 0, 1)
  ys <- y + c(-1, 0, 1)

  xs <- xs[xs > 0 & xs <= x_max]
  ys <- ys[ys > 0 & ys <= y_max]

  mat[xs, ys]
}

neighbors <- function(x, y) {
  sum(around(x, y) == "@") - (mat[x, y] == "@")
}

matches <- mat
matches[] <- 0

repeat {
  for (row in seq_len(nrow(mat))) {
    for (col in seq_len(ncol(mat))) {
      matches[row, col] <- neighbors(row, col)
    }
  }
  delete <- matches < 4 & mat == "@"

  if (!any(delete)) {
    break
  }

  mat[delete] <- "."
}

after <- sum(mat == "@")

before - after
2.219 sec elapsed

Day 5

Part 1

input <- readLines("2025/05-input")

divide <- which(input == "")

fresh <- input[seq(1, divide - 1)]
aval <- input[seq(divide + 1, length(input))] |> as.numeric()

ranges <- fresh |>
  strsplit("-") |>
  lapply(as.numeric)

checker <- function(x) {
  fresh <- FALSE
  for (range in ranges) {
    if (x >= range[1] && x <= range[2]) {
      fresh <- TRUE
      break
    }
  }
  fresh
}

vapply(aval, checker, FUN.VALUE = logical(1)) |>
  sum()
0.007 sec elapsed

Part 2

input <- readLines("2025/05-input")

divide <- which(input == "")

fresh <- input[seq(1, divide - 1)]

ranges <- fresh |>
  strsplit("-") |>
  lapply(as.numeric)

overlap <- function(x, y) {
  any(x[2] >= y) && any(x[1] <= y) || any(y[2] >= x) && any(y[1] <= x)
}
combine <- function(x, y) {
  c(min(x[1], y[1]), max(x[2], y[2]))
}

finished_ranges <- list()

done <- FALSE

while (!done) {
  repeat {
    matched_ranges <- c()

    if (length(ranges) == 1) {
      finished_ranges <- c(finished_ranges, ranges)
      done <- TRUE
      break
    }

    for (i in seq(2, length(ranges))) {
      if (overlap(ranges[[1]], ranges[[i]])) {
        matched_ranges <- c(matched_ranges, i)
      }
    }

    if (length(matched_ranges) == 0) {
      finished_ranges <- c(finished_ranges, ranges[1])
      ranges[1] <- NULL
      break
    }

    for (id in rev(matched_ranges)) {
      ranges[[1]] <- combine(ranges[[1]], ranges[[id]])
      ranges[[id]] <- NULL
    }

  }
}

options(digits = 20)

finished_ranges |>
  purrr::map_dbl(\(x) x[2] - x[1] + 1) |>
  sum()
0.028 sec elapsed

Day 6

Day 7

Day 8

Day 9

Day 10

Day 11

Day 12