2023: Day 2 - Cube Conundrum

dplyr
tidyr
Published

December 2, 2023

Setup

The original challenge

Code
library(aochelpers)
library(tidyr)
library(dplyr)
input <- aoc_input_data_frame(2, 2023)
head(input)
# A tibble: 6 × 2
  X1     X2                                                                     
  <chr>  <chr>                                                                  
1 Game 1 " 1 green, 4 blue; 1 blue, 2 green, 1 red; 1 red, 1 green, 2 blue; 1 g…
2 Game 2 " 2 blue, 2 red, 6 green; 1 red, 6 green, 7 blue; 10 green, 8 blue, 1 …
3 Game 3 " 6 green, 5 blue, 9 red; 4 blue, 1 green, 13 red; 9 green, 14 red, 1 …
4 Game 4 " 14 green, 3 blue, 16 red; 20 red; 4 green, 2 red, 1 blue; 10 blue, 1…
5 Game 5 " 5 green, 4 blue; 1 red, 3 blue, 2 green; 4 green, 2 red, 15 blue; 11…
6 Game 6 " 6 blue, 10 green; 2 red, 6 green, 2 blue; 4 red, 4 blue, 1 green; 2 …
Code
names(input) <- c("game", "set")

TLDR; Solutions

Part 1 ⭐

❓Which games would have been possible if the bag contained only 12 red cubes, 13 green cubes, and 14 blue cubes?

Code
set.results <- input |> 
    # get game numbers
    mutate(game = as.numeric(gsub("Game ","", game))) |>
    # split out the sets
    separate_wider_delim(set, delim = ";", names_sep = "", too_few = "align_start") |>
    # one row per set
    pivot_longer(contains("set"), names_to = "set", values_to = "cube") |>
    # split out the colors
    separate_wider_delim(cube, delim = ",", names_sep = "", too_few = "align_start") |>
    # one row per cube
    pivot_longer(cube1:cube3, names_to = "cube", values_to = "color") |>
    # cleanup
    mutate(color = trimws(color)) |>
    select(-cube) |> 
    na.omit() |>
    # split out number of colors now
    separate_wider_delim(color, delim = " ", names_sep = "", too_few = "align_start") |>
    mutate(color1 = as.numeric(color1)) |>
    # put colors into separate columns
    pivot_wider(names_from = color2, values_from = color1) |>
    replace_na(list(blue=0, red=0, green=0))

    set.results %>%
    mutate(validity = ifelse(red >12 | green > 13 | blue >14, 
                                                     "invalid", "valid")) |>
    # flag games with invalid combos
    group_by(game) |> summarize(n.invalid = sum(validity=="invalid")) |>
    # keep only valid games
    ungroup() |>
    filter(n.invalid ==0) |>
    # solution input is the sum of valid game numbers
    summarise(sum(game))
# A tibble: 1 × 1
  `sum(game)`
        <dbl>
1        2486

Part 2 ⭐⭐

❓What is the fewest number of cubes of each color that could have been in the bag to make the game possible?

Code
set.results |> 
    group_by(game) |>
    # find max number of each color per game
    summarize(min.green = max(green), 
                 min.red = max(red), 
                 min.blue = max(blue)) |>
    # calculate submission value
    mutate(power = min.green*min.red*min.blue) |>
    summarize(sum(power))
# A tibble: 1 × 1
  `sum(power)`
         <dbl>
1        87984

Walkthrough / Explainer

I was launched successfully to Snow Island, which has a noticeable lack of snow. While we’re walking I’m playing a game with a local Elf. “Guess how many cubes are in the bag”. Kinda..

Part 1

:::{.callout-exa icon=true} Example Data

Code
exa <- data.frame(input = 
        c("Game 1: 3 blue, 4 red; 1 red, 2 green, 6 blue; 2 green", 
            "Game 2: 1 blue, 2 green; 3 green, 4 blue, 1 red; 1 green, 1 blue",
        "Game 3: 8 green, 6 blue, 20 red; 5 blue, 4 red, 13 green; 5 green, 1 red",
        "Game 4: 1 green, 3 red, 6 blue; 3 green, 6 red; 3 green, 15 blue, 14 red",
        "Game 5: 6 red, 1 blue, 3 green; 2 blue, 1 red, 2 green"))

