Run
aoc_source(day = 7, part = 1)
input = aoc_read(day = 7)
aoc_run(solve_day7_part1(input))
Elapsed: 0.796 seconds
Memory: 116 KB
Day 1 Day 2 Day 3 Day 4 Day 5 Day 6 Day 7 Day 8 Day 9 Day 10 Day 11 Day 12 Day 13 Day 14 Day 15 Day 16
My part 2 is slow; I threw in some parellelisation and called it a day.
#' Find the total of valid calibrations
#'
#' Extract the target number as the first number on each line. Check if the
#' following sequence can total the target number, applying operators `+` or `*`
#' left-to-right in pairs. Sum the target numbers of all valid lines.
#'
#' @param input
#' Character vector of input lines with a target number followed by a sequence
#' of numbers to test against the target.
#'
#' @return
#' numeric(1) The total of targets that can be met
solve_day7_part1 <- function(input) {
lines <- strsplit(input, ": | ")
targets <- vapply(lines, \(line) line[[1]], character(1))
targets <- as.numeric(targets)
nums <- lapply(lines, \(line) line[-1])
possible <- Map(is_possible, targets, nums)
sum(targets[as.logical(possible)])
}
#' Check if a sequence of numbers can meet a target
#'
#' Uses recursion to follow branches of applying one of a choice of operators
#' to sequential pairs of numbers. If at any point we exceed the target number,
#' returns FALSE. Once we have worked through the sequence to get a single
#' number. Return whether that number is the target number.
#'
#' The `operator`s used are `+` and `*`
#'
#' @param target
#' numeric(1) Target to match.
#' @param nums
#' Sequence of numbers to operate on.
#' @param operator
#' Function which takes 2 arguments to apply to the first 2 numbers in `nums`
#' ie the next operator to try. When NULL (default), the function just calls
#' itself with each operator.
#'
#' @return
#' logical(1) Whether the target can be met with the given sequence of numbers.
is_possible <- function(target, nums, operator = NULL) {
nums <- as.numeric(nums)
if (length(nums) == 1) {
return(nums == target)
}
if (!is.null(operator)) {
new_num <- as.numeric(operator(nums[[1]], nums[[2]]))
# prune
if(new_num > target) {
return(FALSE)
}
nums <- c(new_num, nums[-c(1, 2)])
}
# try each operator
is_possible(target, nums, `+`) ||
is_possible(target, nums, `*`)
}
aoc_source(day = 7, part = 1)
input = aoc_read(day = 7)
aoc_run(solve_day7_part1(input))
Elapsed: 0.796 seconds
Memory: 116 KB
#' Find the total of valid calibrations
#'
#' Extract the target number as the first number on each line. Check if the
#' following sequence can total the target number, applying operators `+`, `*`
#' or concatenation left-to-right in pairs. Sum the target numbers of all valid
#' lines.
#'
#' @param input
#' Character vector of input lines with a target number followed by a sequence
#' of numbers to test against the target.
#'
#' @return
#' numeric(1) The total of targets that can be met
solve_day7_part2 <- function(input) {
lines <- strsplit(input, ": | ")
targets <- vapply(lines, \(line) line[[1]], character(1))
targets <- as.numeric(targets)
nums <- lapply(lines, \(line) line[-1])
# Parellelise
future::plan(future::multisession, workers = 3)
possible <- furrr::future_map2_lgl(targets, nums, is_possible)
sum(targets[possible])
}
#' Check if a sequence of numbers can meet a target
#'
#' Uses recursion to follow branches of applying one of a choice of operators
#' to sequential pairs of numbers. If at any point we exceed the target number,
#' returns FALSE. Once we have worked through the sequence to get a single
#' number. Return whether that number is the target number.
#'
#' The `operator`s used are `+`, `*` and `paste0`.
#'
#' @param target
#' numeric(1) Target to match.
#' @param nums
#' Sequence of numbers to operate on.
#' @param operator
#' Function which takes 2 arguments to apply to the first 2 numbers in `nums`
#' ie the next operator to try. When NULL (default), the function just calls
#' itself with each operator.
#'
#' @return
#' logical(1) Whether the target can be met with the given sequence of numbers.
is_possible <- function(target, nums, operator = NULL) {
nums <- as.numeric(nums)
if (length(nums) == 1) {
return(nums == target)
}
if (!is.null(operator)) {
new_num <- as.numeric(operator(nums[[1]], nums[[2]]))
# prune
if(new_num > target) {
return(FALSE)
}
nums <- c(new_num, nums[-c(1, 2)])
}
is_possible(target, nums, `+`) ||
is_possible(target, nums, `*`) ||
is_possible(target, nums, paste0)
}
aoc_source(day = 7, part = 2)
input = aoc_read(day = 7)
aoc_run(solve_day7_part2(input))
Elapsed: 22.332 seconds
Memory: 12436 KB