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

day04.R (2055B)


      1 #!/usr/bin/env Rscript
      2 
      3 source("src/utils.R")
      4 
      5 test_input <- c(
      6   "..@@.@@@@.",
      7   "@@@.@.@.@@",
      8   "@@@@@.@.@@",
      9   "@.@@@@..@.",
     10   "@@.@@@@.@@",
     11   ".@@@@@@@.@",
     12   ".@.@.@.@@@",
     13   "@.@@@.@@@@",
     14   ".@@@@@@@@.",
     15   "@.@.@@@.@."
     16 )
     17 puzzle_input <- aoc_input(4)
     18 
     19 parse_input <- function(input_lines) {
     20   input_lines |>
     21     strsplit("", fixed = TRUE) |>
     22     (\(x) matrix(unlist(x), nrow = length(x), byrow = TRUE))()
     23 }
     24 
     25 count_neighbors <- function(diagram) {
     26   nc <- ncol(diagram)
     27   nr <- nrow(diagram)
     28   neighbors <- matrix(0, nrow = nr, ncol = nc)
     29   is_occupied <- ifelse(diagram == "@", 1, 0)
     30 
     31   # shift = -1: 1 to nr-1 |-> 2 to nr-0
     32   # shift = +1: 2 to nr-0 |-> 1 to nr-1
     33   # shift =  0: 1 to nr-0 |-> 1 to nr-0
     34   for (r_shift in (-1:1)) {
     35     for (c_shift in (-1:1)) {
     36       if (r_shift != 0 || c_shift != 0) {
     37         neighbors[(1 + max(0, r_shift)):(nr + min(0, r_shift)), (1 + max(0, c_shift)):(nc + min(0, c_shift))] <-
     38           neighbors[(1 + max(0, r_shift)):(nr + min(0, r_shift)), (1 + max(0, c_shift)):(nc + min(0, c_shift))] +
     39           is_occupied[(1 - min(0, r_shift)):(nr - max(0, r_shift)), (1 - min(0, c_shift)):(nc - max(0, c_shift))]
     40       }
     41     }
     42   }
     43   neighbors
     44 }
     45 
     46 mark_moveable <- function(diagram) {
     47   (diagram == "@") & (count_neighbors(diagram) < 4)
     48 }
     49 
     50 part1 <- function(input_lines) {
     51   input_lines |>
     52     parse_input() |>
     53     mark_moveable() |>
     54     sum()
     55 }
     56 
     57 test_part1 <- function() {
     58   stopifnot(part1(test_input) == 13)
     59 }
     60 
     61 count_moveable <- function(diagram) {
     62   total_moved <- 0
     63   repeat {
     64     moveable <- mark_moveable(diagram)
     65     moveable_count <- sum(moveable)
     66     if (moveable_count == 0) break
     67     total_moved <- total_moved + moveable_count
     68     diagram[moveable] <- "."
     69   }
     70   total_moved
     71 }
     72 
     73 part2 <- function(input_lines) {
     74   input_lines |>
     75     parse_input() |>
     76     count_moveable()
     77 }
     78 
     79 test_part2 <- function() {
     80   stopifnot(part2(test_input) == 43)
     81 }
     82 
     83 main <- function() {
     84   test_part1()
     85   cat("Part 1 solution: ", part1(puzzle_input), "\n")
     86   test_part2()
     87   cat("Part 2 solution: ", part2(puzzle_input), "\n")
     88 }
     89 
     90 main()