2024

— R Day 8 Part 1 —

#' Find the number of unique anti node locations
#'
#' Iterate over the grid creating a lookup table of antenna locations against
#' their frequency (ie represented by the same character). For each frequency
#' find the antinodes they produce. Take the unique antinode locations across
#' all frequencies, then count the number that are in bounds.
#'
#' @param input
#' Character vector of rows in the antennas map
#'
#' @return
#' numeric(1) The number of many unique antinode locations
solve_day8_part1 <- function(input) {
  grid <- matrix(unlist(strsplit(input, "")), nrow = length(input), byrow = TRUE)
  nrows <- NROW(grid)
  ncols <- NCOL(grid)

  # lookup table
  antennas <- new.env(parent = emptyenv())

  # store each position for the same letter
  for (row in seq_len(NROW(grid))) {
    for (col in seq_len(NCOL(grid))) {
      frequency <- grid[row, col]

      if (frequency != ".") {
        antennas[[frequency]] <- c(antennas[[frequency]], list(c(row, col)))
      }
    }
  }

  # iterate over frequencies finding their antinodes
  antinodes <- eapply(antennas, find_antinodes)
  antinodes <- unique(unlist(antinodes, recursive = FALSE))

  inbounds <- !vapply(antinodes, out_of_bounds, logical(1), nrows, ncols)

  sum(inbounds)
}

#' Find all antinode locations for one frequency
#'
#' For each antenna before the last: find the distance between it and the
#' following antennas in the list in pairs. Move that distance away from each
#' node in the pair and you have the location of an antinode.
#'
#' Taking the distance as second position - first position, subtract the
#' distance from the first position and add it to the second.
#'
#' So for antennas at (3, 3) and (5, 5).
#' Distance = (5, 5) - (3, 3) = (2, 2)
#' Antinodes are (3, 3) - (2, 2) = (1, 1) and (5, 5) + (2, 2), = (7, 7)
#'
#' @param antennas
#' A list of numeric(2), every position of an antenna of one frequency
#'
#' @return
#' A list of numeric(2), every position of antinodes for the given `antennas`
find_antinodes <- function(antennas) {
  antinodes <- vector("list")

  # iterate first in pair
  for (i in 1:(length(antennas) - 1)) {
    # iterate second in pair
    for (j in (i+1):length(antennas)) {
      pos1 <- antennas[[i]]
      pos2 <- antennas[[j]]

      dist <- pos2 - pos1

      new_pos1 <- pos1 - dist
      new_pos2 <- pos2 + dist

      antinodes <- c(antinodes, list(new_pos1), list(new_pos2))
    }
  }

  antinodes
}

#' Check whether a position out of grid boundaries
#'
#' Given a position (row, col), return TRUE when either are less than 1 and
#' greater than their respective limits.
#'
#' @param pos
#' numeric(2) or list(numeric(2)) to check is within bounds
#' @param nrows
#' numeric(1) row limit
#' @param ncols
#' numeric(1) col limit
#'
#' @return
#' logical(1) Whether the position is out of bounds
out_of_bounds <- function(pos, nrows, ncols) {
  pos <- unlist(pos)
  pos[[1]] <= 0 || pos[[1]] > nrows ||
    pos[[2]] <= 0 || pos[[2]] > ncols
}
Run
aoc_source(day = 8, part = 1)

input = aoc_read(day = 8)

aoc_run(solve_day8_part1(input))
Elapsed: 0.002 seconds
Memory:  309 KB

— R Day 8 Part 2 —

#' Find the number of unique anti node locations
#'
#' Iterate over the grid creating a lookup table of antenna locations against
#' their frequency (ie represented by the same character). For each frequency
#' find the antinodes they produce. Count the unique antinode locations across
#' all frequencies.
#'
#' Bounds checking is done in `find_antinodes()`
#'
#' @param input
#' Character vector of rows in the antennas map
#'
#' @return
#' numeric(1) The number of many unique antinode locations
solve_day8_part2 <- function(input) {
  grid <- matrix(unlist(strsplit(input, "")), nrow = length(input), byrow = TRUE)
  nrows <- NROW(grid)
  ncols <- NCOL(grid)

  # lookup table
  antennas <- new.env(parent = emptyenv())

  # iterate over the grid
  # store each antenna position against its letter in the lookup table
  for (row in seq_len(NROW(grid))) {
    for (col in seq_len(NCOL(grid))) {
      frequency <- grid[row, col]

      if (frequency != ".") {
        antennas[[frequency]] <- c(antennas[[frequency]], list(c(row, col)))
      }
    }
  }

  antinodes <- eapply(antennas, find_antinodes, nrows, ncols)
  antinodes <- unique(unlist(antinodes, recursive = FALSE))

  length(antinodes)
}

#' Find all antinode locations for one frequency
#'
#' For each antenna before the last: find the distance between it and the
#' following antennas in the list in pairs. Move any multiple of that distance
#' away from each node in the pair and you have the location of an antinode, as
#' well as on the antenna positions themselves.
#'
#' Taking the distance as second position - first position, subtract the
#' distance from the first position and add it to the second.
#'
#' So for antennas at (3, 3) and (5, 5).
#' Distance = (5, 5) - (3, 3) = (2, 2)
#'
#' Antinodes are:
#' (3, 3)
#' (3, 3) - (2, 2) = (1, 1)
#' Next position in this direction would be out of bounds
#'
#' (5, 5)
#' (5, 5) + (2, 2) = (7, 7)
#' (7, 7) + (2, 2) = (9, 9)
#' (9, 9) + (2, 2) = (11, 11)
#' ... until out of bounds
#'
#' @param antennas
#' A list of numeric(2), every position of an antenna of one frequency
#'
#' @return
#' A list of numeric(2), every position of antinodes for the given `antennas`
find_antinodes <- function(antennas, nrows, ncols) {
  antinodes <- vector("list")

  # iterate first in pair
  for (i in 1:(length(antennas) - 1)) {
    # iterate second in pair
    for (j in (i+1):length(antennas)) {
      pos1 <- antennas[[i]]
      pos2 <- antennas[[j]]

      dist <- pos2 - pos1

      # moving away from the first antenna
      while (!out_of_bounds(pos1, nrows, ncols)) {
        antinodes <- c(antinodes, list(pos1))
        pos1 <- pos1 - dist
      }

      # moving away from the second antenna
      while (!out_of_bounds(pos2, nrows, ncols)) {
        antinodes <- c(antinodes, list(pos2))
        pos2 <- pos2 + dist
      }
    }
  }

  antinodes
}

#' Check whether a position out of grid boundaries
#'
#' Given a position (row, col), return TRUE when either are less than 1 and
#' greater than their respective limits.
#'
#' @param pos
#' numeric(2) or list(numeric(2)) to check is within bounds
#' @param nrows
#' numeric(1) row limit
#' @param ncols
#' numeric(1) col limit
#'
#' @return
#' logical(1) Whether the position is out of bounds
out_of_bounds <- function(pos, nrows, ncols) {
  pos <- unlist(pos)

  pos[[1]] <= 0 || pos[[1]] > nrows ||
    pos[[2]] <= 0 || pos[[2]] > ncols
}
Run
aoc_source(day = 8, part = 2)

input = aoc_read(day = 8)

aoc_run(solve_day8_part2(input))
Elapsed: 0.006 seconds
Memory:  659 KB
Back to top