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

day09.R (4543B)


      1 #!/usr/bin/env Rscript
      2 
      3 source("src/utils.R")
      4 
      5 test_input <- c(
      6   "7,1",
      7   "11,1",
      8   "11,7",
      9   "9,7",
     10   "9,5",
     11   "2,5",
     12   "2,3",
     13   "7,3"
     14 )
     15 puzzle_input <- aoc_input(9)
     16 
     17 parse_input <- function(input_lines) {
     18   input_lines |>
     19     strsplit(",") |>
     20     lapply(as.numeric) |>
     21     (\(x) do.call(rbind, x))()
     22 }
     23 
     24 tabulate_edges <- function(input_points) {
     25   edge_info <- list(
     26     tab = utils::hashtab("identical", nrow(input_points)),
     27     ind = rep(NA_real_, nrow(input_points))
     28   )
     29   for (p1 in seq_len(nrow(input_points))) {
     30     p2 <- (p1 %% nrow(input_points)) + 1
     31     l_h <- input_points[p1, 2]
     32     if (l_h == input_points[p2, 2]) {
     33       edge_info$ind[length(edge_info$ind) + 1] <- l_h
     34       utils::sethash(
     35         edge_info$tab,
     36         l_h,
     37         c(
     38           list(c(input_points[p1, 1], input_points[p2, 1])),
     39           utils::gethash(edge_info$tab, l_h, list())
     40         )
     41       )
     42     }
     43   }
     44   edge_info$ind <- unique(sort(edge_info$ind[!is.na(edge_info$ind)]))
     45   edge_info
     46 }
     47 
     48 # Part 1 =====================================================================
     49 
     50 measure_rect <- function(p1, p2) {
     51   (abs(p1[1] - p2[1]) + 1) * (abs(p1[2] - p2[2]) + 1)
     52 }
     53 
     54 find_max_rect <- function(edge_info) {
     55   max_rectangle <- NULL
     56   for (sl in seq_along(edge_info$ind)) {
     57     for (sl2 in sl:length(edge_info$ind)) {
     58       for (p1 in range(unlist(edge_info$tab[[edge_info$ind[sl]]]))) {
     59         for (p2 in range(unlist(edge_info$tab[[edge_info$ind[sl2]]]))) {
     60           max_rectangle <- max(
     61             max_rectangle,
     62             measure_rect(
     63               c(p1, edge_info$ind[sl]),
     64               c(p2, edge_info$ind[sl2])
     65             )
     66           )
     67         }
     68       }
     69     }
     70   }
     71   max_rectangle
     72 }
     73 
     74 part1 <- function(input_lines) {
     75   input_lines |>
     76     parse_input() |>
     77     tabulate_edges() |>
     78     find_max_rect()
     79 }
     80 
     81 test_part1 <- function() {
     82   stopifnot(part1(test_input) == 50)
     83 }
     84 
     85 # Part 2 =====================================================================
     86 
     87 edge_inside <- function(e1, e2) {
     88   min(e2) < max(e1) && max(e2) > min(e1)
     89 }
     90 
     91 remove_edge <- function(e1, e2) {
     92   if (max(e2) >= max(e1)) {
     93     if (min(e2) <= min(e1)) {
     94       NULL
     95     } else {
     96       list(c(min(e1), min(e2)))
     97     }
     98   } else if (min(e2) <= min(e1)) {
     99     list(c(max(e2), max(e1)))
    100   } else {
    101     list(c(min(e1), min(e2)), c(max(e2), max(e1)))
    102   }
    103 }
    104 
    105 add_or_remove_edge <- function(edge_front, edge, can_add = TRUE) {
    106   for (i in seq_along(edge_front)) {
    107     if (edge_inside(edge_front[[i]], edge)) {
    108       return(c(
    109         edge_front[-i],
    110         remove_edge(edge_front[[i]], edge)
    111       ))
    112     }
    113   }
    114   if (can_add) {
    115     edge_front <- c(edge_front, list(edge))
    116     edge_front <- edge_front[order(vapply(edge_front, \(x) x[1], numeric(1)))]
    117     i <- 1
    118     repeat {
    119       if (i >= length(edge_front)) break
    120       if (max(edge_front[[i]]) == min(edge_front[[i + 1]])) {
    121         edge_front[[i]] <- c(min(edge_front[[i]]), max(edge_front[[i + 1]]))
    122         edge_front[[i + 1]] <- NULL
    123       } else {
    124         i <- i + 1
    125       }
    126     }
    127   }
    128   edge_front
    129 }
    130 
    131 points_in_edge_front <- function(edge_front, p) {
    132   points_in_edge <- function(edge) {
    133     min(p) >= min(edge) && max(p) <= max(edge)
    134   }
    135   any(vapply(edge_front, points_in_edge, logical(1)))
    136 }
    137 
    138 sweep_edge_front <- function(edge_info) {
    139   max_rectangle <- NULL
    140   start_front <- list()
    141   for (y1_idx in 1:(length(edge_info$ind) - 1)) {
    142     y1 <- edge_info$ind[y1_idx]
    143     start_edges <- edge_info$tab[[y1]]
    144     start_front <- Reduce(add_or_remove_edge, start_edges, start_front)
    145     end_front <- start_front
    146     for (x1 in unlist(start_edges)) {
    147       for (y2_idx in (y1_idx + 1):length(edge_info$ind)) {
    148         y2 <- edge_info$ind[y2_idx]
    149         end_edges <- edge_info$tab[[y2]]
    150         for (x2 in unlist(end_edges)) {
    151           if (points_in_edge_front(start_front, c(x1, x2)) &&
    152             points_in_edge_front(end_front, c(x1, x2))) {
    153             max_rectangle <- max(
    154               max_rectangle,
    155               measure_rect(c(y1, x1), c(y2, x2))
    156             )
    157           }
    158         }
    159         end_front <- Reduce(\(x, y) add_or_remove_edge(x, y, FALSE), end_edges, end_front)
    160         if (!points_in_edge_front(end_front, x1)) break
    161       }
    162     }
    163   }
    164   max_rectangle
    165 }
    166 
    167 part2 <- function(input_lines) {
    168   input_lines |>
    169     parse_input() |>
    170     tabulate_edges() |>
    171     sweep_edge_front()
    172 }
    173 
    174 test_part2 <- function() {
    175   stopifnot(part2(test_input) == 24)
    176 }
    177 
    178 main <- function() {
    179   test_part1()
    180   cat("Part 1 solution: ", part1(puzzle_input), "\n")
    181   test_part2()
    182   cat("Part 2 solution: ", part2(puzzle_input), "\n")
    183 }
    184 
    185 main()