advent_of_code_2025

My (attempted) solutions to the 2025 Advent of Code
git clone https://git.eamoncaddigan.net/advent_of_code_2025.git
Log | Files | Refs | README

day05.R (2511B)


      1 #!/usr/bin/env Rscript
      2 
      3 source("src/utils.R")
      4 
      5 test_input <- c(
      6   "3-5",
      7   "10-14",
      8   "16-20",
      9   "12-18",
     10   "",
     11   "1",
     12   "5",
     13   "8",
     14   "11",
     15   "17",
     16   "32"
     17 )
     18 puzzle_input <- aoc_input(5)
     19 
     20 parse_input <- function(input_lines) {
     21   blank_idx <- which(input_lines == "")
     22   parsed_input <- list()
     23 
     24   parsed_input$fresh_ranges <- input_lines[1:(blank_idx - 1)] |>
     25     strsplit("-") |>
     26     unlist() |>
     27     as.numeric() |>
     28     matrix(nrow = blank_idx - 1, byrow = TRUE)
     29   parsed_input$fresh_ranges <- parsed_input$fresh_ranges[order(
     30     parsed_input$fresh_ranges[, 1], parsed_input$fresh_ranges[, 2]
     31   ), ]
     32 
     33   parsed_input$ingredient_ids <- input_lines[(blank_idx + 1):length(input_lines)] |>
     34     as.numeric() |>
     35     sort()
     36 
     37   parsed_input
     38 }
     39 
     40 test_ingredients <- function(ingredient_ids, fresh_ranges) {
     41   is_fresh <- rep(NA, length(ingredient_ids))
     42 
     43   fresh_idx <- 1
     44   for (ingredient_idx in seq_along(ingredient_ids)) {
     45     while (
     46       fresh_idx <= nrow(fresh_ranges) &&
     47         fresh_ranges[fresh_idx, 2] < ingredient_ids[ingredient_idx]
     48     ) {
     49       fresh_idx <- fresh_idx + 1
     50     }
     51     if (fresh_idx > nrow(fresh_ranges)) {
     52       is_fresh[ingredient_idx:length(ingredient_ids)] <- FALSE
     53       break
     54     }
     55     is_fresh[ingredient_idx] <- ingredient_ids[ingredient_idx] >= fresh_ranges[fresh_idx, 1]
     56   }
     57   is_fresh
     58 }
     59 
     60 part1 <- function(input_lines) {
     61   input_parsed <- parse_input(input_lines)
     62   sum(test_ingredients(input_parsed$ingredient_ids, input_parsed$fresh_ranges))
     63 }
     64 
     65 test_part1 <- function() {
     66   stopifnot(part1(test_input) == 3)
     67 }
     68 
     69 count_fresh_ids <- function(fresh_ranges) {
     70   add_remaining_fresh_ids <- function(row_id, last_fresh_id) {
     71     new_ids <- if (fresh_ranges[row_id, 2] <= last_fresh_id) {
     72       0
     73     } else if (fresh_ranges[row_id, 1] <= last_fresh_id) {
     74       fresh_ranges[row_id, 2] - last_fresh_id
     75     } else {
     76       fresh_ranges[row_id, 2] - fresh_ranges[row_id, 1] + 1
     77     }
     78 
     79     if (row_id == nrow(fresh_ranges)) {
     80       new_ids
     81     } else {
     82       new_ids +
     83         add_remaining_fresh_ids(
     84           row_id + 1,
     85           max(last_fresh_id, fresh_ranges[row_id, 2])
     86         )
     87     }
     88   }
     89   add_remaining_fresh_ids(1, 0)
     90 }
     91 
     92 part2 <- function(input_lines) {
     93   input_parsed <- parse_input(input_lines)
     94   count_fresh_ids(input_parsed$fresh_ranges)
     95 }
     96 
     97 test_part2 <- function() {
     98   part2(test_input) == 14
     99 }
    100 
    101 main <- function() {
    102   test_part1()
    103   cat("Part 1 solution: ", part1(puzzle_input), "\n")
    104   test_part2()
    105   cat(sprintf("Part 2 solution: %0.0f\n", part2(puzzle_input)))
    106 }
    107 
    108 main()