R/day02.R

# techniques: split-apply-combine

#' Day 02: Inventory Management System
#'
#' [Inventory Management System](https://adventofcode.com/2018/day/2)
#'
#' @name day02
#' @rdname day02
#' @details
#'
#' **Part One**
#'
#' You stop falling through time, catch your breath, and check the screen
#' on the device. "Destination reached. Current Year: 1518. Current
#' Location: North Pole Utility Closet 83N10." You made it! Now, to find
#' those anomalies.
#'
#' Outside the utility closet, you hear footsteps and a voice. "...I'm not
#' sure either. But now that [so many people have
#' chimneys]{title="This is, in fact, roughly when chimneys became common in houses."},
#' maybe he could sneak in that way?" Another voice responds, "Actually,
#' we've been working on a new kind of *suit* that would let him fit
#' through tight spaces like that. But, I heard that a few days ago, they
#' lost the prototype fabric, the design plans, everything! Nobody on the
#' team can even seem to remember important details of the project!"
#'
#' "Wouldn't they have had enough fabric to fill several boxes in the
#' warehouse? They'd be stored together, so the box IDs should be similar.
#' Too bad it would take forever to search the warehouse for *two similar
#' box IDs*..." They walk too far away to hear any more.
#'
#' Late at night, you sneak to the warehouse - who knows what kinds of
#' paradoxes you could cause if you were discovered - and use your fancy
#' wrist device to quickly scan every box and produce a list of the likely
#' candidates (your puzzle input).
#'
#' To make sure you didn't miss any, you scan the likely candidate boxes
#' again, counting the number that have an ID containing *exactly two of
#' any letter* and then separately counting those with *exactly three of
#' any letter*. You can multiply those two counts together to get a
#' rudimentary [checksum](https://en.wikipedia.org/wiki/Checksum) and
#' compare it to what your device predicts.
#'
#' For example, if you see the following box IDs:
#'
#' -   `abcdef` contains no letters that appear exactly two or three times.
#' -   `bababc` contains two `a` and three `b`, so it counts for both.
#' -   `abbcde` contains two `b`, but no letter appears exactly three
#'     times.
#' -   `abcccd` contains three `c`, but no letter appears exactly two
#'     times.
#' -   `aabcdd` contains two `a` and two `d`, but it only counts once.
#' -   `abcdee` contains two `e`.
#' -   `ababab` contains three `a` and three `b`, but it only counts once.
#'
#' Of these box IDs, four of them contain a letter which appears exactly
#' twice, and three of them contain a letter which appears exactly three
#' times. Multiplying these together produces a checksum of `4 * 3 = 12`.
#'
#' *What is the checksum* for your list of box IDs?
#'
#' **Part Two**
#'
#' Confident that your list of box IDs is complete, you're ready to find
#' the boxes full of prototype fabric.
#'
#' The boxes will have IDs which differ by exactly one character at the
#' same position in both strings. For example, given the following box IDs:
#'
#'     abcde
#'     fghij
#'     klmno
#'     pqrst
#'     fguij
#'     axcye
#'     wvxyz
#'
#' The IDs `abcde` and `axcye` are close, but they differ by two characters
#' (the second and fourth). However, the IDs `fghij` and `fguij` differ by
#' exactly one character, the third (`h` and `u`). Those must be the
#' correct boxes.
#'
#' *What letters are common between the two correct box IDs?* (In the
#' example above, this is found by removing the differing character from
#' either ID, producing `fgij`.)
#'
#' @param x a character vector of box IDs to checksum
#' @return For Part One, `checksum_box_id(x)` returns the number of IDs with
#'   exactly 2 characters times number with exactly 3 characters. For Part Two,
#'   `find_one_character_neighbors(x)` the characters shared by the two items
#'   that share all but one character.
#' @export
#' @examples
#' x <- c("abcdef", "bababc", "abbcde", "abcccd",
#'        "aabcdd", "abcdee", "ababab")
#' checksum_box_id(x)
#' x <- c("abcde", "fghij", "klmno", "pqrst",
#'        "fguij", "axcye", "wvxyz")
#' find_one_character_neighbors(x)
checksum_box_id <- function(x) {
  char_counts <- x %>%
    purrr::map(str_tokenize) %>%
    purrr::map(table)

  have_exactly_three <- char_counts %>%
    purrr::keep(~ 3 %in% .x) %>%
    length()

  have_exactly_two <- char_counts %>%
    purrr::keep(~ 2 %in% .x) %>%
    length()

  have_exactly_three * have_exactly_two
}


#' @rdname day02
#' @export
find_one_character_neighbors <- function(x) {
  # Try dropping each character position. Look for duplicates on resulting
  # items.
  seq_len(nchar(x[1])) %>%
    purrr::map(~ check_for_one_character_neighbors(x, .x)) %>%
    purrr::flatten_chr()
}

check_for_one_character_neighbors <- function(x, position) {
  x %>%
    purrr::map(str_tokenize) %>%
    purrr::map(drop_element, position) %>%
    purrr::map_chr(paste0, collapse = "") %>%
    pull_duplicates()
}

drop_element <- function(xs, i) {
  xs[-i]
}

pull_duplicates <- function(xs) {
  xs[duplicated(xs)]
}

str_tokenize <- function(x) {
  unlist(strsplit(x, ""))
}
tjmahr/adventofcode18 documentation built on May 24, 2019, 4:10 p.m.