Coding,  R

Advent Of Code 2021

This was my first year trying Advent of Code. I originally intended to do all the solutions in both R and python. However, as they got more complicated, I decided to prioritize R, sticking with the tidyverse as much as possible. I put all of the RMarkdown and Jupyter notebooks in a public git repo.

Overall, it was a great learning experience. I didn't finish all of the days, both because of travel and because of difficulties working with 3D matrices in tidyverse.

I think the best part was finally have an excuse to work with classes in R. Obviously, R's approach is very different from python, but you can still get a lot of the same functionality.

For instance, on Day 4, you have to keep track of a collection of Bingo game boards, and determine when a board obtains a Bingo. I created a new class called "Board" along with methods for marking a board when a number was drawn, checking a board to see if it had a Bingo, and scoring a board according to the AoC rules.

Here's the code I ended up using:

# read in list of draws
require(tidyverse)
draws <- read_csv('data/aoc-4_draws.txt', col_names=FALSE)
draws <- draws %>%
  pivot_longer(everything(), names_to = 'Draw', values_to = 'Number')

# read in boards data
boards <- read_delim('data/aoc-4_boards.txt', delim=" ",
                     col_names=FALSE)
n_board <- nrow(boards)/5
boards$board <- rep(1:n_board, each=5)
boards <- boards %>%
  mutate_if(is.character, as.numeric) 

# create list to store all of the boards
B <- vector(mode = 'list', length = n_board)
hits <- matrix(rep(0,25), nrow=5)
for (i in 1:n_board) {
  b <- boards %>% 
    filter(board == i) %>%
    select(-board)
  bd <- list('bid' = i, 'board' = b, 'hits' = hits, 'score' = 0)
  class(bd) <- 'Board'
  B[[i]] <- bd
}

# prep methods for class Board
mark <- function(object, ...) {
  UseMethod('mark')
}
check <- function(object) {
  UseMethod('check')
}
score <- function(object, ...) {
  UseMethod('score')
}
draw <- function(object, ...) {
  UseMethod('draw')
}

# define methods of class Board
mark.Board <- function(object, num) {
  # check for the given number and add it to hits if it exists
  found <- FALSE
  row <- NULL
  col <- NULL
  for (i in 1:5) {
    if (num %in% object$board[i,]) {
      row <- i
      for (j in 1:5) {
        if (num == object$board[i,j]) {
          col <- j
          found <- TRUE
        }
      }
    }
  }
  if (found) {
    object$hits[row,col] <- 1
  }

  return(object)
}
check.Board <- function(object) {
  # Does Board have a bingo?
  bingo <- FALSE
  # check rows
  if (any(rowSums(object$hits) == 5)) {
    bingo <- TRUE
  } else if (any(colSums(object$hits) == 5)) {
    bingo <- TRUE
  }
  return(bingo)
}
score.Board <- function(object, num) {
  # sum unmarked numbers and multiply by last pulled number
  object$score <- num * sum(object$board[object$hits == 0])
  return(object)
}
draw.Board <- function(object, num) {
  # update a Board based on the given draw number
  object <- mark(object, num)
  bingo <- check(object)
  if (bingo) {
    object <- score(object, num)
  }
  return(object)
}

# Loop through draws until a board wins
all_boards <- B
winner <- FALSE
for (ii in 1:nrow(draws)) {
  pull <- draws[ii,2]
  for (bi in 1:n_board) {
    bd <- draw(all_boards[[bi]], pull)
    if (bd$score > 0) {
      print(str_c('Board ', bd$bid, ' won! Pull ', pull, ', score = ', bd$score))
      winner <- TRUE
    }
    all_boards[[bi]] <- bd
  }
  if (winner) break
}