library(tidyverse)
Day 18
Advent of Code: Worked Solutions
Setup
Import libraries:
Read input from file:
<- read_lines(file = "../input/day18.txt") input
Convert JSON-like input to “snailfish number” format: a flat, named vector whose names give the depth of the nesting:
<- function(nested, depth = 0) {
nested_to_sn if (is.integer(nested))
set_names(nested, depth)
else
map(nested, \(lst) nested_to_sn(lst, depth + 1)) |>
list_c()
}
<- input |>
sn map(~ jsonlite::fromJSON(.x, simplifyVector = FALSE)) |>
map(nested_to_sn)
Part 1
Define a function to explode the first applicable pair in a snailfish number:
<- function(x) {
explode_sn
<- which((names(x) == lead(names(x))) & names(x) > 4) |> head(1)
idx
# Return as-is if there are no numbers to explode
if (length(idx) == 0) return(x)
# Add the values of the exploding pair outward to the left and right
1:length(x) |>
case_match(
- 1 ~ x + lead(x),
idx + 2 ~ x + lag(x),
idx .default = x
|>
)
# Replace the exploded pair with 0 and reduce its depth
discard_at(idx) |>
modify_at(idx, ~ 0) |>
set_names(\(nm) {
case_match(
1:length(nm),
~ as.character(as.numeric(nm) - 1),
idx .default = nm
)
}) }
Define a function to split the first applicable pair in a snailfish number:
<- function(x) {
split_sn <- detect_index(x, ~ .x >= 10)
idx <- keep_at(x, idx)
val
# Return as-is if there are no numbers to split
if (idx == 0) return(x)
# Convert the value to a new pair at 1 level greater depth
<- c(floor(val / 2), ceiling(val / 2)) |>
pair set_names(\(nm) as.character(as.numeric(nm) + 1))
# Replace the old value with the new pair
c(head(x, idx - 1), pair, tail(x, -idx))
}
Define a function to reduce a snailfish number by iteravely exploding and splitting its contents:
<- function(x) {
reduce_sn <- x
prv
repeat {
<- explode_sn(x)
x if (identical(x, prv)) {
<- split_sn(x)
x if (identical(x, prv)) return(x)
}<- x
prv
} }
Define a function that adds two snailfish numbers by combining them as a new pair and then reducing them:
<- \(x, y) {
add_sn c(x, y) |>
set_names(\(nm) as.character(as.numeric(nm) + 1)) |>
reduce_sn()
}
Define a function to compute the magnitude of a snailfish number:
<- function(x) {
magnitude
# Iteratively replace the deepest pair with its magnitude
while (length(x) > 1) {
<- which(as.numeric(names(x)) == max(as.numeric(names(x)))) |> head(1)
idx <- (x[idx] * 3 + x[idx + 1] * 2) |>
mag set_names(\(nm) as.character(as.numeric(nm) - 1))
<- c(head(x, idx - 1), mag, tail(x, -(idx + 1)))
x
}
unname(x)
}
Add together all numbers from the input in order:
<- reduce(sn, add_sn) final_sn
Compute the magnitude of the result:
magnitude(final_sn)
Part 2
Produce all pairs of values from the input:
<- sn |>
df as_tibble_col(column_name = "sn1") |>
mutate(id1 = row_number())
<- df |>
pairs cross_join(rename(df, sn2 = sn1, id2 = id1)) |>
filter(id2 != id1)
Compute the maximum magnitude across all summed pairs:
|>
pairs pmap_dbl(\(sn1, sn2, ...) magnitude(add_sn(sn1, sn2))) |>
max()