# Libraries
library(tidyverse)
library(unglue)
library(bit64)
# Read input from file
<- read_lines("../input/day17.txt", skip_empty_rows = TRUE) |>
input unglue_data(patterns = c(
"{label}: {value}"
))
Day 17
Advent of Code: Worked Solutions
Setup
Part 1
Initialize the machine from the text input:
<- input |>
program filter(label == "Program") |>
pull(value) |>
str_split_1(",") |>
as.integer()
<- input |>
A filter(label == "Register A") |>
pull(value) |>
as.integer()
<- input |>
B filter(label == "Register B") |>
pull(value) |>
as.integer()
<- input |>
C filter(label == "Register C") |>
pull(value) |>
as.integer()
<- list(program = program, A = A, B = B, C = C, pointer = 0L, output = NULL) machine
Define machine’s helper functions:
<- function(machine, operand) {
combo case_match(operand,
0 ~ 0,
1 ~ 1,
2 ~ 2,
3 ~ 3,
4 ~ machine$A,
5 ~ machine$B,
6 ~ machine$C
)
}
<- function(machine, opcode, operand) {
run_opcode <- case_match(opcode,
func 0 ~ "adv",
1 ~ "bxl",
2 ~ "bst",
3 ~ "jnz",
4 ~ "bxc",
5 ~ "out",
6 ~ "bdv",
7 ~ "cdv"
)
get(func)(machine, operand)
}
<- function(machine) {
run_machine while (machine$pointer < length(machine$program)) {
<- machine$program[machine$pointer + 1]
opcode <- machine$program[machine$pointer + 2]
operand <- run_opcode(machine, opcode, operand)
machine
}return(machine$output)
}
Need to define custom bitwise XOR function to handle very large integers without error:
<- function(x, y) {
bitwXor64 <- as.bitstring(as.integer64(x))
x <- as.bitstring(as.integer64(y))
y
::xor(
baseas.integer(str_split_1(x, "")),
as.integer(str_split_1(y, ""))
|>
) as.integer() |>
str_c(collapse = "") |>
structure(class = "bitstring") |>
as.integer64() |>
as.numeric()
}
Define the opcode functions:
<- function(machine, operand) {
adv $A <- floor(machine$A / 2^combo(machine, operand))
machine$pointer <- machine$pointer + 2
machinereturn(machine)
}
<- function(machine, operand) {
bxl $B <- bitwXor64(machine$B, operand)
machine$pointer <- machine$pointer + 2
machinereturn(machine)
}
<- function(machine, operand) {
bst $B <- combo(machine, operand) %% 8
machine$pointer <- machine$pointer + 2
machinereturn(machine)
}
<- function(machine, operand) {
jnz if (machine$A != 0)
$pointer <- operand
machineelse
$pointer <- machine$pointer + 2
machinereturn(machine)
}
<- function(machine, operand) {
bxc $B <- bitwXor64(machine$B, machine$C)
machine$pointer <- machine$pointer + 2
machinereturn(machine)
}
<- function(machine, operand) {
out $output <- c(
machine$output,
machinecombo(machine, operand) %% 8
)$pointer <- machine$pointer + 2
machinereturn(machine)
}
<- function(machine, operand) {
bdv $B <- floor(machine$A / 2^combo(machine, operand))
machine$pointer <- machine$pointer + 2
machinereturn(machine)
}
<- function(machine, operand) {
cdv $C <- floor(machine$A / 2^combo(machine, operand))
machine$pointer <- machine$pointer + 2
machinereturn(machine)
}
Run on puzzle input:
run_machine(machine) |>
str_c(collapse = ",")
[1] "3,1,4,3,1,7,1,6,3"
Part 2
Reverse engineer, testing sequences of 3 bits at a time. Thanks to hints from Reddit:
<- function(a) run_machine(list(
run_machine_a program = program,
A = a,
B = B,
C = C,
pointer = 0L,
output = NULL
))
<- function(program, digit = 1, a = 0) {
reveng if (digit > length(program))
return(a)
<- tibble(candidates = 8 * a + 0:7) |>
df mutate(
output = map(candidates, run_machine_a),
output = map(output, head, n = 1)
|>
) filter(output == rev(program)[digit]) |>
mutate(
res = map_dbl(candidates, ~ reveng(program, digit + 1, .x))
|>
) filter(!is.na(res))
if (nrow(df) == 0) return(Inf)
else return(min(df$res))
}
reveng(program) |>
format(scientific = FALSE)
[1] "37221270076916"