input<-read_lines("Day14Sample.txt")

robotframe<-as.data.frame(matrix(ncol=4,nrow=0))
for(i in 1:length(input)){
  y<-as.numeric(unlist(str_extract_all(input[i],"-?\\d+")))
  robotframe<-rbind(robotframe,y)}
colnames(robotframe)<-c("px","py","vx","vy")

Part 1

Move everything about in the frame and then count how many are in each quadrant

safetyfactor<-function(rf,max_x,max_y,s){
  midx<-max_x%/%2
  midy<-max_y%/%2
  rf<-rf%>%rowwise%>%
    ### find the final x & y position
    mutate(finalx=((px+s*vx)%%max_x),finaly=((py+s*vy)%%max_y)) %>%
    ### find which quadrant it is in
    mutate(quad=if_else(finalx<midx&&finaly<midy,1,
                        if_else(finalx>midx&&finaly<midy,2,
                                if_else(finalx<midx&&finaly>midy,3,
                                        if_else(finalx>midx&&finaly>midy,4,0)))))
  sf<-rf%>%count(quad)%>%filter(quad!=0)%>%arrange(quad)
  sf<-sf$n
  sf}
p1<-safetyfactor(robotframe,11,7,100)
p1
[1] 1 3 4 1
part1<-prod(p1)
part1
[1] 12

Part 2

Because there is a repeat every 10403, animate and watch that long is a possibility, but that’s a lot of rendering and watching

My first idea was that the tree would be centered around the midline - so I was looking for symmetry across the quadrants - that went nowhere.

Then, I thought about looking for anomalies in the safety factor -

This slightly alters the safetyfactor function from above

safac2<-function(rf,max_x,max_y,s){
  midx<-max_x%/%2
  midy<-max_y%/%2
  rf<-rf%>%rowwise%>%
    ### find the final x & y position
    mutate(finalx=((px+s*vx)%%max_x),finaly=((py+s*vy)%%max_y)) %>%
    select(finalx,finaly)%>%
    ### find which quadrant it is in
    mutate(quad=if_else(finalx<midx&&finaly<midy,1,
                        if_else(finalx>midx&&finaly<midy,2,
                                if_else(finalx<midx&&finaly>midy,3,
                                        if_else(finalx>midx&&finaly>midy,4,0)))))
  sf<-rf%>%count(quad)%>%filter(quad!=0)%>%arrange(quad)
  sf<-sf$n
  sf<-c(sf,prod(sf))
  sf}

### find tree runs this through all repeats.  It also takes forever.

findtree<-function(rf,max_x,max_y){
  treeframe<-as.data.frame(matrix(ncol=6,nrow=0))
  for(i in 1:(max_x*max_y)){
    t<-safac2(rf,max_x,max_y,i)
    treeframe<-rbind(treeframe,c(i,t))}
  colnames(treeframe)<-c("dex","q1","q2","q3","q4","safetyfactor")
  treeframe}

Running this allowed me to look for clumps

quadsearch<-findtree(robotframe,101,103)

The plot shows that right around 7000, theres an especially large group of robots in q1.

And plotting that one specifically shows

plottree<-function(rf,max_x,max_y,s){
  midx<-max_x%/%2
  midy<-max_y%/%2
  rf<-rf%>%rowwise%>%
    ### find the final x & y position
    mutate(finalx=((px+s*vx)%%max_x),finaly=((py+s*vy)%%max_y)) %>%
    ### find which quadrant it is in
    select(finalx,finaly)
  rf}

sevenzerofivefive<-plottree(robotframe,101,103,7055)

second_7055<-ggplot(data=sevenzerofivefive,aes(x=finalx,y=finaly))+
  geom_point()+
  theme(axis.text.x = element_blank(),
        axis.ticks.x = element_blank(),
        axis.text.y = element_blank(),
        axis.ticks.y = element_blank(),
        axis.title.y = element_blank(),
        axis.title.x = element_blank(),
        panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        legend.position="none")+
  scale_y_reverse()+
  coord_fixed()
second_7055
Tree

Tree

Finally - here’s a sped up version of the 4 minutes before the tree appears.

Robots Wandering

Robots Wandering

