04: Ceres Search

library(mistlecode)

options(scipen = 999)
dt <-
  readLines("input.txt") |> 
  strsplit('')

dt <-
  dt |>
  unlist() |>
  matrix(ncol = length(dt[[1]]), byrow = TRUE)

Part 1

I’m disappointed in myself. I really should have a function to parse input like this by now, but again I sat on my butt and didn’t make one then struggled for it. I also got super duper stuck when my mistlecode::get_diagonal() wasn’t able to pick up diagonals that start in the lower right corner. It’s something I’ll have to go fix. I’ll probably re-do the whole thing to just say ‘top-left’ or ‘bottom-right’ or something to actually say what corner it’s coming off of rather than trying to say how it’s divided. Anyways, I knew I had overlapping diagonals and needed to drop them, but I was dropping them inside count_diag() which wasn’t doing anything except dropping a letter off the diagonals. Once I moved that outside, I was able to recognize my missing corner.

count <- function(x) {
  x |>
    paste0(collapse = '') |>
    mistlecode::strg_extract_all('XMAS|SAMX') |>
    unlist() |>
    length()
}

count_diag <- function(x, dim, dir) {
  x |>
    purrr::map_int(\(x) {
      mistlecode::get_diagonal(dt, x, dim, dir) |>
        count()
    }) |>
    sum()
}

rows <- dt |> apply(1, count) |> sum()
cols <- dt |> apply(2, count) |> sum()

drow <- dt |> nrow() |> seq_len() |> count_diag('row', 'down')
dcol <- dt |> ncol() |> seq_len() |> tail(-1) |> count_diag('col', 'down')

urow <-
  dt |> rev() |> `dim<-`(rev(dim(dt))) |>
  nrow() |> seq_len() |> count_diag('row', 'up')
ucol <-
  dt |>  rev() |> `dim<-`(rev(dim(dt))) |>
  ncol() |> seq_len() |> tail(-1) |> count_diag('col', 'up')

rows + cols + drow + dcol + urow + ucol
[1] 2524

Part 2

Thankfully this was much easier. It was a small struggle adapting my count() which I really didn’t need to do if I hadn’t been so eager to paste0() in my for loop. But if it works, it works, and it was much much faster than part 1.

dt[dt == 'X'] <- ''

count <- function(x) {
  x |>
    stringr::str_count('MAS|SAM') |>
    sum()
}

tally <- logical()
for (r in seq_len(nrow(dt) - 2)) {
  for (c in seq_len(ncol(dt) - 2)) {
    x <- dt[r:(r+2), c:(c+2)]
    if (x[2, 2] != 'A') next
    left_right <- paste0(c(x[1,1], x[2,2], x[3,3]), collapse = '')
    right_left <- paste0(c(x[1,3], x[2,2], x[3,1]), collapse = '')
    valid <- count(c(left_right, right_left)) == 2
    if (valid) tally <- c(tally, valid)
  }
}
tally |>
  sum()
[1] 1873