-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathday03.R
132 lines (99 loc) · 3.59 KB
/
day03.R
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
# Example input, if needed
# day03 <- readLines("puzzle_input/input_day03_exp.txt")
day03 <- readLines("puzzle_input/input_day03.txt")
day03 <- strsplit(day03, "")
day03 <- t(matrix(unlist(day03), nrow = length(day03)))
## FUNCTIONS -------------------------------------------------------------------
# Get neighbours in a matrix, given a position. Uses the matrix only for
# boundaries.
get_neighbours <-
function(x, y, mat) {
x_cors <- rep(c(x - 1, x, x + 1), each = 3)
y_cors <- rep(c(y - 1, y, y + 1), 3)
cors <- cbind(x_cors, y_cors)
# Exclude origin
cors <- cors[!(cors[, 1] == x & cors[, 2] == y), ]
# Exclude everything outside matrix dimensions
cors <- cors[cors[ , 1] <= nrow(mat), ]
cors <- cors[cors[ , 2] <= ncol(mat), ]
return(cors)
}
## PART 1 ----------------------------------------------------------------------
start <- Sys.time()
# Turn all symbols into a single symbol for easier searching
part1 <- sub("[^0-9.]", "x", day03)
# Get number positions
number_pos <- arrayInd(which(part1 %in% 0:9), dim(part1))
number_pos <- data.frame(number_pos[order(number_pos[ , 1]), ])
# Get number digits
number_pos$digit <- part1[cbind(number_pos$X1, number_pos$X2)]
# Digits that are adjacent on the x-axis belong to the same number. Assign ID.
number_pos$dist <- c(1, diff(number_pos$X2))
number_pos$num_id <- cumsum(number_pos$dist != 1) + 1
# Get neighbours for every number and check whether one of them is a symbol (x)
number_pos$sym_neighbour <- NA
for (i_pos in 1:nrow(number_pos)) {
temp_neighbours <-
part1[
get_neighbours(
number_pos$X1[i_pos],
number_pos$X2[i_pos], part1
)
]
number_pos$sym_neighbour[i_pos] <- any(temp_neighbours == "x")
}
# Identify all numbers that have at least one neighbouring symbol
have_neighbours <-
which(as.vector(by(number_pos$sym_neighbour, number_pos$num_id, sum)) > 0)
# Get numbers
have_neighbours <- number_pos[number_pos$num_id %in% have_neighbours, ]
target_nums <-
by(
have_neighbours$digit,
have_neighbours$num_id,
function(x) as.numeric(paste0(x, collapse = ""))
)
sum(target_nums)
# 550934
Sys.time() - start
## PART 2 ----------------------------------------------------------------------
start <- Sys.time()
# Remove all symbols that are not *
part2 <- sub("[^0-9.*]", ".", day03)
# Get * positions and give them an ID
gear_pos <- data.frame(which(part2 == "*", arr.ind = TRUE))
gear_pos$gear_ratio <- NA
# This time, it's more efficient to work with the neighbours of the gears, not
# the digits.
for (i_gear in 1:nrow(gear_pos)) {
temp_neighbours <-
get_neighbours(
gear_pos$row[i_gear],
gear_pos$col[i_gear], part2
)
# Any neighbours that are digits?
if(any(grepl("[0-9]", part2[temp_neighbours]))) {
# Check whether the gear is adjacent to exactly 2 numbers
# Use number positions from previous step
neighbour_nums <-
number_pos[
paste0(number_pos$X1, "-", number_pos$X2) %in%
paste0(temp_neighbours[ , 1], "-", temp_neighbours[ , 2]),
]
# If only two neighbouring numbers, identify and multiply them
if(length(unique(neighbour_nums$num_id)) == 2) {
neighbour_nums <-
number_pos[number_pos$num_id %in% neighbour_nums$num_id, ]
neighbour_nums <-
by(
neighbour_nums$digit,
neighbour_nums$num_id,
function(x) as.numeric(paste0(x, collapse = ""))
)
gear_pos$gear_ratio[i_gear] <- prod(neighbour_nums)
}
}
}
sum(gear_pos$gear_ratio, na.rm = TRUE)
# 81997870
Sys.time() - start