library(tidyverse)
library(stringr)
library(tictoc)
Read in the problem:
dat <- read_lines("aoc06.txt")
mapmat <- str_split_fixed(dat, "", n = str_length(dat[1]))
Top left corner of the map:
mapmat[1:10, 1:10]
[,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
[1,] "." "." "." "." "." "." "." "." "." "."
[2,] "." "." "." "." "." "." "#" "." "." "."
[3,] "." "." "." "#" "." "#" "." "." "." "."
[4,] "." "." "." "." "." "." "." "." "." "."
[5,] "." "." "." "." "." "." "." "." "." "."
[6,] "." "." "." "." "." "." "." "." "." "."
[7,] "." "." "." "." "." "." "." "." "." "."
[8,] "." "." "#" "." "." "." "." "#" "." "."
[9,] "." "." "." "." "." "." "." "." "." "."
[10,] "." "." "." "#" "." "." "." "#" "#" "."
Part 1
Here are the moves:
moves <- read.csv(text = "now,then,row_step,col_step
N,E,-1,0
S,W,1,0
W,N,0,-1
E,S,0,1")
moves
A dirty loop:
work_map <- mapmat
cur_dir <- "N"
cur_pos <- which(work_map == "^", arr.ind = TRUE)
work_map[cur_pos] <- "X"
tic()
done <- FALSE
while (!done) {
cur_move <- moves |>
filter(now == cur_dir)
new_pos <- matrix(c(NA, NA), nrow = 1, ncol = 2)
new_pos[1] <- cur_pos[1] + cur_move$row_step
new_pos[2] <- cur_pos[2] + cur_move$col_step
if (!between(new_pos[1], 1, nrow(work_map)) ||
!between(new_pos[2], 1, ncol(work_map))) {
done <- TRUE
}
else if (work_map[new_pos] %in% c(".", "X")) {
work_map[new_pos] <- "X"
cur_pos <- new_pos
}
else {
cur_dir <- cur_move$then
}
}
toc()
3.86 sec elapsed
Two bugs that wasted some time:
Forgot that the guard can walk over their own path
again.
Needed to check that the new position was still on the map,
rather than only current position.
which(work_map == "X") |> length()
[1] 5129
Part 2
Originally ran out of time; just realised that I’d already worked out
the search space with the X’s above:
start_pos <- which(mapmat == "^", arr.ind = TRUE)
I’m sure there’s a smaller space, but at least this is less than a
day’s computations…
Remove the starting position:
potentials <- which(work_map == "X", arr.ind = TRUE) |>
as.data.frame() |>
filter(!(row == start_pos[1] & col == start_pos[2]))
nrow(potentials)
[1] 5128
Spot a loop by checking if we have passed the same location, pointing
the same direction, twice.
has_loop <- function(the_map) {
cur_dir <- "N"
cur_pos <- which(the_map == "^", arr.ind = TRUE)
blank_map <- matrix(0, nrow = nrow(the_map), ncol = ncol(the_map))
been_here <- list(N = blank_map,
S = blank_map,
W = blank_map,
E = blank_map)
done <- FALSE
loopy <- FALSE
while (!done) {
been_here[[cur_dir]][cur_pos] <- been_here[[cur_dir]][cur_pos] + 1
if (been_here[[cur_dir]][cur_pos] == 2) {
loopy <- TRUE
break
}
cur_move <- moves |>
filter(now == cur_dir)
new_pos <- matrix(c(NA, NA), nrow = 1, ncol = 2)
new_pos[1] <- cur_pos[1] + cur_move$row_step
new_pos[2] <- cur_pos[2] + cur_move$col_step
if (!between(new_pos[1], 1, nrow(the_map)) ||
!between(new_pos[2], 1, ncol(the_map))) {
done <- TRUE
}
else if (the_map[new_pos] != "#") {
cur_pos <- new_pos
}
else {
cur_dir <- cur_move$then
}
}
loopy
}
Stick a block at (x,y) and check if there’s a loop.
block_loops <- function(the_map, x, y) {
the_map[x, y] <- "#"
has_loop(the_map)
}
Do it for all the locations we visited in Part 1.
tic()
res <- map2_lgl(potentials[,1],
potentials[,2],
\(x, y) block_loops(mapmat, x, y),
.progress = TRUE)
toc()
First (and last) time run, it was 14440.11 sec.
sum(res)
[1] 1888
LS0tDQp0aXRsZTogIkRheSA2OiBHdWFyZCBHYWxsaXZhbnQiDQphdXRob3I6IEFuZGlGDQpkYXRlOiA2IERlYyAyMDI0DQpvdXRwdXQ6IA0KICBodG1sX25vdGVib29rOiANCiAgICBjb2RlX2ZvbGRpbmc6IG5vbmUNCi0tLQ0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KHN0cmluZ3IpDQpsaWJyYXJ5KHRpY3RvYykNCmBgYA0KDQpSZWFkIGluIHRoZSBwcm9ibGVtOg0KDQpgYGB7cn0NCmRhdCA8LSByZWFkX2xpbmVzKCJhb2MwNi50eHQiKQ0KbWFwbWF0IDwtIHN0cl9zcGxpdF9maXhlZChkYXQsICIiLCBuID0gc3RyX2xlbmd0aChkYXRbMV0pKQ0KYGBgDQoNClRvcCBsZWZ0IGNvcm5lciBvZiB0aGUgbWFwOg0KDQpgYGB7cn0NCm1hcG1hdFsxOjEwLCAxOjEwXQ0KYGBgDQoNCg0KIyMjIFBhcnQgMQ0KDQpIZXJlIGFyZSB0aGUgbW92ZXM6DQoNCmBgYHtyfQ0KbW92ZXMgPC0gcmVhZC5jc3YodGV4dCA9ICJub3csdGhlbixyb3dfc3RlcCxjb2xfc3RlcA0KTixFLC0xLDANClMsVywxLDANClcsTiwwLC0xDQpFLFMsMCwxIikNCm1vdmVzDQpgYGANCg0KQSBkaXJ0eSBsb29wOg0KDQpgYGB7cn0NCndvcmtfbWFwIDwtIG1hcG1hdA0KY3VyX2RpciA8LSAiTiINCmN1cl9wb3MgPC0gd2hpY2god29ya19tYXAgPT0gIl4iLCBhcnIuaW5kID0gVFJVRSkNCndvcmtfbWFwW2N1cl9wb3NdIDwtICJYIg0KDQp0aWMoKQ0KZG9uZSA8LSBGQUxTRQ0Kd2hpbGUgKCFkb25lKSB7DQogIGN1cl9tb3ZlIDwtIG1vdmVzIHw+DQogICAgZmlsdGVyKG5vdyA9PSBjdXJfZGlyKQ0KICANCiAgbmV3X3BvcyA8LSBtYXRyaXgoYyhOQSwgTkEpLCBucm93ID0gMSwgbmNvbCA9IDIpDQogIG5ld19wb3NbMV0gPC0gY3VyX3Bvc1sxXSArIGN1cl9tb3ZlJHJvd19zdGVwDQogIG5ld19wb3NbMl0gPC0gY3VyX3Bvc1syXSArIGN1cl9tb3ZlJGNvbF9zdGVwDQogIA0KICBpZiAoIWJldHdlZW4obmV3X3Bvc1sxXSwgMSwgbnJvdyh3b3JrX21hcCkpIHx8DQogICAgICAhYmV0d2VlbihuZXdfcG9zWzJdLCAxLCBuY29sKHdvcmtfbWFwKSkpIHsNCiAgICBkb25lIDwtIFRSVUUgIA0KICB9DQogIGVsc2UgaWYgKHdvcmtfbWFwW25ld19wb3NdICVpbiUgYygiLiIsICJYIikpIHsNCiAgICB3b3JrX21hcFtuZXdfcG9zXSA8LSAiWCINCiAgICBjdXJfcG9zIDwtIG5ld19wb3MNCiAgfQ0KICBlbHNlICB7DQogICAgY3VyX2RpciA8LSBjdXJfbW92ZSR0aGVuDQogIH0NCn0NCnRvYygpDQpgYGANCg0KVHdvIGJ1Z3MgdGhhdCB3YXN0ZWQgc29tZSB0aW1lOg0KDQoxLiBGb3Jnb3QgdGhhdCB0aGUgZ3VhcmQgY2FuIHdhbGsgb3ZlciB0aGVpciBvd24gcGF0aCBhZ2Fpbi4NCg0KMi4gTmVlZGVkIHRvIGNoZWNrIHRoYXQgdGhlIG5ldyBwb3NpdGlvbiB3YXMgc3RpbGwgb24gdGhlIG1hcCwgcmF0aGVyIHRoYW4gb25seSBjdXJyZW50IHBvc2l0aW9uLg0KDQoNCmBgYHtyfQ0Kd2hpY2god29ya19tYXAgPT0gIlgiKSB8PiBsZW5ndGgoKQ0KYGBgDQoNCiMjIyBQYXJ0IDINCg0KT3JpZ2luYWxseSByYW4gb3V0IG9mIHRpbWU7IGp1c3QgcmVhbGlzZWQgdGhhdCBJJ2QgYWxyZWFkeSB3b3JrZWQgb3V0IHRoZSBzZWFyY2ggc3BhY2Ugd2l0aCB0aGUgWCdzIGFib3ZlOg0KDQpgYGB7cn0NCnN0YXJ0X3BvcyA8LSB3aGljaChtYXBtYXQgPT0gIl4iLCBhcnIuaW5kID0gVFJVRSkNCmBgYA0KDQpJJ20gc3VyZSB0aGVyZSdzIGEgc21hbGxlciBzcGFjZSwgYnV0IGF0IGxlYXN0IHRoaXMgaXMgbGVzcyB0aGFuIGEgZGF5J3MgY29tcHV0YXRpb25zLi4uDQoNClJlbW92ZSB0aGUgc3RhcnRpbmcgcG9zaXRpb246DQoNCmBgYHtyfQ0KcG90ZW50aWFscyA8LSB3aGljaCh3b3JrX21hcCA9PSAiWCIsIGFyci5pbmQgPSBUUlVFKSB8Pg0KICBhcy5kYXRhLmZyYW1lKCkgfD4NCiAgZmlsdGVyKCEocm93ID09IHN0YXJ0X3Bvc1sxXSAmIGNvbCA9PSBzdGFydF9wb3NbMl0pKQ0KbnJvdyhwb3RlbnRpYWxzKQ0KYGBgDQoNClNwb3QgYSBsb29wIGJ5IGNoZWNraW5nIGlmIHdlIGhhdmUgcGFzc2VkIHRoZSBzYW1lIGxvY2F0aW9uLCBwb2ludGluZyB0aGUgc2FtZSBkaXJlY3Rpb24sIHR3aWNlLg0KDQpgYGB7cn0NCmhhc19sb29wIDwtIGZ1bmN0aW9uKHRoZV9tYXApIHsNCiAgY3VyX2RpciA8LSAiTiINCiAgY3VyX3BvcyA8LSB3aGljaCh0aGVfbWFwID09ICJeIiwgYXJyLmluZCA9IFRSVUUpDQogIGJsYW5rX21hcCA8LSBtYXRyaXgoMCwgbnJvdyA9IG5yb3codGhlX21hcCksIG5jb2wgPSBuY29sKHRoZV9tYXApKQ0KICBiZWVuX2hlcmUgPC0gbGlzdChOID0gYmxhbmtfbWFwLA0KICAgICAgICAgICAgICAgICAgICBTID0gYmxhbmtfbWFwLA0KICAgICAgICAgICAgICAgICAgICBXID0gYmxhbmtfbWFwLA0KICAgICAgICAgICAgICAgICAgICBFID0gYmxhbmtfbWFwKQ0KICANCiAgZG9uZSA8LSBGQUxTRQ0KICBsb29weSA8LSBGQUxTRQ0KICB3aGlsZSAoIWRvbmUpIHsNCiAgICBiZWVuX2hlcmVbW2N1cl9kaXJdXVtjdXJfcG9zXSA8LSBiZWVuX2hlcmVbW2N1cl9kaXJdXVtjdXJfcG9zXSArIDENCiAgICBpZiAoYmVlbl9oZXJlW1tjdXJfZGlyXV1bY3VyX3Bvc10gPT0gMikgew0KICAgICAgbG9vcHkgPC0gVFJVRQ0KICAgICAgYnJlYWsNCiAgICB9DQogICAgDQogICAgY3VyX21vdmUgPC0gbW92ZXMgfD4NCiAgICAgIGZpbHRlcihub3cgPT0gY3VyX2RpcikNCiAgICANCiAgICBuZXdfcG9zIDwtIG1hdHJpeChjKE5BLCBOQSksIG5yb3cgPSAxLCBuY29sID0gMikNCiAgICBuZXdfcG9zWzFdIDwtIGN1cl9wb3NbMV0gKyBjdXJfbW92ZSRyb3dfc3RlcA0KICAgIG5ld19wb3NbMl0gPC0gY3VyX3Bvc1syXSArIGN1cl9tb3ZlJGNvbF9zdGVwDQogICAgDQogICAgaWYgKCFiZXR3ZWVuKG5ld19wb3NbMV0sIDEsIG5yb3codGhlX21hcCkpIHx8DQogICAgICAgICFiZXR3ZWVuKG5ld19wb3NbMl0sIDEsIG5jb2wodGhlX21hcCkpKSB7DQogICAgICBkb25lIDwtIFRSVUUNCiAgICB9DQogICAgZWxzZSBpZiAodGhlX21hcFtuZXdfcG9zXSAhPSAiIyIpIHsNCiAgICAgIGN1cl9wb3MgPC0gbmV3X3Bvcw0KICAgIH0NCiAgICBlbHNlICB7DQogICAgICBjdXJfZGlyIDwtIGN1cl9tb3ZlJHRoZW4NCiAgICB9DQogIH0NCiAgDQogIGxvb3B5DQp9DQpgYGANCg0KU3RpY2sgYSBibG9jayBhdCAoeCx5KSBhbmQgY2hlY2sgaWYgdGhlcmUncyBhIGxvb3AuDQoNCmBgYHtyfQ0KYmxvY2tfbG9vcHMgPC0gZnVuY3Rpb24odGhlX21hcCwgeCwgeSkgew0KICB0aGVfbWFwW3gsIHldIDwtICIjIg0KICBoYXNfbG9vcCh0aGVfbWFwKQ0KfQ0KYGBgDQoNCg0KRG8gaXQgZm9yIGFsbCB0aGUgbG9jYXRpb25zIHdlIHZpc2l0ZWQgaW4gUGFydCAxLg0KDQpgYGB7cn0NCnRpYygpDQpyZXMgPC0gbWFwMl9sZ2wocG90ZW50aWFsc1ssMV0sDQogICAgICAgICAgICAgICAgcG90ZW50aWFsc1ssMl0sDQogICAgICAgICAgICAgICAgXCh4LCB5KSBibG9ja19sb29wcyhtYXBtYXQsIHgsIHkpLA0KICAgICAgICAgICAgICAgIC5wcm9ncmVzcyA9IFRSVUUpDQp0b2MoKQ0KYGBgDQoNCkZpcnN0IChhbmQgbGFzdCkgdGltZSBydW4sIGl0IHdhcyAxNDQ0MC4xMSBzZWMuDQoNCmBgYHtyfQ0Kc3VtKHJlcykNCmBgYA0K