# Libraries
library(tidyverse)
library(igraph)
# Read input from file into a data frame
<- read_table("../input/day12.txt", col_names = "chr") |>
input mutate(
row = row_number(),
chr = str_split(chr, "")
|>
) unnest(chr) |>
mutate(col = row_number(), .by = row) |>
mutate(idx = row_number(), .before = everything())
Day 12
Advent of Code: Worked Solutions
Setup
Part 1
Format the input as a graph, with edges connecting neighbors of the same type:
# Flag neighboring characters of the same value that border one other
<- input |>
edges_wide mutate(v = case_when(row + 1 == lead(row) ~ lead(idx)), .by = c(chr, col)) |>
mutate(h = case_when(col + 1 == lead(col) ~ lead(idx)), .by = c(chr, row))
<- edges_wide |>
edges_long pivot_longer(
c(v, h),
names_to = NULL,
values_to = "target",
values_drop_na = TRUE
)
# Format neighbors as a list of edges and add to add a graph
<- edges_long |>
g transmute(
edge_id = row_number(),
src = idx,
target|>
) pivot_longer(c(src, target)) |>
arrange(edge_id, value) |>
pull(value) |>
make_graph(n = nrow(input), directed = FALSE)
V(g)$name <- 1:nrow(input)
# Separate out the resulting graph into sub-graphs of innerconnected regions
<- decompose(g) dg
Compute the perimeter, area, and cost of each subgraph then sum the total:
|>
dg map_int(\(subgraph) {
<- sum(4 - degree(subgraph))
perim <- gorder(subgraph)
area * area
perim |>
}) sum()
[1] 1433460
Part 2
Used a hint from reddit: the number of corners is equal to the number of sides.
A plot can have a convex corner or a concave corner.
- A cell has a convex corner for each pair of adjacent borders
- A cell has a concave corner if it has two adjacent cells of its same group, but its diagonal cell between the two has a different group.
# Get original row/column input and join on the group output from the graph
<- left_join(
groups
input,imap_dfr(dg, \(g, grp_idx) tibble(grp = grp_idx, idx = V(g)$name)),
join_by(idx)
|>
) select(idx, grp, row, col)
# For each of a cell's neighbors, flag if they're in the same group
<- groups |>
neighbors # Get group number of each adjacent cell (N/S/E/W)
left_join(transmute(groups, n = grp, row = row + 1, col), join_by(row, col)) |>
left_join(transmute(groups, w = grp, col = col + 1, row), join_by(row, col)) |>
left_join(transmute(groups, s = grp, row = row - 1, col), join_by(row, col)) |>
left_join(transmute(groups, e = grp, col = col - 1, row), join_by(row, col)) |>
# Get group number of each diagonal cell (NW/NE/SW/SE)
left_join(transmute(groups, nw = grp, row = row + 1, col = col + 1), join_by(row, col)) |>
left_join(transmute(groups, ne = grp, row = row + 1, col = col - 1), join_by(row, col)) |>
left_join(transmute(groups, sw = grp, row = row - 1, col = col + 1), join_by(row, col)) |>
left_join(transmute(groups, se = grp, row = row - 1, col = col - 1), join_by(row, col)) |>
select(-c(row, col)) |>
# Compare group numbers of adjacent/diagonal cells to the current cell
mutate(across(c(n, w, s, e, nw, ne, sw, se), ~ replace_na(.x == grp, FALSE)))
# Compute total number of concave/convex corners for each cell
<- neighbors |>
corners mutate(
convex = (!n & !w) + (!s & !w) + (!s & !e) + (!n & !e),
concave = (n & w & !nw) + (s & w & !sw) + (s & e & !se) + (n & e & !ne)
)
Total the number of corners per group and multiply by the group’s area to get the total cost:
|>
corners summarize(
area = n(),
num_sides = sum(convex + concave),
.by = grp
|>
) mutate(cost = area * num_sides) |>
pull(cost) |>
sum()
[1] 855082