1 Setup
1.1 Libraries
library(httr)
library(xml2)
library(magrittr)
library(dplyr)
library(purrr)
library(stringr)
library(glue)
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
}
puzzle_data <- local({
GET(paste0(base_url, "/input"),
session_cookie) %>%
content(encoding = "UTF-8") %>%
parse_puzzle_data()
})
2 Puzzle Day 9
2.1 Part 1
2.1.1 Description
— Day 9: Explosives in Cyberspace —
Wandering around a secure area, you come across a datalink port to a new part of the network. After briefly scanning it for interesting files, you find one file in particular that catches your attention. It’s compressed with an experimental format, but fortunately, the documentation for the format is nearby.
The format compresses a sequence of characters. Whitespace is ignored. To indicate that some sequence should be repeated, a marker is added to the file, like (10x2)
. To decompress this marker, take the subsequent 10
characters and repeat them 2
times. Then, continue reading the file after the repeated data. The marker itself is not included in the decompressed output.
If parentheses or other characters appear within the data referenced by a marker, that’s okay - treat it like normal data, not a marker, and then resume looking for markers after the decompressed section.
For example:
-
ADVENT
contains no markers and decompresses to itself with no changes, resulting in a decompressed length of6
. -
A(1x5)BC
repeats only theB
a total of5
times, becomingABBBBBC
for a decompressed length of7
. -
(3x3)XYZ
becomesXYZXYZXYZ
for a decompressed length of9
. -
A(2x2)BCD(2x2)EFG
doubles theBC
andEF
, becomingABCBCDEFEFG
for a decompressed length of11
. -
(6x1)(1x3)A
simply becomes(1x3)A
- the(1x3)
looks like a marker, but because it’s within a data section of another marker, it is not treated any differently from theA
that comes after it. It has a decompressed length of6
. -
X(8x2)(3x3)ABCY
becomesX(3x3)ABC(3x3)ABCY
(for a decompressed length of18
), because the decompressed data from the(8x2)
marker (the(3x3)ABC
) is skipped and not processed further.
What is the decompressed length of the file (your puzzle input)? Don’t count whitespace.
We use the following algorithm:
- Get everything up to (but not including) the first (from the left)
(nxm)
marker. - Add the length of this tring to the result.
- Extract the
(nxm)
pattern and addn * m
to the result (we repeat a substring of lengthn
m
times.) - Remove the marker plus
n
characters from the string. - Repeat until the string is fully consumed.
2.1.2 Solution
get_decompressed_length <- function(string) {
compression_pattern <- "\\((\\d+)x(\\d+)\\)"
## everything from start to the next pattern
chunk_pattern <- glue("^.*?(?={compression_pattern})")
len <- 0
while (str_length(string) > 0L) {
left <- str_extract(string, chunk_pattern)
rest <- str_remove(string, chunk_pattern)
len <- len + str_length(left)
compression <- str_match_all(rest, glue("^{compression_pattern}")) %>%
extract2(1L) %>%
c() %>%
extract(-1L) %>%
as.integer()
len <- len + prod(compression)
string <- str_remove(rest, glue("^{compression_pattern}.{{{compression[1L]}}}"))
}
len
}
get_decompressed_length(puzzle_data)
## [1] 115118
2.2 Part 2
2.2.1 Description
— Part Two —
Apparently, the file actually uses version two of the format.
In version two, the only difference is that markers within decompressed data are decompressed. This, the documentation explains, provides much more substantial compression capabilities, allowing many-gigabyte files to be stored in only a few kilobytes.
For example:
-
(3x3)XYZ
still becomesXYZXYZXYZ
, as the decompressed section contains no markers. -
X(8x2)(3x3)ABCY
becomesXABCABCABCABCABCABCY
, because the decompressed data from the(8x2)
marker is then further decompressed, thus triggering the(3x3)
marker twice for a total of sixABC
sequences. -
(27x12)(20x12)(13x14)(7x10)(1x12)A
decompresses into a string ofA
repeated241920
times. -
(25x3)(3x3)ABC(2x3)XY(5x2)PQRSTX(18x9)(3x2)TWO(5x7)SEVEN
becomes445
characters long.
Unfortunately, the computer you brought probably doesn’t have enough memory to actually decompress the file; you’ll have to come up with another way to get its decompressed length.
What is the decompressed length of the file using this improved format?
2.2.2 Solution
This time we us a recursive version of the same idea. Instead of simply discarding the
next n
characters, we recurse with this substring and multiply it by the outer m
.
get_decompressed_length_recursive <- function(string) {
len <- 0
compression_pattern <- "\\((\\d+)x(\\d+)\\)"
chunk_pattern <- glue("^.*?(?={compression_pattern})")
if (!str_detect(string, compression_pattern)) {
## no more compression markers
len <- str_length(string)
} else {
## everything from start to the next pattern
left <- str_extract(string, chunk_pattern)
rest <- str_remove(string, chunk_pattern)
len <- len + str_length(left)
compression <- str_match_all(rest, glue("^{compression_pattern}")) %>%
extract2(1L) %>%
c() %>%
extract(-1L) %>%
as.integer()
next_chunk <- str_remove(rest, glue("^{compression_pattern}")) %>%
str_extract(glue(".{{{compression[1L]}}}"))
rest <- str_remove(rest, glue("^{compression_pattern}.{{{compression[1L]}}}"))
len <- len + compression[2L] * Recall(next_chunk) + Recall(rest)
}
len
}
get_decompressed_length_recursive(puzzle_data)
## [1] 11107527530