library(tidyverse)
Day 22
Advent of Code: Worked Solutions
Setup
Import libraries:
Read input from txt file into a numeric vector:
<- scan("../input/day22.txt") input
Part 1
R only has bitwise operations for integers up to 32 bits. First, we have to define functions to convert between large numeric values and 64-“bit” 1/0 vectors:
<- 2^(63:0) # (pre-compute this vector to reduce runtime)
base64
<- \(x) x %/% base64 %% 2
as_b64 <- \(x) sum(x * base64) as_num
Define a function to perform xor operations on large integers:
<- \(x, y) as_num(base::xor(as_b64(x), as_b64(y))) b64xor
Define the algorithm for producing a sequence of “secret” numbers:
<- \(x) x %% 16777216
prune
<- function(x0) {
secret_alg <- prune(b64xor(x0, x0 * 64))
x1 <- prune(b64xor(x1, x1 %/% 32))
x2 <- prune(b64xor(x2, x2 * 2048))
x3
x3
}
<- function(init, n) {
secret_seq map(init, \(init) accumulate(1:n, \(x, y) secret_alg(x), .init = init))
}
Generate 2000 “secret numbers” for each initial value in the puzzle input:
<- secret_seq(input, n = 2000) secret_nums
Sum the 2000th “secret number” for each value in our input:
|>
secret_nums map_dbl(~ tail(.x, n = 1)) |>
sum()
Part 2
Convert the set of secret number sequences to a dataframe organized by buyer and time:
<- secret_nums |>
diffs enframe(name = "buyer_id", value = "secret_number") |>
unnest_longer(secret_number, indices_to = "time") |>
mutate(
# Get the price at each time by taking the ones digit of each secret number
price = secret_number %% 10L,
# Compute the difference in price at the current time vs the previous time
diff = price - lag(price),
# Compute the sequence of 4 price changes preceeding the current price
lag1 = lag(diff, n = 1L),
lag2 = lag(diff, n = 2L),
lag3 = lag(diff, n = 3L),
diff_seq = str_c(lag3, lag2, lag1, diff, sep = ","),
.by = buyer_id
|>
) arrange(buyer_id, time)
For each price change sequence, compute the bananas you will get from each buyer:
<- diffs |>
bananas_by_seq filter(!is.na(diff_seq)) |>
summarize(
bananas = head(price, 1),
.by = c(buyer_id, diff_seq)
)
Find the most advantageous sequence:
|>
bananas_by_seq summarize(bananas = sum(bananas), .by = diff_seq) |>
slice_max(bananas) |>
pull(bananas)