Day 24

Advent of Code: Worked Solutions

About
Date

December 24, 2022

Setup

# Libraries
library(tidyverse)

# Read input from file
input <- read_lines("../input/day24.txt", skip_empty_rows = TRUE)

Part 1

Convert input string to a matrix of characters:

w <- str_length(input[[1]])
h <- length(input)

mtx <- input |> 
  str_split("") |> 
  unlist() |> 
  matrix(nrow = h, byrow = TRUE)

Define helper functions to determine whether a map space is occupied at a given time. Hint from reddit user u/jaccomoc to scan rows/columns for blizzards at a certain time, rather than simulating the entire map at every time point:

# Define the constant start/end locations
init   <- c(row = 1, col = 2)
target <- c(row = h, col = w - 1)

# Wrap blizzards aroound the map edges
wrap_w <- function(idx) (idx - 2) %% (w - 2) + 2
wrap_h <- function(idx) (idx - 2) %% (h - 2) + 2

# Check whether a given cell is open at a given time
is_open <- function(idx, time) {
  row <- idx[[1]]
  col <- idx[[2]]
  mtx[row, wrap_w(col - time)] != '>' & mtx[wrap_h(row - time), col] != 'v' &
  mtx[row, wrap_w(col + time)] != '<' & mtx[wrap_h(row + time), col] != '^'
}

# Check whether a given cell is in the bounds of the map
in_bounds <- function(idx) {
  row <- idx[[1]]
  col <- idx[[2]]
  
  (between(row, 2, h - 1) & between(col, 2, w - 1)) | 
  all(idx == init) | all(idx == target)
}

Define a function that loops through time points and stops when the first elf reaches the target. Hint on use of “quantum elves” that die if standing on a blizzard spot from Reddit user u/SLiV9.

moves <- list(
  c(row =  1, col =  0),  # down
  c(row =  0, col =  1),  # right
  c(row = -1, col =  0),  # up
  c(row =  0, col = -1),  # left
  c(row =  0, col =  0)   # still
)

time_cross <- function(time_start, init, target) {
  
  cur_locations <- list(init)
  t <- time_start
  
  repeat {
    t <- t + 1

    cur_locations <- cur_locations |> 
      map(\(start) map(moves, \(move) start + move)) |> 
      flatten() |> 
      keep(in_bounds) |> 
      keep(partial(is_open, time = t)) |> 
      unique()
    
    if (list(target) %in% cur_locations) break
  }
  
  t
}

Run on puzzle input:

t1 <- time_cross(0, init, target)

t1
[1] 299

Part 2

Run the simulation again two more times, swapping the target and source locations, and starting where the last left off.

t2 <- time_cross(t1, target, init)
t3 <- time_cross(t2, init, target)

t3
[1] 899