1 Setup
1.1 Libraries
library(httr)
library(xml2)
library(magrittr)
library(dplyr)
library(purrr)
library(stringr)
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 %>%
as.integer()
}
puzzle_data <- local({
GET(paste0(base_url, "/input"),
session_cookie) %>%
content(encoding = "UTF-8") %>%
parse_puzzle_data()
})
2 Puzzle Day 3
2.1 Part 1
2.1.1 Description
— Day 3: Spiral Memory —
You come across an experimental new kind of memory stored on an infinite two-dimensional grid.
Each square on the grid is allocated in a spiral pattern starting at a location marked 1
and then counting up while spiraling outward. For example, the first few squares are allocated like this:
17 16 15 14 13
18 5 4 3 12
19 6 1 2 11
20 7 8 9 10
21 22 23---> ...
While this is very space-efficient (no squares are skipped), requested data must be carried back to square 1
(the location of the only access port for this memory system) by programs that can only move up, down, left, or right. They always take the shortest path: the Manhattan Distance between the location of the data and square 1
.
For example:
-
Data from square
1
is carried0
steps, since it’s at the access port. -
Data from square
12
is carried3
steps, such as: down, left, left. -
Data from square
23
is carried only2
steps: up twice. -
Data from square
1024
must be carried31
steps.
How many steps are required to carry the data from the square identified in your puzzle input all the way to the access port?
2.1.2 Solution
We observe the following things:
- We label the first square (just the file with 1) as square \(s_0\) and each surrounding square with \(s_i\). Fields 2 - 9 are for example on square \(s_1\), fields 10 - 25 on square \(s_2\) and so on.
- The right lower corner of each square \(s_i\) ends with the number \((2i + 1)^2\).
- Either the horizontal or the vertical distance to each field on square \(s_i\) is \(i\).
- The other direction is the distance to the middle point of this square to the field.
Thus, to get the distance for field \(j\) the algorithm finds first \(i\) by solving \(j \leq (2i + 1)^2 \implies i = \left\lceil(\sqrt{j} - 1) / 2\right\rceil\) and then gets the distance to the nearest middle point.
get_distance <- function(j) {
if (j == 1L) {
0L
} else {
i <- ceiling((sqrt(j) - 1L) / 2L)
if (sqrt(j) == floor(sqrt(j)) && (sqrt(j) %% 2L == 1L)) {
i <- i - 1
}
rc <- (2L * i + 1L) ^ 2 ## right corner
mp <- rc - (2L * 0:3 + 1L) * i ## middle points
min(abs(mp - j)) + i
}
}
get_distance(puzzle_data)
## [1] 480
2.2 Part 2
2.2.1 Description
— Part Two —
As a stress test on the system, the programs here clear the grid and then store the value 1
in square 1
. Then, in the same allocation order as shown above, they store the sum of the values in all adjacent squares, including diagonals.
So, the first few squares’ values are chosen as follows:
-
Square
1
starts with the value1
. -
Square
2
has only one adjacent filled square (with value1
), so it also stores1
. -
Square
3
has both of the above squares as neighbors and stores the sum of their values,2
. -
Square
4
has all three of the aforementioned squares as neighbors and stores the sum of their values,4
. -
Square
5
only has the first and fourth squares as neighbors, so it gets the value5
.
Once a square is written, its value does not change. Therefore, the first few squares would receive the following values:
147 142 133 122 59
304 5 4 2 57
330 10 1 1 54
351 11 23 25 26
362 747 806---> ...
What is the first value written that is larger than your puzzle input?
2.2.2 Solution
We solve the second part by constructing the matrix iteratively.
embed_matrix <- function(m, n = 2) {
for (i in 1:n) {
m <- cbind(NA, rbind(NA, m , NA), NA)
}
m
}
fill_ulam_spiral <- function(max_val) {
dir <- rbind(
">" = c(0L, 1L),
"^" = c(-1L, 0L),
"<" = c(0L, -1L),
"v" = c(1L, 0L),
"^>" = c(-1L, 1L),
"^<" = c(-1L, -1L),
"v>" = c(1L, 1L),
"v<" = c(1L, -1L)
)
n <- 9L
m <- matrix(NA, n, n)
val <- 1L
pos <- cbind((n + 1L) / 2L, (n + 1L) / 2L)
m[pos] <- val
j <- 2L
while (val < max_val) {
i <- ceiling((sqrt(j) - 1L) / 2L)
l <- 2L * i + 1L
if (pos[1L] > n - 2L) {
k <- 2L
m <- embed_matrix(m, k)
pos <- t(t(pos) + c(k, k))
n <- n + 2L * k
}
movements <- rep(c(">", "^", "<", "v", ">"),
c(1L, l - 2L, l - 1L, l - 1L, l - 1L))
for (next_dir in movements) {
pos <- pos + dir[next_dir, ]
val <- sum(m[t(t(dir) + c(pos))], na.rm = TRUE)
m[pos] <- val
j <- j + 1L
if (val >= max_val) {
break
}
}
}
val
}
fill_ulam_spiral(puzzle_data)
## [1] 349975