08: Haunted Wasteland

library(mistlecode)

options(scipen = 999)
file <- 'input.txt'

dir <- 
  file |>
  readLines(n = 1) |>
  stringr::str_split_1('')
  
dt <-
  file |>
  readr::read_csv(skip = 2, trim_ws = TRUE, col_names = FALSE) |>
  tidyr::separate(X1, into = c('start', 'L'), sep = ' = \\(') |>
  dplyr::mutate('R' = stringr::str_remove_all(.data$X2, '\\)')) |>
  dplyr::select(-'X2')
Rows: 718 Columns: 2
── Column specification ────────────────────────────────────────────────────────
Delimiter: ","
chr (2): X1, X2

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.

Part 1

I have evidence that I struggled some other time, but I have no recollection. This was pretty easy, just had an off-by-one to deal with to get around the modulo of length sometimes being zero.

start <- function(end, dir) {
  dt[[dir]][dt$start == end]
}

count <- 0
end <- 'AAA'
repeat {
  end <- start(end, dir[(count %% length(dir)) + 1])
  if (end == 'ZZZ') break else count <- count + 1
}
count + 1
[1] 17141

Part 2

I hate when there’s a fancy math way. It’s just least common multiples this time and I’m super dumb for not thinking of this sooner. I did have to be careful with match instead of %in% because that would return the indices in the incorrect order and cause everything to fail.

start <- function(end, dir) {
  dt[[dir]][match(end, dt$start)]
}

count <- 0
end <-
  dt |>
    dplyr::filter(grepl('A$', .data$start)) |>
    dplyr::pull(.data$start)
targets <-
  dt |>
    dplyr::filter(grepl('Z$', .data$start)) |>
    dplyr::pull(.data$start)
target_pos <- integer(length(end))
repeat {
  end <- start(end, dir[(count %% length(dir)) + 1])
  if (any(end %in% targets)) {
    target_pos[end %in% targets] <- count
    if (!any(target_pos == 0)) break
  }
  count <- count + 1
}
cheapr::scm(target_pos + 1)
[1] 10818234074807