exa <- exa |> 
    separate_wider_delim(input, delim = ":", names = c("game", "set"))

:::

My solution I think is going to use a series of separates and pivoting to get the games as rows, colors on the columns and number of colors as values. Then I can compare each row with the total number of cubes to see which games were possible.

Get number of each color cube per game/set combo.

Code
    # get game numbers
set.results <- input |> 
    mutate(game = as.numeric(gsub("Game ","", game))) |>
    # split out the sets
    separate_wider_delim(set, delim = ";", names_sep = "", too_few = "align_start") |>
    # one row per set
    pivot_longer(contains("set"), names_to = "set", values_to = "cube") |>
    # split out the colors
    separate_wider_delim(cube, delim = ",", names_sep = "", too_few = "align_start") |>
    # one row per cube
    pivot_longer(cube1:cube3, names_to = "cube", values_to = "color") |>
    # cleanup
    mutate(color = trimws(color)) |>
    select(-cube) |> 
    na.omit() |>
    # split out number of colors now
    separate_wider_delim(color, delim = " ", names_sep = "", too_few = "align_start") |>
    mutate(color1 = as.numeric(color1)) |>
    # put colors into separate columns
    pivot_wider(names_from = color2, values_from = color1) |>
    replace_na(list(blue=0, red=0, green=0))

head(set.results)
# A tibble: 6 × 5
   game set   green  blue   red
  <dbl> <chr> <dbl> <dbl> <dbl>
1     1 set1      1     4     0
2     1 set2      2     1     1
3     1 set3      1     2     1
4     1 set4      1     0     1
5     1 set5      1     0     0
6     1 set6      1     1     1

Now compare each set result against the proposed pool of 12 red cubes, 13 green cubes, and 14 blue cubes.

Code
set.results %>%
    mutate(validity = ifelse(red >12 | green > 13 | blue >14, 
                                                     "invalid", "valid")) |>
    # flag games with invalid combos
    group_by(game) |> summarize(n.invalid = sum(validity=="invalid")) |>
    # keep only valid games
    ungroup() |>
    filter(n.invalid ==0) |>
    # solution input is the sum of valid game numbers
    summarise(sum(game))
# A tibble: 1 × 1
  `sum(game)`
        <dbl>
1        2486

Part 2

So I find out that much like California, they’re in a drought. So no water, no snow. So while we go check on the water, the elf asks another question about said game. What is the fewest number of cubes of each color that could have been in the bag to make the game possible?

Code
set.results |> 
    group_by(game) |>
    # find max number of each color per game
    summarize(min.green = max(green), 
                 min.red = max(red), 
                 min.blue = max(blue)) |>
    # calculate submission value
    mutate(power = min.green*min.red*min.blue) |>
    summarize(sum(power))
# A tibble: 1 × 1
  `sum(power)`
         <dbl>
1        87984

Right on the first try! Today’s challenge was relativly straight forward, and my approach to the first challenge made the second part very easy.

⭐⭐

Session info

Toggle
─ Session info ───────────────────────────────────────────────────────────────
 setting  value
 version  R version 4.3.1 (2023-06-16 ucrt)
 os       Windows 11 x64 (build 22000)
 system   x86_64, mingw32
 ui       RTerm
 language (EN)
 collate  English_United States.utf8
 ctype    English_United States.utf8
 tz       America/Los_Angeles
 date     2023-12-02
 pandoc   3.1.1 @ C:/Program Files/RStudio/resources/app/bin/quarto/bin/tools/ (via rmarkdown)

─ Packages ───────────────────────────────────────────────────────────────────
 package    * version    date (UTC) lib source
 aochelpers * 0.1.0.9000 2023-11-30 [1] Github (EllaKaye/aochelpers@c2afc01)
 dplyr      * 1.1.2      2023-04-20 [1] CRAN (R 4.3.1)
 tidyr      * 1.3.0      2023-01-24 [1] CRAN (R 4.3.1)

 [1] C:/Users/renta/AppData/Local/R/win-library/4.3
 [2] C:/Program Files/R/R-4.3.1/library

──────────────────────────────────────────────────────────────────────────────