-
Notifications
You must be signed in to change notification settings - Fork 0
/
day_12.Rmd
204 lines (156 loc) · 5.21 KB
/
day_12.Rmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
# Rain Risk
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)
options(crayon.enabled = NULL)
library(tidyverse)
START_TIME <- Sys.time()
```
This is my attempt to solve [Day 12](https://adventofcode.com/2020/day/12).
```{r load data}
sample <- read_lines("samples/day_12_sample.txt")
actual <- read_lines("inputs/day_12_input.txt")
```
## Part 1
Today's task could be a tedious series of if else statements. Instead I will use Object-Orientated Programming and the
[s3](https://adv-r.hadley.nz/s3.html) class system. I will strip the initial input to just be the number, but I will
give each of the inputs a class of "N", "E", "W", "S", "F", "R", or "L".
```{r part 1split instructions}
split_instructions <- function(input) {
transpose(
list(
d = str_sub(input, 1, 1),
n = as.integer(str_sub(input, 2))
)
) %>%
map(function(.x) {
structure(.x$n, class = .x$d)
})
}
isample <- split_instructions(sample)
iactual <- split_instructions(actual)
isample
```
First, let's define the initial state:
```{r part 1 initial state}
initial_state <- list(direction = "E", position = c(0, 0))
```
We now need to create a function called that will run an instruction: I will call this function `inst`. It will take as
argument the object itself (`n`), this will contain the number from the input. It will also take the current state.
```{r part 1 inst}
inst <- function(n, state) {
UseMethod("inst")
}
```
Now we need to implement a method for each of the classes. Let's start with the N/E/S/W classes. These methods are
pretty simple, we just need to add on `n` to the current position.
```{r part 1 inst NESW}
# create a helper function that the other functions use
inst_NESW_helper <- function(state, p) {
state$position <- state$position + p
state
}
inst.N <- function(n, state) inst_NESW_helper(state, c( n, 0))
inst.E <- function(n, state) inst_NESW_helper(state, c( 0, n))
inst.S <- function(n, state) inst_NESW_helper(state, c(-n, 0))
inst.W <- function(n, state) inst_NESW_helper(state, c( 0, -n))
```
The L/R functions are slightly more tricky, but, looking at the actual data there are only 3 different angles that are
turned:
```{r part 1 LR angles}
actual %>% str_subset("L|R") %>% str_remove("L|R") %>% unique()
```
We can create our function now. This time, we our helper function will just rotate the direction that we are facing.
```{r part 1 inst LR}
inst_LR_helper <- function(state, r) {
directions <- c("N", "E", "S", "W")
current <- which(state$direction == directions) - 1
next_direction <- (current + r / 90) %% 4
state$direction = directions[[next_direction + 1]]
state
}
# L turns anti-clockwise, so we can turn it into a clockwise rotation
inst.L <- function(n, state) inst_LR_helper(state, 360 - n)
inst.R <- function(n, state) inst_LR_helper(state, n)
```
Finally we can create our function for moving forward. In this case we will take the value from `n`, but update the
class to be that of the current direction from `state`.
```{r part 1 inst F}
inst.F <- function(n, state) {
nn <- structure(n, class = state$direction)
inst(nn, state)
}
```
Now we have all the parts we can build our function to solve part 1. We start with an initial state, and keep calling
a function on each successive value in the input with the current state. To do this we can use the reduce function.
```{r part 1 solve}
solve <- function(input, state = initial_state) {
final_state <- reduce(
input,
.init = initial_state,
function (state, instruction) {
inst(instruction, state)
}
)
sum(abs(final_state$position))
}
```
Checking the sample data:
```{r part 1 sample test}
solve(isample) == 25
```
We can now run with the actual data:
```{r part 1 actual}
solve(iactual)
```
## Part 2
We can now update our instruction functions, our solver should still work.
First we need to update our initial state.
```{r part 2 update initial state}
initial_state <- list(waypoint = c(1, 10), position = c(0, 0))
```
We can cheat a bit on the N/E/S/W functions: we can just change the update position helper function to instead update
the waypoint.
```{r part 2 update NESW functions}
inst_NESW_helper <- function(state, p) {
state$waypoint <- state$waypoint + p
state
}
```
Likewise, we just need to update the L/R helper function. We can use
[matrix multiplication](https://en.wikipedia.org/wiki/Rotation_matrix#Common_rotations) to rotate our waypoint.
```{r part 2 update LR functions}
inst_LR_helper <- function(state, r) {
# r is either 90, 180, 270, and it a clockwise rotation
rot_mat <- matrix(
if (r == 90) {
c(0, 1, -1, 0)
} else if (r == 180) {
c(-1, 0, 0, -1)
} else {
c(0, -1, 1, 0)
},
nrow = 2
)
state$waypoint <- (rot_mat %*% state$waypoint)[,1]
state
}
```
We now just need to update the F function.
```{r part 2 update F function}
inst.F <- function(n, state) {
w <- state$waypoint
s <- state$position
state$position <- w * n + s
state
}
```
We should now be in a position to run our `solve` function again. Checking the sample:
```{r part 2 sample test}
solve(isample) == 286
```
We can now run with the actual data:
```{r part 2 actual}
solve(iactual)
```
---
*Elapsed Time: `r round(Sys.time() - START_TIME, 3)`s*