1 Setup
1.1 Libraries
library(httr)
library(xml2)
library(magrittr)
library(dplyr)
library(purrr)
library(stringr)
library(knitr)
library(tidyr)
1.2 Retrieve Data from AoC
session_cookie <- set_cookies(session = keyring::key_get("AoC-GitHub-Cookie"))
base_url <- paste0("https://adventofcode.com/", params$year, "/day/", params$task_nr)
puzzle <- GET(base_url,
session_cookie) %>%
content(encoding = "UTF-8") %>%
xml_find_all("///article") %>%
lapply(as.character)
parse_puzzle_data <- function(text_block = readClipboard()) {
if (length(text_block) == 1L) {
text_block <- text_block %>%
str_split("\n") %>%
extract2(1L) %>%
keep(nzchar)
}
text_block %>%
str_split(",") %>%
do.call(c, .) %>%
as.numeric()
}
puzzle_data <- local({
GET(paste0(base_url, "/input"),
session_cookie) %>%
content(encoding = "UTF-8") %>%
parse_puzzle_data()
})
2 Puzzle Day 25
2.1 Part 1
2.1.1 Description
— Day 25: Cryostasis —
As you approach Santa’s ship, your sensors report two important details:
First, that you might be too late: the internal temperature is -40 degrees.
Second, that one faint life signature is somewhere on the ship.
The airlock door is locked with a code; your best option is to send in a small droid to investigate the situation. You attach your ship to Santa’s, break a small hole in the hull, and let the droid run in before you seal it up again. Before your ship starts freezing, you detach your ship and set it to automatically stay within range of Santa’s ship.
This droid can follow basic instructions and report on its surroundings; you can communicate with it through an Intcode program (your puzzle input) running on an ASCII-capable computer.
As the droid moves through its environment, it will describe what it encounters. When it says Command?, you can give it a single instruction terminated with a newline (ASCII code 10). Possible instructions are:
-
Movement via
north,south,east, orwest. -
To take an item the droid sees in the environment, use the command
take <name of item>. For example, if the droid reports seeing ared ball, you can pick it up withtake red ball. -
To drop an item the droid is carrying, use the command
drop <name of item>. For example, if the droid is carrying agreen ball, you can drop it withdrop green ball. -
To get a list of all of the items the droid is currently carrying, use the command
inv(for “inventory”).
Extra spaces or other characters aren’t allowed - instructions must be provided precisely.
Santa’s ship is a Reindeer-class starship; these ships use pressure-sensitive floors to determine the identity of droids and crew members. The standard configuration for these starships is for all droids to weigh exactly the same amount to make them easier to detect. If you need to get past such a sensor, you might be able to reach the correct weight by carrying items from the environment.
Look around the ship and see if you can find the password for the main airlock.
2.1.2 Solution
We build upon the code from Day 21 @ 2019. We first played tha game by hand to explore the map and get a list of all collectible items (some items cannot be picked up without exiting the game immediately).
Once we got a list of all collectible items, we wrote automatized the walk and brute forced all needed items to enter the security lock. For this we dropped all items, (irrespective of whether we were currently carrying them - the program would just issue a message that we do not carry this item in the moment, which was simply easier than keeping track of the inventory by our-self) and picked up each combination. Eventually, we found the correct combination of items to pass.
read_input <- function() {
cmd <- readline(": ")
paste0(cmd, "\n") %>%
charToRaw() %>%
as.integer()
}
decode_output <- function(out) {
if (all(out <= 255)) {
intToUtf8(out) %>%
str_split("\n") %>%
extract2(1L)
} else {
tail(out, 1L)
}
}
parse_op_codes_fast <- function(op_codes = puzzle_data, input = numeric(0L),
verbose = FALSE) {
ip <- 1L
rel_base <- 0L
out_buffer <- numeric(0)
out_len <- 0L
out_block <- 1024L
block_size <- 1024L
grow_memory <- function(idx) {
if (idx > length(op_codes)) {
new_size <- ceiling(idx / block_size) * block_size
op_codes <<- c(op_codes, rep(0, new_size - length(op_codes)))
}
}
append_out <- function(val) {
if (out_len + 1L > length(out_buffer)) {
new_size <- length(out_buffer) + out_block
out_buffer <<- c(out_buffer, rep(0, new_size - length(out_buffer)))
}
out_len <<- out_len + 1L
out_buffer[out_len] <<- val
}
get_val <- function(param, mode) {
if (mode == 0L) {
grow_memory(param + 1L)
return(op_codes[param + 1L])
} else if (mode == 1L) {
return(param)
} else if (mode == 2L) {
idx <- rel_base + param + 1L
grow_memory(idx)
return(op_codes[idx])
} else {
stop("unknown mode")
}
}
get_addr <- function(param, mode) {
if (mode == 0L) {
return(param + 1L)
} else if (mode == 2L) {
return(rel_base + param + 1L)
} else {
stop("invalid write mode")
}
}
halt <- FALSE
while (!halt) {
instr <- op_codes[ip]
op <- instr %% 100L
modes <- c(
(instr %/% 100L) %% 10L,
(instr %/% 1000L) %% 10L,
(instr %/% 10000L) %% 10L
)
if (op == 1L) { # add
a <- get_val(op_codes[ip + 1L], modes[1L])
b <- get_val(op_codes[ip + 2L], modes[2L])
addr <- get_addr(op_codes[ip + 3L], modes[3L])
grow_memory(addr)
op_codes[addr] <- a + b
ip <- ip + 4L
} else if (op == 2L) { # mul
a <- get_val(op_codes[ip + 1L], modes[1L])
b <- get_val(op_codes[ip + 2L], modes[2L])
addr <- get_addr(op_codes[ip + 3L], modes[3L])
grow_memory(addr)
op_codes[addr] <- a * b
ip <- ip + 4L
} else if (op == 3L) { # input
if (length(input) == 0L) {
input <- read_input()
}
addr <- get_addr(op_codes[ip + 1L], modes[1])
grow_memory(addr)
op_codes[addr] <- input[1L]
input <- input[-1]
ip <- ip + 2L
} else if (op == 4L) { # output
val <- get_val(op_codes[ip + 1L], modes[1])
append_out(val)
if (verbose) {
cat(intToUtf8(val))
}
ip <- ip + 2L
} else if (op == 5L) { # jump-if-true
a <- get_val(op_codes[ip + 1L], modes[1L])
b <- get_val(op_codes[ip + 2L], modes[2L])
if (a != 0L) {
ip <- b + 1L
} else {
ip <- ip + 3L
}
} else if (op == 6L) { # jump-if-false
a <- get_val(op_codes[ip + 1L], modes[1L])
b <- get_val(op_codes[ip + 2L], modes[2L])
if (a == 0L) {
ip <- b + 1L
} else {
ip <- ip + 3L
}
} else if (op == 7L) { # less than
a <- get_val(op_codes[ip + 1L], modes[1L])
b <- get_val(op_codes[ip + 2L], modes[2L])
addr <- get_addr(op_codes[ip + 3L], modes[3L])
grow_memory(addr)
op_codes[addr] <- as.numeric(a < b)
ip <- ip + 4L
} else if (op == 8L) { # equals
a <- get_val(op_codes[ip + 1L], modes[1L])
b <- get_val(op_codes[ip + 2L], modes[2L])
addr <- get_addr(op_codes[ip + 3L], modes[3L])
grow_memory(addr)
op_codes[addr] <- as.numeric(a == b)
ip <- ip + 4L
} else if (op == 9L) { # adjust relative base
a <- get_val(op_codes[ip + 1L], modes[1L])
rel_base <- rel_base + a
ip <- ip + 2L
} else if (op == 99L) { # halt
halt <- TRUE
} else {
stop("Unknown opcode ", op)
}
}
out_buffer[1:(out_len - 1L)]
}
solve_inventory <- function(pgrm = puzzle_data) {
## we found the commands by playing the game first by hand
collect_all <- c(
"north",
"west",
"take planetoid",
"west",
"take spool of cat6",
"east",
"east",
"south",
"west",
"north",
"take dark matter",
"south",
"east",
"east",
"north",
"take sand",
"west",
"take coin",
"north",
"take jam",
"south",
"west",
"south",
"take wreath",
"west",
"take fuel cell",
"east",
"north",
"north",
"west"
) %>%
paste(collapse = "\n")
## at this stage we are in front of the security checkpoint with all collectible items
all_items <- c("jam", "fuel cell", "planetoid", "sand", "spool of cat6", "coin",
"dark matter", "wreath")
make_cmd <- function(items) {
drop_all <- paste("drop", all_items, collapse = "\n")
take_items <- paste("take", items, collapse = "\n")
paste(drop_all, take_items, "south", sep = "\n")
}
all_combs <- rep(list(c(TRUE, FALSE)), length(all_items)) %>%
set_names(paste0("i", seq_along(all_items))) %>%
do.call(expand_grid, .) %>%
filter(if_any(everything())) %>%
rowwise() %>%
mutate(cmd = make_cmd(all_items[c_across(everything())]),
.keep = "unused")
cmd <- all_combs %>%
ungroup() %>%
summarize(prgm = paste(collect_all, paste(cmd, collapse = "\n"), sep = "\n")) %>%
pull(prgm) %>%
charToRaw() %>%
as.integer()
res <- parse_op_codes_fast(puzzle_data, input = cmd)
res %>%
decode_output() %>%
tail(1L) %>%
str_extract("\\d+") %>%
extract2(1L) %>%
as.integer()
}
solve_inventory(puzzle_data)
## [1] 8401920
2.2 Part 2
2.2.1 Description
— Part Two —
As you move through the main airlock, the air inside the ship is already heating up to reasonable levels. Santa explains that he didn’t notice you coming because he was just taking a quick nap. The ship wasn’t frozen; he just had the thermostat set to “North Pole”.
You make your way over to the navigation console. It beeps. “Status: Stranded. Please supply measurements from 49 stars to recalibrate.”
“49 stars? But the Elves told me you needed fifty–”
Santa just smiles and nods his head toward the window. There, in the distance, you can see the center of the Solar System: the Sun!
The navigation console beeps again.
2.2.2 Solution
Hohoho, yet another year solved! I loved the intcodes, but the puzzles were a bit harder than the previous years.