# Libraries
library(tidyverse)
# Read input from file
<- read_lines("../input/day23.txt", skip_empty_rows = TRUE) |>
input enframe(name = "row") |>
mutate(value = map(value, ~ enframe(str_split_1(.x, ""), name = "col"))) |>
unnest(value)
Day 23
Advent of Code: Worked Solutions
Setup
Part 1
Convert map to a list of positions for each elf, using complex numbers to store 2D coords:
<- input |>
elves filter(value == "#") |>
mutate(z = complex(real = col, imaginary = row)) |>
pull(z)
Define function to move elves in each round:
<- c(
cardinal_dirs n = 0 - 1i,
s = 0 + 1i,
w = -1 + 0i,
e = 1 + 0i,
nw = -1 - 1i,
ne = 1 - 1i,
sw = -1 + 1i,
se = 1 + 1i
)
<- list(
adjacent_dirs n = c("n", "nw", "ne"),
s = c("s", "sw", "se"),
w = c("w", "nw", "sw"),
e = c("e", "ne", "se")
)
<- function(elves, round_num) {
move_elves
# Determine which neighboring cells are occupied
<- map(cardinal_dirs, \(dir) elves + dir)
neighbors <- map(neighbors, ~ .x %in% elves)
occupied
# Determine which n/s/w/e moves are valid
<- map(adjacent_dirs, \(dir_set) {
valid |>
dir_set map(~!occupied[[.x]]) |>
reduce(`&`)
})
# Re-order the n/s/w/e priority according to the current round number
<- valid[(1:4 + round_num - 2) %% 4 + 1]
valid
# For all elves not surrounded by empty cells, determine their proposed move
<- reduce(valid, `&`)
all_borders_empty <- valid |>
proposals imap(\(vec, dir) case_when(vec ~ neighbors[[dir]])) |>
pmap(
~ c(discard(c(..1, ..2, ..3, ..4), is.na), NA) |>
head(1)
|>
) unlist() |>
modify_if(all_borders_empty, ~ NA)
# Nullify any colliding proposed moves
<- na.omit(proposals)[duplicated(na.omit(proposals))]
collisions <- if_else(proposals %in% collisions, NA, proposals)
movements
# Return the new elf coordinates
coalesce(movements, elves)
}
Define a function to compute the area of the bounding box then subtract away the number of elves:
<- function(elves) {
n_empty_tiles <- 1 + diff(range(Im(elves)))
height <- 1 + diff(range(Re(elves)))
width
* width - length(elves)
height }
Run 10 rounds on puzzle input:
reduce(1:10, move_elves, .init = elves) |>
n_empty_tiles()
[1] 3996
Part 2
Run until no further movements occur:
<- 1
i <- elves
cur_elves
repeat {
<- move_elves(cur_elves, i)
new_elves
if (all(new_elves == cur_elves)) break
<- new_elves
cur_elves <- i + 1
i
}
i
[1] 908