library(tidyverse)
library(unglue)
library(bit64)
Day 17
Advent of Code: Worked Solutions
Setup
Import libraries:
Disable scientific formatting when displaying large numbers:
options(scipen = 999)
Read input from file:
<- read_lines("../input/day17.txt", skip_empty_rows = TRUE) |>
input unglue_data("{label}: {value}")
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(
machine program = program,
A = A,
B = B,
C = C,
pointer = 0L,
output = NULL
)
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
}
$output
machine }
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
machine
machine
}
<- function(machine, operand) {
bxl $B <- bitwXor64(machine$B, operand)
machine$pointer <- machine$pointer + 2
machine
machine
}
<- function(machine, operand) {
bst $B <- combo(machine, operand) %% 8
machine$pointer <- machine$pointer + 2
machine
machine
}
<- function(machine, operand) {
jnz if (machine$A != 0)
$pointer <- operand
machineelse
$pointer <- machine$pointer + 2
machine
machine
}
<- function(machine, operand) {
bxc $B <- bitwXor64(machine$B, machine$C)
machine$pointer <- machine$pointer + 2
machine
machine
}
<- function(machine, operand) {
out $output <- c(machine$output, combo(machine, operand) %% 8)
machine$pointer <- machine$pointer + 2
machine
machine
}
<- function(machine, operand) {
bdv $B <- floor(machine$A / 2^combo(machine, operand))
machine$pointer <- machine$pointer + 2
machine
machine
}
<- function(machine, operand) {
cdv $C <- floor(machine$A / 2^combo(machine, operand))
machine$pointer <- machine$pointer + 2
machine
machine }
Run on puzzle input:
|>
machine run_machine() |>
str_flatten(",")
Part 2
Reverse engineer, testing sequences of 3 bits at a time. Thanks to hints from Reddit:
<- function(a) {
run_machine_a run_machine(list(
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)