Task 1

Thorn Thaler - <

2021-12-2

1 Preamble

A tweet in my timeline called my attention to https://adventofcode.com/1. So I thought I give it a try.

To spice things up, we want to do everything in R, that is also getting the task from the webpage, downloading the data and so on. Thus, we will use library httr to deal with the authentication and retrieval of the needed data and use the session cookie, whose value I shamelessly copied & pasted from a manual login.

2 Setup

2.1 Libraries

First we need to load the required libraries.

library(httr)
library(xml2)
library(magrittr)
library(tibble)
library(zoo)
library(glue)
library(fontawesome)

2.2 Retrieve Data from AoC

2.2.1 Initial Plan

Here I need to go farther back. When you look at AoC you see that the authentication is delegated to either of the following popular services using the OAuth protocol.

Login Options for AoC

I am everything but an expert when it comes to server communication, so I did my research to look for the API specifications to understand the requests which are exchanged in the process of authentication:

I decided to use my GitHub account for this purpose.

At first with my very superficial understanding, I thought httr::oauth2.0_token would handle the authentication for me. But right after reading the documentation, I realized that I am missing some information (like the client id or even more importantly the client secret). Yet I did not give up. I logged in manually to AoC and was redirected (as per OAuth protocol) to a GitHub login page:

GitHub Login Page

As I knew already from the API docs, the first step was to send a GET request to https://github.com/login/oauth/authorize containing - inter alia - the client_id and a redirect_uri. GET requests mean that the query parameters are simply appended to the URL, so I could simply get the client_id from the navigation bar2 and I found the relevant client_id: https://github.com/login?client_id=7bb0a7ec13388aa67963&[…]. With this piece of information and still no deep understanding of the “dance” I simply tried to set up an authentication as per the demo of httr (silently ignoring that I still had no client secret 🙈):

myapp <- oauth_app("github",
  key = "7bb0a7ec13388aa67963"
)
github_token <- oauth2.0_token(oauth_endpoints("github"), myapp)

My first enthusiasm yielded quickly to disillusion. While this approach was properly opening the login page, the redirect did not work and in the GET parameters I also saw why:

URI must match the registerd callback URL

I learned, that I can not redirect the request to my localhost, which would be needed to proceed with the next step (the API told me that I should receive a temporary code). I guess that this safety measure is a good thing such that you cannot simply redirect the process.

Anyways, my interest was sparked and I wanted at least to understand the dance. And of course I still wanted to follow this route to login to AoC. I quickly drafted a StackOverflow question asking for help for the overall goal. Here’s what I did in more detail:

First, I set quickly set up a test OAuth App in my GitHub account:

OAuth App in GitHub

This time I got both the client id and the client secret. Hooray!

OAuth App Credentials

Time to start the “dance”! As a warm-up we can do that easily using httr::oauth2.0_token as per the demo:

client_id <- "f6385d144d7ac17b83df"
client_secret <- keyring::key_get("OAUth_Test_Client-Secret") ## sorry folks ;)
myapp <- oauth_app("test_oauth",
                   key = client_id,
                   secret = client_secret)
github_token <- oauth2.0_token(oauth_endpoints("github"), myapp)
gtoken <- config(token = github_token)
GET("https://api.github.com/rate_limit", gtoken)
## Response [https://api.github.com/rate_limit]
##   Date: 2021-12-04 21:01
##   Status: 200
##   Content-Type: application/json; charset=utf-8
##   Size: 1.07 kB
## {
##   "resources": {
##     "core": {
##       "limit": 5000,
##       "used": 0,
##       "remaining": 5000,
##       "reset": 1638655269
##     },
##     "search": {
##       "limit": 30,
## ...

Worked pretty smooth, didn’t it? Yet I wanted to understand each part of the dance. Thus, instead of relying on the blackbox oauth2.o_token I constructed all the necessary calls separately which helped me to finally understand (or so I think) what is going on under the hood:

base_url <- "https://github.com"
path <- "login/oauth/{end_point}"
## oauth_callback() --> http://localhost:1410/
url <- modify_url(base_url,
                  path = glue(path, end_point = "authorize"),
                  query = list(client_id = client_id,
                               redirect_uri = oauth_callback()))
## This opens a web browser pointing to the url and 
## opens a webserver on port 1410 to listen to the response. 
code <- oauth_listener(url)

## Provide credentials in the spawned browser
access_token <- POST(modify_url(base_url, 
                                path = glue(path, end_point = "access_token")),
                     add_headers(Accept = "application/json"),
                     body = list(client_id = client_id,
                                 client_secret = client_secret,
                                 code = code$code))
GET("https://api.github.com/rate_limit", 
    add_headers(Authorization = paste(content(access_token)$access_token,
                                      "OAUTH-TOKEN")))
## Response [https://api.github.com/rate_limit]
##   Date: 2021-12-04 21:01
##   Status: 200
##   Content-Type: application/json; charset=utf-8
##   Size: 1.03 kB
## {
##   "resources": {
##     "core": {
##       "limit": 60,
##       "used": 0,
##       "remaining": 60,
##       "reset": 1638655279
##     },
##     "search": {
##       "limit": 10,
## ...

