base R
strings
regex
⭐⭐
Author

Ella Kaye

Published

December 2, 2023

Setup

The original challenge

My data

Part 1

Toggle the code
library(aochelpers)
input <- aoc_input_vector(2, 2023)
head(input)
[1] "Game 1: 4 blue, 7 red, 5 green; 3 blue, 4 red, 16 green; 3 red, 11 green"                                                            
[2] "Game 2: 20 blue, 8 red, 1 green; 1 blue, 2 green, 8 red; 9 red, 4 green, 18 blue; 2 green, 7 red, 2 blue; 10 blue, 2 red, 5 green"   
[3] "Game 3: 2 red, 5 green, 1 blue; 3 blue, 5 green; 8 blue, 13 green, 2 red; 9 green, 3 blue; 12 green, 13 blue; 3 green, 3 blue, 1 red"
[4] "Game 4: 1 red, 6 green, 4 blue; 3 green, 1 blue, 1 red; 7 blue, 1 red, 2 green"                                                      
[5] "Game 5: 2 green, 9 blue, 1 red; 3 green, 1 blue, 3 red; 1 red, 4 blue, 9 green"                                                      
[6] "Game 6: 2 blue, 5 red, 7 green; 5 blue, 8 red, 3 green; 2 red, 9 blue, 2 green"                                                      
The crux of the puzzle

A game is possible if, in every draw, there are no more than 12 red cubes, 13 green cubes and 14 blue cubes. Find the sum of the IDs of the possible games.

This input looks like it might be well-suited to a tidyverse approach, but I decided to stick with base R plus stringr, solving the challenge via a series of custom functions.

First, we need, in a couple of places, to extract the first number from a string:

Toggle the code
library(stringr)
extract_first_num <- function(x) {
  x |> 
    str_extract("\\d+") |> 
    as.numeric()
}

From a row of input, we next want a vector of the draws, i.e. not the game ID, and the string separated at the ";":

Toggle the code
get_draws <- function(game) {
  game |> 
    str_split(":|;") |> 
    unlist() |> 
    tail(-1) # discard the game ID
}

From a draw, for each colour, we want to know the number of balls drawn:

Toggle the code
colour_val <- function(draw, colour) {
  # separate colours
  split_draw <- str_split(draw, ",") |> unlist() 

  # get value for the color
  num <- split_draw[str_detect(split_draw, colour)] |> 
    extract_first_num() |> 
    as.numeric()

  # deal with 0-length vector if colour doesn't appear in draw
  ifelse((length(num) == 1), num, 0) 
}

Now we can determine if a draw is possible:

Toggle the code
draw_possible <- function(draw) {
  if (colour_val(draw, "red") > 12 
      || colour_val(draw, "green") > 13 
      || colour_val(draw, "blue") > 14) {
    FALSE
    } else {
    TRUE
  }
} 

A game is possible if all draws are possible. For a possible game, we want a function that returns the game ID. Since we’ll be summing over these, we return 0 if the game is not possible.

Toggle the code
game_possible <- function(game) {
  game_ID <- extract_first_num(game)

  draws <- get_draws(game)

  all_possible <- draws |> 
    sapply(draw_possible) |> 
    all()

    ifelse(all_possible, game_ID, 0)
}

Finally, we check all games for whether they’re possible, and find the sum of the IDs:

Toggle the code
input |> 
  sapply(game_possible) |> 
  sum()
[1] 2512

Part 2

The crux of the puzzle

What is the fewest number of cubes of each colour that could have been in the bag to make the game possible? Multiply these together for each game (the power), then sum the power across all games.

Some of the functions written for Part 1 come in handy here. We need to look across all the draws in a game and find the max of each colour; that is the fewest of each colour we need.

Toggle the code
fewest_of_colour <- function(draws, colour) {
  draws |> 
    sapply(colour_val, colour) |> 
    max()
}

To find the power of a set of cubes:

Toggle the code
power_cubes <- function(draws) {
  red <- fewest_of_colour(draws, "red")
  blue <- fewest_of_colour(draws, "blue")
  green <- fewest_of_colour(draws, "green")

  red * blue * green
}

Putting these together, starting with the full input:

Toggle the code
input |> 
  sapply(get_draws) |> 
  sapply(power_cubes) |> 
  sum()
[1] 67335

I completed Day 2 without any recourse to LLMs.

Session info

Toggle
─ Session info ───────────────────────────────────────────────────────────────
 setting  value
 version  R version 4.3.2 (2023-10-31)
 os       macOS Sonoma 14.1
 system   aarch64, darwin20
 ui       X11
 language (EN)
 collate  en_US.UTF-8
 ctype    en_US.UTF-8
 tz       Europe/London
 date     2023-12-06
 pandoc   3.1.1 @ /Applications/RStudio.app/Contents/Resources/app/quarto/bin/tools/ (via rmarkdown)
 quarto   1.4.515 @ /usr/local/bin/quarto

─ Packages ───────────────────────────────────────────────────────────────────
 package     * version    date (UTC) lib source
 aochelpers  * 0.1.0.9000 2023-12-06 [1] local
 sessioninfo * 1.2.2      2021-12-06 [1] CRAN (R 4.3.0)
 stringr     * 1.5.1      2023-11-14 [1] CRAN (R 4.3.1)

 [1] /Users/ellakaye/Library/R/arm64/4.3/library
 [2] /Library/Frameworks/R.framework/Versions/4.3-arm64/Resources/library

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