LS0tDQp0aXRsZTogIkRheSAxNCBOb3RlYm9vayINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0KbGlicmFyeShSQ29sb3JCcmV3ZXIpDQpsaWJyYXJ5KGdnYW5pbWF0ZSkNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkocmVzaGFwZTIpDQpsaWJyYXJ5KGtuaXRyKQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkoc3RyaW5ncikNCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShyZWFkcikNCmxpYnJhcnkoY29sbGVjdGlvbnMpDQpvcHRpb25zKHNjaXBlbiA9IDk5OSkNCmBgYA0KDQpgYGB7cn0NCmlucHV0PC1yZWFkX2xpbmVzKCJEYXkxNFNhbXBsZS50eHQiKQ0KDQpyb2JvdGZyYW1lPC1hcy5kYXRhLmZyYW1lKG1hdHJpeChuY29sPTQsbnJvdz0wKSkNCmZvcihpIGluIDE6bGVuZ3RoKGlucHV0KSl7DQogIHk8LWFzLm51bWVyaWModW5saXN0KHN0cl9leHRyYWN0X2FsbChpbnB1dFtpXSwiLT9cXGQrIikpKQ0KICByb2JvdGZyYW1lPC1yYmluZChyb2JvdGZyYW1lLHkpfQ0KY29sbmFtZXMocm9ib3RmcmFtZSk8LWMoInB4IiwicHkiLCJ2eCIsInZ5IikNCmBgYA0KDQojIyBQYXJ0IDENCk1vdmUgZXZlcnl0aGluZyBhYm91dCBpbiB0aGUgZnJhbWUgYW5kIHRoZW4gY291bnQgaG93IG1hbnkgYXJlIGluIGVhY2ggcXVhZHJhbnQNCmBgYHtyfQ0Kc2FmZXR5ZmFjdG9yPC1mdW5jdGlvbihyZixtYXhfeCxtYXhfeSxzKXsNCiAgbWlkeDwtbWF4X3glLyUyDQogIG1pZHk8LW1heF95JS8lMg0KICByZjwtcmYlPiVyb3d3aXNlJT4lDQogICAgIyMjIGZpbmQgdGhlIGZpbmFsIHggJiB5IHBvc2l0aW9uDQogICAgbXV0YXRlKGZpbmFseD0oKHB4K3MqdngpJSVtYXhfeCksZmluYWx5PSgocHkrcyp2eSklJW1heF95KSkgJT4lDQogICAgIyMjIGZpbmQgd2hpY2ggcXVhZHJhbnQgaXQgaXMgaW4NCiAgICBtdXRhdGUocXVhZD1pZl9lbHNlKGZpbmFseDxtaWR4JiZmaW5hbHk8bWlkeSwxLA0KICAgICAgICAgICAgICAgICAgICAgICAgaWZfZWxzZShmaW5hbHg+bWlkeCYmZmluYWx5PG1pZHksMiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZfZWxzZShmaW5hbHg8bWlkeCYmZmluYWx5Pm1pZHksMywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZl9lbHNlKGZpbmFseD5taWR4JiZmaW5hbHk+bWlkeSw0LDApKSkpKQ0KICBzZjwtcmYlPiVjb3VudChxdWFkKSU+JWZpbHRlcihxdWFkIT0wKSU+JWFycmFuZ2UocXVhZCkNCiAgc2Y8LXNmJG4NCiAgc2Z9DQpgYGANCg0KYGBge3J9DQpwMTwtc2FmZXR5ZmFjdG9yKHJvYm90ZnJhbWUsMTEsNywxMDApDQpwMQ0KcGFydDE8LXByb2QocDEpDQpwYXJ0MQ0KYGBgDQoNCg0KIyMgUGFydCAyDQpCZWNhdXNlIHRoZXJlIGlzIGEgcmVwZWF0IGV2ZXJ5IDEwNDAzLCBhbmltYXRlIGFuZCB3YXRjaCB0aGF0IGxvbmcgaXMgYSBwb3NzaWJpbGl0eSwgYnV0IHRoYXQncyBhIGxvdCBvZiByZW5kZXJpbmcgYW5kIHdhdGNoaW5nDQoNCk15IGZpcnN0IGlkZWEgd2FzIHRoYXQgdGhlIHRyZWUgd291bGQgYmUgY2VudGVyZWQgYXJvdW5kIHRoZSBtaWRsaW5lIC0gc28gSSB3YXMgbG9va2luZyBmb3Igc3ltbWV0cnkgYWNyb3NzIHRoZSBxdWFkcmFudHMgLSB0aGF0IHdlbnQgbm93aGVyZS4NCg0KDQoNClRoZW4sIEkgdGhvdWdodCBhYm91dCBsb29raW5nIGZvciBhbm9tYWxpZXMgaW4gdGhlIHNhZmV0eSBmYWN0b3IgLSANCg0KVGhpcyBzbGlnaHRseSBhbHRlcnMgdGhlIHNhZmV0eWZhY3RvciBmdW5jdGlvbiBmcm9tIGFib3ZlDQoNCmBgYHtyfQ0Kc2FmYWMyPC1mdW5jdGlvbihyZixtYXhfeCxtYXhfeSxzKXsNCiAgbWlkeDwtbWF4X3glLyUyDQogIG1pZHk8LW1heF95JS8lMg0KICByZjwtcmYlPiVyb3d3aXNlJT4lDQogICAgIyMjIGZpbmQgdGhlIGZpbmFsIHggJiB5IHBvc2l0aW9uDQogICAgbXV0YXRlKGZpbmFseD0oKHB4K3MqdngpJSVtYXhfeCksZmluYWx5PSgocHkrcyp2eSklJW1heF95KSkgJT4lDQogICAgc2VsZWN0KGZpbmFseCxmaW5hbHkpJT4lDQogICAgIyMjIGZpbmQgd2hpY2ggcXVhZHJhbnQgaXQgaXMgaW4NCiAgICBtdXRhdGUocXVhZD1pZl9lbHNlKGZpbmFseDxtaWR4JiZmaW5hbHk8bWlkeSwxLA0KICAgICAgICAgICAgICAgICAgICAgICAgaWZfZWxzZShmaW5hbHg+bWlkeCYmZmluYWx5PG1pZHksMiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZfZWxzZShmaW5hbHg8bWlkeCYmZmluYWx5Pm1pZHksMywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZl9lbHNlKGZpbmFseD5taWR4JiZmaW5hbHk+bWlkeSw0LDApKSkpKQ0KICBzZjwtcmYlPiVjb3VudChxdWFkKSU+JWZpbHRlcihxdWFkIT0wKSU+JWFycmFuZ2UocXVhZCkNCiAgc2Y8LXNmJG4NCiAgc2Y8LWMoc2YscHJvZChzZikpDQogIHNmfQ0KDQojIyMgZmluZCB0cmVlIHJ1bnMgdGhpcyB0aHJvdWdoIGFsbCByZXBlYXRzLiAgSXQgYWxzbyB0YWtlcyBmb3JldmVyLg0KDQpmaW5kdHJlZTwtZnVuY3Rpb24ocmYsbWF4X3gsbWF4X3kpew0KICB0cmVlZnJhbWU8LWFzLmRhdGEuZnJhbWUobWF0cml4KG5jb2w9Nixucm93PTApKQ0KICBmb3IoaSBpbiAxOihtYXhfeCptYXhfeSkpew0KICAgIHQ8LXNhZmFjMihyZixtYXhfeCxtYXhfeSxpKQ0KICAgIHRyZWVmcmFtZTwtcmJpbmQodHJlZWZyYW1lLGMoaSx0KSl9DQogIGNvbG5hbWVzKHRyZWVmcmFtZSk8LWMoImRleCIsInExIiwicTIiLCJxMyIsInE0Iiwic2FmZXR5ZmFjdG9yIikNCiAgdHJlZWZyYW1lfQ0KYGBgDQoNClJ1bm5pbmcgdGhpcyBhbGxvd2VkIG1lIHRvIGxvb2sgZm9yIGNsdW1wcw0KDQpgYGB7cixldmFsPUZBTFNFfQ0KcXVhZHNlYXJjaDwtZmluZHRyZWUocm9ib3RmcmFtZSwxMDEsMTAzKQ0KYGBgDQoNCg0KDQpgYGB7cixlY2hvPUZBTFNFLGV2YWw9RkFMU0V9DQpsb29rZm9yY2x1bXBzPC1nZ3Bsb3QoZGF0YT1xdWFkc2VhcmNoKSsNCiAgZ2VvbV9wYXRoKGFlcyh4PWRleCx5PXExKSxjb2xvcj0iI0ZGMDBGRiIpKw0KICBnZW9tX3BhdGgoYWVzKHg9ZGV4LHk9cTIpLGNvbG9yPSIjMDBGRjAwIikrDQogIGdlb21fcGF0aChhZXMoeD1kZXgseT1xMyksY29sb3I9IiMwMDAwRkYiKSsNCiAgZ2VvbV9wYXRoKGFlcyh4PWRleCx5PXE0KSxjb2xvcj0iI0ZGMDAwMCIpKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgYXhpcy50aWNrcy54ID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgYXhpcy50aWNrcy55ID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uPSJub25lIikrDQogIHNjYWxlX3lfcmV2ZXJzZSgpKw0KICBjb29yZF9maXhlZCgpDQpsb29rZm9yY2x1bXBzDQpgYGANCg0KDQpUaGUgcGxvdCBzaG93cyB0aGF0IHJpZ2h0IGFyb3VuZCA3MDAwLCB0aGVyZXMgYW4gZXNwZWNpYWxseSBsYXJnZSBncm91cCBvZiByb2JvdHMgaW4gcTEuICANCg0KQW5kIHBsb3R0aW5nIHRoYXQgb25lIHNwZWNpZmljYWxseSBzaG93cw0KDQpgYGB7cixldmFsPUZBTFNFfQ0KcGxvdHRyZWU8LWZ1bmN0aW9uKHJmLG1heF94LG1heF95LHMpew0KICBtaWR4PC1tYXhfeCUvJTINCiAgbWlkeTwtbWF4X3klLyUyDQogIHJmPC1yZiU+JXJvd3dpc2UlPiUNCiAgICAjIyMgZmluZCB0aGUgZmluYWwgeCAmIHkgcG9zaXRpb24NCiAgICBtdXRhdGUoZmluYWx4PSgocHgrcyp2eCklJW1heF94KSxmaW5hbHk9KChweStzKnZ5KSUlbWF4X3kpKSAlPiUNCiAgICAjIyMgZmluZCB3aGljaCBxdWFkcmFudCBpdCBpcyBpbg0KICAgIHNlbGVjdChmaW5hbHgsZmluYWx5KQ0KICByZn0NCg0Kc2V2ZW56ZXJvZml2ZWZpdmU8LXBsb3R0cmVlKHJvYm90ZnJhbWUsMTAxLDEwMyw3MDU1KQ0KDQpzZWNvbmRfNzA1NTwtZ2dwbG90KGRhdGE9c2V2ZW56ZXJvZml2ZWZpdmUsYWVzKHg9ZmluYWx4LHk9ZmluYWx5KSkrDQogIGdlb21fcG9pbnQoKSsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgIGF4aXMudGlja3MueCA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgIGF4aXMudGlja3MueSA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgIGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpKw0KICBzY2FsZV95X3JldmVyc2UoKSsNCiAgY29vcmRfZml4ZWQoKQ0Kc2Vjb25kXzcwNTUNCmBgYA0KDQpgYGB7ciBlY2hvPUZBTFNFLCBmaWcuY2FwPSJUcmVlIiwgb3V0LndpZHRoID0gJzUwJSd9DQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygidHJlZS5wbmciKQ0KYGBgDQoNCg0KRmluYWxseSAtIGhlcmUncyBhIHNwZWQgdXAgdmVyc2lvbiBvZiB0aGUgNCBtaW51dGVzIGJlZm9yZSB0aGUgdHJlZSBhcHBlYXJzLg0KDQpgYGB7cixpbmNsdWRlPUZBTFNFLGV2YWw9RkFMU0V9DQojIyMjIHJlIHJ1bnMgYW5kIHNhdmVzIGV2ZXJ5dGhpbmcgdG8gYSBkYXRhZnJhbWUgLSBhbHNvLCBpbmRpY2F0ZXMNCiMjIyMgdHJhY2tzIHRoZSByb2JvdHMNCnZpc3VhbGVmZmVjdDI8LWZ1bmN0aW9uKHJmLG1heF94LG1heF95LGJlZ2luc2VjLGVuZHNlYyl7DQogIHJmPC1jYmluZChyZixyb2JvdD0xOm5yb3cocmYpKQ0KICBldmVyeXRoaW5nPC1hcy5kYXRhLmZyYW1lKG1hdHJpeChucm93PTAsbmNvbD00KSkNCiAgZm9yKGkgaW4gYmVnaW5zZWM6ZW5kc2VjKXsNCiAgICByZjwtcmYlPiVyb3d3aXNlJT4lDQogICAgIyMjIGZpbmQgdGhlIGZpbmFsIHggJiB5IHBvc2l0aW9uDQogICAgICBtdXRhdGUoZmluYWx4PSgocHgraSp2eCklJW1heF94KSxmaW5hbHk9KChweStpKnZ5KSUlbWF4X3kpKQ0KICAgIHRtcDwtcmYgJT4lIHNlbGVjdChmaW5hbHgsZmluYWx5LHJvYm90KSAlPiUNCiAgICAgIG11dGF0ZShzPWkpDQogICAgZXZlcnl0aGluZzwtcmJpbmQoZXZlcnl0aGluZyx0bXApfQ0KICBldmVyeXRoaW5nfQ0KDQojIyMjIHNhdmVzIHRoZSBkYXRhIGZvciB0aGUgMjUwIGJlZm9yZSB0aGUgdHJlZSBhcHBlYXJzDQoNCnZpc3VhbGl6ZTE0PC12aXN1YWxlZmZlY3QyKHJvYm90ZnJhbWUsMTAxLDEwMyw2ODA2LDcwNTUpDQpgYGANCg0KDQpgYGB7cixpbmNsdWRlPUZBTFNFLGV2YWw9RkFMU0V9DQojIyMjIGNyZWF0ZXMgdGhlIHBsb3RzDQoNCml0c19hX3RyZWU8LWdncGxvdChkYXRhPXZpc3VhbGl6ZTE0KSsNCiAgZ2VvbV9wb2ludChhZXMoeD1maW5hbHgseT1maW5hbHksY29sb3VyPXJvYm90KSxzaXplPTUpKw0KICBzY2FsZV9jb2xvdXJfZ3JhZGllbnRuKGNvbG91cnMgPSBicmV3ZXIucGFsKDksICJHcmVlbnMiKSkrDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBheGlzLnRpY2tzLnggPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBheGlzLnRpY2tzLnkgPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGw9J3RyYW5zcGFyZW50JyksDQogICAgICAgIHBsb3QuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsPSd0cmFuc3BhcmVudCcsIGNvbG9yPU5BKSwNCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uPSJub25lIikrDQogIHNjYWxlX3lfcmV2ZXJzZSgpKw0KICBjb29yZF9maXhlZCgpKw0KICB0cmFuc2l0aW9uX3N0YXRlcyhzLHdyYXA9RkFMU0UpKw0KICBzaGFkb3dfd2FrZSh3YWtlX2xlbmd0aCA9LjA0LHNpemU9LjUsYWxwaGE9Myx3cmFwPUZBTFNFLGV4Y2x1ZGVfcGhhc2U9YygiZXhpdCIpKQ0KDQojIyMjIGNyZWF0ZXMgdGhlIGFuaW1hdGlvbg0KDQoNCiBhbmltX3NhdmUoIml0c19hX3RyZWVfdHNwLmdpZiIsYW5pbWF0ZShpdHNfYV90cmVlLG5mcmFtZXMgPSAyNTAsIGVuZF9wYXVzZT0zMCkpDQpgYGANCg0KDQoNCg0KDQpgYGB7ciBlY2hvPUZBTFNFLCBmaWcuY2FwPSJSb2JvdHMgV2FuZGVyaW5nIiwgb3V0LndpZHRoID0gJzUwJSd9DQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygiaXRzX2FfdHJlZV90c3AuZ2lmIikNCmBgYA0K