# Libraries
library(tidyverse)
# Read input from file
<- read_lines("../input/day03.txt", skip_empty_rows = FALSE) input
Day 3
Advent of Code: Worked Solutions
Setup
Part 1
Convert the input strings into a character matrix for ease of indexing:
<- input |>
mtx str_split("") |>
unlist() |>
matrix(nrow = length(input), byrow = TRUE)
<- nrow(mtx)
max_row <- ncol(mtx) max_col
Identify all candidates for part numbers and their coordinates in the matrix representation of the input:
# Pull all candidates from the text input
<- input |>
parts str_extract_all("\\d+") |>
map(unique)
# Compute the indices of each candidate within each input string
<- map2(input, parts, \(chr_row, row_parts) {
parts_locations |>
chr_row str_locate_all(str_c("\\b", row_parts, "\\b")) |>
map(partial(asplit, MARGIN = 1))
})
Identify all neighbors of each part number candidate:
<- parts_locations |>
parts_df
# Combine all candidates and their indices into a data frame of coordiantes
enframe(name = "row") |>
unnest_longer(value, values_to = "locations", indices_to = "part_id") |>
unnest_longer(locations, values_to = "col_seq", indices_to = "loc_id") |>
mutate(col_seq = map(col_seq, partial(full_seq, period = 1))) |>
left_join(
|>
parts enframe(name = "row") |>
unnest_longer(
value, values_to = "part_int",
indices_to = "part_id",
transform = as.integer
),join_by(row, part_id)
|>
) mutate(part_id = cur_group_id(), .by = c(row, part_id, loc_id)) |>
# Pull the neighboring characters for every part in the input
mutate(
search_rows = map(row, \(row) {
c(row - 1, row, row + 1) |>
keep(partial(between, left = 1, right = max_row))
}),search_cols = map(col_seq, \(col_seq) {
c(min(col_seq) - 1, col_seq, max(col_seq) + 1) |>
keep(partial(between, left = 1, right = max_row))
}),neighbor = map2(search_rows, search_cols, \(rows, cols) {
expand_grid(row = rows, col = cols) |>
mutate(chr = map2_chr(row, col, \(row, col) mtx[row, col]))
})|>
) unnest(neighbor, names_sep = "_") |>
# Simplify into a list of part IDs, integer values, and neighboring chars
select(row, part_id, part_int, starts_with("neighbor")) |>
mutate(
neighbor_id = cur_group_id(),
.by = c(neighbor_row, neighbor_col),
.before = neighbor_chr
)
Pull all candidates that are true part numbers (have a neighboring symbol) and sum their values:
|>
parts_df filter(!str_detect(neighbor_chr, "[0-9.]")) |>
distinct(part_id, part_int) |>
pull(part_int) |>
sum()
[1] 522726
Part 2
Pull symbols with exactly two adjacent parts, multiply the part integers to get each gear ratio, and sum the total of the gear ratios:
|>
parts_df filter(!str_detect(neighbor_chr, "[0-9.]")) |>
filter(n() == 2, .by = neighbor_id) |>
summarize(gear_ratio = prod(part_int), .by = neighbor_id) |>
pull(gear_ratio) |>
sum()
[1] 81721933