# Libraries
library(tidyverse)
library(unglue)
# Read input from file
<- read_lines("../input/day20.txt", skip_empty_rows = FALSE) input
Day 20
Advent of Code: Worked Solutions
Setup
Part 1
Convert text input into a nested list of modules:
<- input |>
df unglue_data("{prefix=(%|&)?}{name} -> {destinations}") |>
mutate(destinations = str_split(destinations, ",\\s*"))
<- df |>
sources unnest_longer(destinations) |>
summarize(sources = list(name), .by = destinations)
<- sources |>
blank_add anti_join(df, join_by(x$destinations == y$name)) |>
transmute(name = destinations, prefix = '')
<- df |>
modules left_join(sources, join_by(x$name == y$destinations)) |>
add_row(name = "button", prefix = '', destinations = list("broadcaster")) |>
bind_rows(blank_add) |>
mutate(
state = case_when(prefix == '%' ~ FALSE),
memories = case_when(
== '&' ~ map(sources, ~ set_names(rep(FALSE, length(.x)), .x))
prefix
),n_lo = 0,
n_hi = 0
|>
) transmute(
name,value = pmap(lst(prefix, destinations, state, memories, n_lo, n_hi), lst)
|>
) deframe()
Define a function that modifies a set of modules when the button is pushed:
<- function(modules) {
push_button <- list(list(source = "button", target = "broadcaster", pulse = FALSE))
queue $button$n_lo <- modules$button$n_lo + 1
modules
while (length(queue) > 0) {
<- queue[[1]]
signal <- queue[-1]
queue <- modules[[signal$target]]
module <- NULL
output
if (signal$target == "broadcaster") {
<- signal$pulse
output else if (module$prefix == '%' & signal$pulse == FALSE) {
} $target]]$state <- !module$state
modules[[signal<- !module$state
output else if (module$prefix == '&') {
} $target]]$memories[[signal$source]] <- signal$pulse
modules[[signal<- !all(modules[[signal$target]]$memories == TRUE)
output
}
if (!is.null(output)) {
<- module$destinations |>
queue_add map(~ list(source = signal$target, target = .x, pulse = output))
<- c(queue, queue_add)
queue
<- length(module$destinations)
n
if (output == TRUE)
$target]]$n_hi <- modules[[signal$target]]$n_hi + n
modules[[signalelse
$target]]$n_lo <- modules[[signal$target]]$n_lo + n
modules[[signal
}
}
modules }
Define a function that sums the total low vs high buttons sent at a given state of the modules:
<- function(modules) {
count_pulses c(
lo = sum(map_int(modules, ~ .x$n_lo)),
hi = sum(map_int(modules, ~ .x$n_hi))
) }
Run on puzzle input:
<- modules
output
for (i in 1:1000) {
<- push_button(output)
output
}
count_pulses(output) |>
prod()
[1] 818649769
Part 2
Examining the input, tj sends a low pulse to rx if its last memories (kk, xc, sk, vt) are all high pulses. For each of those memories, we just check when it first sends a high pulse to compute its cycle length, then take the LCM:
<- function(modules, name, pulse = c("hi", "lo")) {
pushes_until_sent <- c("n_", pulse)
pulse <- 0
i
while (pluck(modules, name, "n_hi") == 0) {
<- push_button(modules)
modules <- i + 1
i
}
return(i)
}
c("kk", "xc", "sk", "vt") |>
map_dbl(~ pushes_until_sent(modules, .x, "hi")) |>
::mLCM() |>
numbersformat(scientific = FALSE)
[1] "246313604784977"