· 9 min read

Part 1

Read in our puzzle input:

day <- 7

input <- here::here(
  "2022", "input",
  paste0("day_", stringr::str_pad(day, 2, "left", "0"))
  ) |>
  readLines()



test_input <- c(
  "$ cd /",
  "$ ls",
  "dir a",
  "14848514 b.txt",
  "8504156 c.dat",
  "dir d",
  "$ cd a",
  "$ ls",
  "dir e",
  "29116 f",
  "2557 g",
  "62596 h.lst",
  "$ cd e",
  "$ ls",
  "584 i",
  "$ cd ..",
  "$ cd ..",
  "$ cd d",
  "$ ls",
  "4060174 j",
  "8033020 d.log",
  "5626152 d.ext",
  "7214296 k"
)

Some functions to help parse the input, breaking it up into blocks for each directory and summing the file sizes within each.

parse_dir <- function(latest_list, vec) {

  data_list <- latest_list[[1]]
  wd <- latest_list[[2]]

  wd <- change_wd(wd, vec[1])

  listing <- vec[-1]

  if (length(listing)) {
    file_names <- stringr::str_extract(listing, "(?<=[0-9]\\s)[a-z\\.]+$")
    file_sizes <- stringr::str_extract(listing, "^[0-9]+") |>
      as.numeric()
    assertthat::assert_that(length(file_sizes) == length(file_names), msg = "check 1")

    file_names <- purrr::discard(file_names, ~ is.na(.))
    file_sizes <- purrr::discard(file_sizes, ~ is.na(.))
    assertthat::assert_that(length(file_sizes) == length(file_names), msg = "check 2")

    if (length(file_sizes)) {
      total_file_size <- sum(file_sizes)
      names(file_sizes) <- file_names
    } else {
      total_file_size <- 0
      file_sizes <- character(0)
    }

    subdirs <- stringr::str_extract(listing, "(?<=^dir )[:lower:]+$")

    this_list <- list(
      list(
        size = total_file_size,
        files = file_sizes,
        subdirs = purrr::discard(subdirs, ~ is.na(.))
        )) |>
      rlang::set_names(wd)
    
    data_list <- purrr::list_merge(data_list, this_list)
  }
  list(data_list, wd)
}

change_wd <- function(wd, str) {
  new_wd <- stringr::str_extract(str, "(?<=\\$ cd )[:graph:]+")

  if (new_wd == "/") wd <- "root"
  else if (new_wd == "..") wd  <- stringr::str_extract(wd, ".*(?=/[:alnum:]+$)")
  else if (stringr::str_detect(new_wd, "[a-z]+")) wd <- paste(wd, new_wd, sep = "/")
  else stop(stringr::str_glue("change_wd: directory name '{wd}' not correctly parsed."))

  wd
}

batch_instructions <- function(vec) {
  vec <- stringr::str_c(vec, collapse = "|")
  vec <- stringr::str_split_1(vec, "\\|(?=\\$ cd)")
  vec |>
    purrr::map(~ stringr::str_split_1(., "\\|"))
}

Try to find the answer for part 1.

out <- input |>
  batch_instructions() |>
  purrr::reduce(.f = parse_dir, .init = list(data_list = list(), wd = NULL)) |>
  purrr::pluck(1, 1) |>
  purrr::map_dfc("size") |>
  tidyr::pivot_longer(cols = everything())

filter_and_sum <- function(x, dtf) {
  filtered_sum <- dtf |>
    dplyr::filter(stringr::str_starts(name, x)) |>
    dplyr::summarise(across(value, sum))
  dplyr::bind_cols(name = x, filtered_sum)
}

res <- out |>
  dplyr::pull(name) |>
  purrr::map_df(filter_and_sum, dtf = out) 

res |>
  dplyr::filter(value <= 100000) |>
  dplyr::pull(value) |>
  sum()

## [1] 1444896

Success! (At long, long last - I am not going to post the details!)

Part 2

Now we have correct data with the files rolled up correctly into folders, we should easily be able to solve part 2.

current_du <- res |>
  dplyr::filter(name == "root") |>
  dplyr::pull(value)

current_df <- 70000000 - current_du

space_needed <- 30000000 - current_df

res |>
  dplyr::filter(value >= space_needed) |>
  dplyr::slice_min(order_by = value, n = 1) |>
  dplyr::pull(value)

## [1] 404395

Success again!

This took me till the 20th December, for various reasons.

F
fran barton

Based on a template by rongying.