07: Handy Haversacks

library(mistlecode)
options(scipen = 999)

df <- 
  read.csv("input.csv") |>
  dplyr::mutate(dplyr::across(
      tidyselect::matches('child[2-4]C'), 
      \(x) ifelse(as.character(x) == " ", NA, x)
  ))

df$child2C[as.character(df$child2C) == " "] <- NA
df$child3C[as.character(df$child3C) == " "] <- NA
df$child4C[as.character(df$child4C) == " "] <- NA

Part 1

longerDF <- data.frame(parent = "", nChild = 0, cChild = "")

for(col in seq(3, 9, by = 2)) {
  for(r in 1:nrow(df)) {
    if(!is.na(df[r,col-1]))
      for(i in 1:df[r,col-1]) {
        longerDF <- rbind(longerDF, data.frame(parent = df[r,1], 
                                               nChild = df[r,col-1], 
                                               cChild = df[r,col]))
    }
  }
}
longerDF <- longerDF[-1,]
shinygoldDF <- 
  longerDF[longerDF$cChild == "shiny gold" | longerDF$parent == "shiny gold",]
level <- c("shiny gold")
oldRows <- 0

while(oldRows != nrow(shinygoldDF)) {
  oldRows <- nrow(shinygoldDF)
  level <- unlist(shinygoldDF$parent)
  shinygoldDF <-
    df[as.character(df$parent) %in% level |
         as.character(df$child1C) %in% level |
         as.character(df$child2C) %in% level |
         as.character(df$child3C) %in% level |
         as.character(df$child4C) %in% level, ]
}
nrow(shinygoldDF) - 1
[1] 164

Part 2

Holy crap. I actually did it. I had some help from ChatGPT because I really struggle with recursion like this where it can’t just be a for loop (and even with the help it took four years…). I think the key point for me here is that I really like to do early returns, and you can’t. The return must be at the bottom and explicit. I also tried really hard not to use a for loop, but that makes it a lot harder and more confusing to iterate through everything. I’m hoping I can learn from this and apply it to future recursion problems.

df <-
  'input.txt' |>
  readLines() |> 
  stringr::str_replace_all(' bags contain ', ',') |>
  stringr::str_remove_all('(bags?[\\. ]?)') |>
  stringr::str_split(' ?, ?') |>
  purrr::map(\(x) {
    x <-
      x |>
      stringr::str_match('([0-9]*) ?([a-z]+ [a-z]+)') |>
      tibble::as_tibble() |>
      dplyr::select(-1) |>
      dplyr::mutate('V2' = as.integer(.data$V2))
    x |>
      dplyr::filter(!is.na(.data$V2)) |>
      tidyr::uncount(weights = .data$V2) |>
      dplyr::pull(.data$V3) |>
      list() |>
      setNames(x$V3[1])
  }) |>
  unlist(recursive = FALSE)
Warning: The `x` argument of `as_tibble.matrix()` must have unique column names if
`.name_repair` is omitted as of tibble 2.0.0.
ℹ Using compatibility `.name_repair`.
dig <- function(df, bag, bags = c()) {
  contents <- 
    df[bag] |>
    unlist() |>
    unname()
  bags <- c(bags, bag)
  if (length(contents) == 0) return(bags)
  for (bag in contents) {
    bags <- dig(df, bag, bags)
  }
  return(bags)
}
df |>
  dig('shiny gold') |>
  tail(-1) |>
  length()
[1] 7872