# Libraries
library(tidyverse)
library(unglue)
# Read input from file
<- read_lines("../input/day12.txt", skip_empty_rows = TRUE) input
Day 12
Advent of Code: Worked Solutions
Setup
Part 1
Convert text input to a dataframe. Identifiy the indices of each unknown value per row, and convert the numeric input into a regex pattern to test if a given spring arrangement is possible:
<- input |>
df unglue_data("{chr} {num}") |>
mutate(
regex_pattern = str_c(
"^\\.*#{",
str_replace_all(num, ",", "}\\.+#{"),
"}\\.*$"
),u_idx = map(chr, ~ which(str_split_1(.x, "") == "?")),
num = map(num, ~ as.integer(str_split_1(.x, ","))),
total_broken = map_int(num, sum),
cur_broken = str_count(chr, "#"),
num_b = total_broken - cur_broken
)
Define a function to compute the total number of possible arrangements of broken springs for a given row using regex:
<- function(u_idx, num_b) {
idx_permutations
if (num_b == 0) {
list(list(B = numeric(0), O = u_idx))
else {
} <- combn(length(u_idx), num_b)
permutations map(
1:ncol(permutations),
~ list(
B = u_idx[permutations[,.x]],
O = u_idx[-permutations[,.x]]
)
)
}
}
<- function(input, regex_pattern, u_idx, num_b) {
num_arragements <- idx_permutations(u_idx, num_b)
perms <- str_split_1(input, "")
vec
|>
perms map_chr(\(perm) {
|>
vec replace(perm$O, ".") |>
replace(perm$B, "#") |>
str_c(collapse = "")
|>
}) str_detect(regex_pattern) |>
sum()
}
Run on puzzle input:
|>
df pmap_int(\(chr, regex_pattern, u_idx, num_b, ...) {
num_arragements(chr, regex_pattern, u_idx, num_b)
|>
}) sum()
[1] 7674
Part 2
With hints from Reddit, re-define the arrangement-counting function using memoization and recursion to examine at each character:
<- memoise::memoise(function(str, grps, grp_count) {
count_arr
# Check for contradictions and/or the end of the input
if (length(grps) == 0)
return(if_else(grp_count == 0 & str_detect(str, "^(\\.|\\?)*$"), 1, 0))
else if (str == "")
return(0)
else if (grp_count > grps[1])
return(0)
# Initialize the total number of arrangements to 0 and get the current char
<- 0
total_arr <- str_sub(str, 1, 1)
cur_chr
# If the char is unknown, examine both of the two possible states
if (cur_chr == "?")
<- c(".", "#")
cur_chr
for (chr in cur_chr) {
# If '#', recurse within the current group
if (chr == "#")
<- total_arr + count_arr(str_sub(str, 2), grps, grp_count + 1)
total_arr
# If '.', close the current group (if one is active) & recurse
else if (chr == ".") {
if (grp_count == 0)
<- total_arr + count_arr(str_sub(str, 2), grps, grp_count)
total_arr else if (grp_count == grps[1])
<- total_arr + count_arr(str_sub(str, 2), tail(grps, -1), 0)
total_arr
}
}
total_arr })
Run on puzzle input:
|>
df pmap_dbl(\(chr, num, ...) {
count_arr(str_c(str_c(rep(chr, 5), collapse = "?"), "."), rep(num, 5), 0)
|>
}) sum() |>
format(scientific = FALSE)
[1] "4443895258186"