library(tidyverse)
library(igraph)
Day 15
Advent of Code: Worked Solutions
Setup
Import libraries:
Read input from file into a row/column indexed data frame:
<- read_lines(file = "../input/day15.txt") |>
input str_split("") |>
imap_dfr(\(vec, idx) {
tibble(row = idx, col = 1:length(vec), risk = as.integer(vec))
|>
}) mutate(id = row_number(), .before = everything())
Part 1
Convert input into a directed graph. Define the edge weights to be the risk level of each target vertex.
<- function(df) {
df_to_graph
<- df |>
edges
# Define all neighbors and edge weights to the n/s/w/e of each vertex
mutate(n_target = lag(id), n_weight = lag(risk), .by = col) |>
mutate(s_target = lead(id), s_weight = lead(risk), .by = col) |>
mutate(w_target = lag(id), w_weight = lag(risk), .by = row) |>
mutate(e_target = lead(id), e_weight = lead(risk), .by = row) |>
# Convert to a list of formatted vertex pairs for igraph
pivot_longer(
starts_with(c("n_", "s_", "w_", "e_")),
names_to = ".value",
names_pattern = "._(.*)",
values_drop_na = TRUE
|>
) select(src = id, target, weight)
# Convert to a weighted graph
<- make_graph(list_c(map2(edges$src, edges$target, ~ c(.x, .y))))
g E(g)$weight <- edges$weight
return(g)
}
Find the path with the least total risk and compute its risk:
<- function(df) {
min_risk <- min(df$id)
vtx_start <- max(df$id)
vtx_end
<- df_to_graph(df)
graph <- shortest_paths(graph, from = vtx_start, to = vtx_end)$vpath[[1]]
path
$risk[path] |>
dftail(n = -1) |>
sum()
}
Run on puzzle input:
min_risk(input)
Part 2
Expand the input X5 in each direction:
<- max(input$row)
nrow <- max(input$col)
ncol
<- accumulate(
df_big 1:4,
mutate(df, col = col + ncol, risk = pmax(1, (risk + 1) %% 10)),
\(df, n) .init = input
|>
) bind_rows() |>
accumulate(
1:4,
mutate(df, row = row + nrow, risk = pmax(1, (risk + 1) %% 10)),
\(df, n) .init = _
|>
) bind_rows() |>
arrange(row, col) |>
mutate(id = row_number())
Re-run the minimum risk function on the expanded input:
min_risk(df_big)