# Libraries
library(tidyverse)
library(unglue)
library(sets)
# Read input from file
<- read_lines("../input/day19.txt", skip_empty_rows = FALSE) input
Day 19
Advent of Code: Worked Solutions
Setup
Part 1
Separate the input into rules and parts:
<- input |>
parts keep(~ str_starts(.x, "\\{")) |>
unglue_data(
"{x=[x],m=[m],a=[a],s=[s]}",
open = "[",
close = "]",
convert = TRUE
|>
) pmap(\(x, m, a, s) lst(x, m, a, s))
<- input |>
rules keep(~ str_starts(.x, "\\w")) |>
unglue_data("[name]{[rules]}", open = "[", close = "]") |>
mutate(rules = str_split(rules, ",")) |>
unnest_longer(rules, values_to = "condition", indices_to = "cond_num") |>
unglue_unnest(
condition, c("{var}{eq=[<>]}{val}:{goto}", "{goto}"),
convert = TRUE
|>
) mutate(value = pmap(
lst(var, eq, val, goto),
~ lst(var = ..1, eq = ..2, val = ..3, goto = ..4)
|>
)) summarize(value = list(value), .by = name) |>
deframe()
Define a function that rates a part according to the rule list for each workflow:
<- function(part, workflow = "in") {
rate if (workflow %in% c('A', 'R'))
return(workflow == 'A')
<- rules[[workflow]]
workflow
for (rule in workflow) {
if (is.na(rule$var))
return(rate(part, rule$goto))
if (get(rule$eq)(part[[rule$var]], rule$val))
return(rate(part, rule$goto))
} }
Rate the list of parts, then for the accepted parts, add all ratings:
keep(parts, map_lgl(parts, rate)) |>
unlist() |>
sum()
[1] 397643
Part 2
Define a function to cut the valid set of inputs into a set of intervals. Looping through our rules, trim down the intervals for x, m, a, and s until we have the final set of valid inputs:
<- set_names(c("x", "m", "a", "s"))
keys <- map(keys, ~ interval(domain = 'Z'))
empty <- map(keys, ~ interval(l = 1, r = 4000, domain = 'Z'))
init
<- function(cur_range, workflow = "in") {
rate_range
if (workflow == 'A')
return(list(cur_range))
if (workflow == 'R')
return(list())
<- rules[[workflow]]
workflow <- list()
accepted
for (rule in workflow) {
if (is.na(rule$var)) {
<- c(accepted, rate_range(cur_range, rule$goto))
accepted return(accepted)
}
<- sets::interval(
rule_range l = case_match(rule$eq, '<' ~ 1, '>' ~ rule$val + 1),
r = case_match(rule$eq, '<' ~ rule$val - 1, '>' ~ 4000),
domain = 'Z'
)
<- cur_range |>
rule_pass modify_at(rule$var, ~ interval_intersection(.x, rule_range))
<- cur_range |>
rule_fail modify_at(rule$var, ~ interval_complement(rule_range, .x))
<- c(accepted, rate_range(rule_pass, rule$goto))
accepted <- rule_fail
cur_range
} }
Run and compute the number of total valid combinations:
rate_range(init) |>
map(\(intrvl) map_dbl(intrvl, ~ length(as.set(.x)))) |>
map_dbl(prod) |>
sum() |>
format(scientific = FALSE)
[1] "132392981697081"