Booom! My key take away messages from here:

  1. You need a web server to participate in the dance (https://localhost is just fine).
  2. You cannot (at least for GitHub authentication) redirect the response form the authorize service but to the callback URL.
  3. Because you cannot redirect the response with the code, you can authenticate only at the service defined by the callback URL.
  4. Thus, you cannot remotely authenticate via a script. Full stop.3

While this helped tremendously to understand the OAuth protocol it did not help a bit to extract the data via script as I could not log on.

I was just about to give up, when I made a last Google search on how to login curl adventofcode and I found this gem. A python library to download the AoC data. But how the heck did they manage to authenticate? And here’s where the pieces fall into place. I learned that all I have to do is to

  1. Log on AoC manually.
  2. Get the session cookie.
  3. Eventually use the session cookie to authenticate our requests.

Et voilà. Quite some trial and error until I found out that the solution is rather easy. But as always, I learned an awful lot.

2.2.2 Final Solution

session_cookie <- set_cookies(session = keyring::key_get("AoC-GitHub-Cookie"))
puzzle_1 <- GET("https://adventofcode.com/2021/day/1",
                session_cookie) %>% 
  content(encoding = "UTF-8") %>% 
  xml_find_all("///article") %>% 
  lapply(as.character)

data_1 <- GET("https://adventofcode.com/2021/day/1/input",
              session_cookie) %>% 
  content(encoding = "UTF-8") %>% 
  read.table(text = .) %>% 
  as_tibble() %>% 
  set_names("depth")

3 Puzzle Day 1

3.1 Part 1

3.1.1 Description

— Day 1: Sonar Sweep —

You’re minding your own business on a ship at sea when the overboard alarm goes off! You rush to see if you can help. Apparently, one of the Elves tripped and accidentally sent the sleigh keys flying into the ocean!

Before you know it, you’re inside a submarine the Elves keep ready for situations like this. It’s covered in Christmas lights (because of course it is), and it even has an experimental antenna that should be able to track the keys if you can boost its signal strength high enough; there’s a little meter that indicates the antenna’s signal strength by displaying 0-50 stars.

Your instincts tell you that in order to save Christmas, you’ll need to get all fifty stars by December 25th.

Collect stars by solving puzzles. Two puzzles will be made available on each day in the Advent calendar; the second puzzle is unlocked when you complete the first. Each puzzle grants one star. Good luck!

As the submarine drops below the surface of the ocean, it automatically performs a sonar sweep of the nearby sea floor. On a small screen, the sonar sweep report (your puzzle input) appears: each line is a measurement of the sea floor depth as the sweep looks further and further away from the submarine.

For example, suppose you had the following report:

199
200
208
210
200
207
240
269
260
263

This report indicates that, scanning outward from the submarine, the sonar sweep found depths of 199, 200, 208, 210, and so on.

The first order of business is to figure out how quickly the depth increases, just so you know what you’re dealing with - you never know if the keys will get carried into deeper water by an ocean current or a fish or something.

To do this, count the number of times a depth measurement increases from the previous measurement. (There is no measurement before the first measurement.) In the example above, the changes are as follows:

199 (N/A - no previous measurement)
200 (increased)
208 (increased)
210 (increased)
200 (decreased)
207 (increased)
240 (increased)
269 (increased)
260 (decreased)
263 (increased)

In this example, there are 7 measurements that are larger than the previous measurement.

How many measurements are larger than the previous measurement?

3.1.2 Solution

We need to count how often the depth measurement increases, which breaks down to counting the positive signs of the differences of consecutive numbers:

sgn_a <- data_1$depth %>% 
  diff()
sum(sgn_a > 0)
## [1] 1713

3.2 Part 2

3.2.1 Description

— Part Two —

Considering every single measurement isn’t as useful as you expected: there’s just too much noise in the data.

Instead, consider sums of a three-measurement sliding window. Again considering the above example:

199  A      
200  A B    
208  A B C  
210    B C D
200  E   C D
207  E F   D
240  E F G  
269    F G H
260      G H
263        H

Start by comparing the first and second three-measurement windows. The measurements in the first window are marked A (199, 200, 208); their sum is 199 + 200 + 208 = 607. The second window is marked B (200, 208, 210); its sum is 618. The sum of measurements in the second window is larger than the sum of the first, so this first comparison increased.

Your goal now is to count the number of times the sum of measurements in this sliding window increases from the previous sum. So, compare A with B, then compare B with C, then C with D, and so on. Stop when there aren’t enough measurements left to create a new three-measurement sum.

In the above example, the sum of each three-measurement window is as follows:

A: 607 (N/A - no previous sum)
B: 618 (increased)
C: 618 (no change)
D: 617 (decreased)
E: 647 (increased)
F: 716 (increased)
G: 769 (increased)
H: 792 (increased)

In this example, there are 5 sums that are larger than the previous sum.

Consider sums of a three-measurement sliding window. How many sums are larger than the previous sum?

3.2.2 Solution

Now we need to do the same but not for the single measurements, but for the consecutive sum of 3 elements. We can conveniently use zoo::rollsum:

sgn_b <- data_1$depth %>% 
  rollsum(3) %>% 
  diff()
sum(sgn_b > 0)
## [1] 1734

  1. In the following I will refer to Advent of Code simply as AoC.↩︎

  2. Alternatively I could also look into the Network section of the DevTools of my Chrome browser.↩︎

  3. As said, I am a novice when it comes to authentication processes. Thus, if you know a way how I could authenticate, please let me know, either by answering my SO question, issuing a PR on this document or by good old email.↩︎