library(tidyverse)

The lines have different numbers of reports, so loading it in using a rawer method than the usual read.csv and friends:

dat <- read_lines("aoc02.txt")
head(dat)
[1] "87 90 92 95 96 93"    "12 15 16 17 17"       "26 27 29 31 34 36 40"
[4] "79 80 82 85 87 88 95" "17 20 22 20 23 24"    "10 13 11 12 9"       

That’s a vector of strings. Next, split each string on the space and make a list of numeric vectors:

repl <- strsplit(dat, " ") |> map(as.numeric)
head(repl)
[[1]]
[1] 87 90 92 95 96 93

[[2]]
[1] 12 15 16 17 17

[[3]]
[1] 26 27 29 31 34 36 40

[[4]]
[1] 79 80 82 85 87 88 95

[[5]]
[1] 17 20 22 20 23 24

[[6]]
[1] 10 13 11 12  9

Part 1

The rules are:

Wrap in a function:

test_report <- function(nums) {
  test_diffs <- diff(nums, lag = 1, difference = 1)
  all(test_diffs %in% 1:3) || all(-test_diffs %in% 1:3) 
}

The answer:

res <- map_lgl(repl, test_report)
sum(res)
[1] 483

Part 2

I’m going to brute force this one and try dropping elements even if it’s unnecessary, as I don’t have time to be clever 🙃

robust_test_report <- function(nums) {
  if (test_report(nums)) {
    TRUE
  }
  else {
    res <- FALSE
    for (i in 1:length(nums)) {
      res <- test_report(nums[-i])
      if (res)
        break
    }
    res
  }
}

And it worked:

robust_res <- map_lgl(repl, robust_test_report)
sum(robust_res)
[1] 528
LS0tDQp0aXRsZTogIkRheSAyOiBSZWQtTm9zZWQgUmVwb3J0cyINCmF1dGhvcjogQW5kaUYNCmRhdGU6IDIgRGVjIDIwMjQNCm91dHB1dDogDQogIGh0bWxfbm90ZWJvb2s6IA0KICAgIGNvZGVfZm9sZGluZzogbm9uZQ0KLS0tDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmBgYA0KDQpUaGUgbGluZXMgaGF2ZSBkaWZmZXJlbnQgbnVtYmVycyBvZiByZXBvcnRzLCBzbyBsb2FkaW5nIGl0IGluIHVzaW5nIGEgcmF3ZXIgbWV0aG9kIHRoYW4gdGhlIHVzdWFsIHJlYWQuY3N2IGFuZCBmcmllbmRzOg0KDQpgYGB7cn0NCmRhdCA8LSByZWFkX2xpbmVzKCJhb2MwMi50eHQiKQ0KYGBgDQoNCg0KYGBge3J9DQpoZWFkKGRhdCkNCmBgYA0KDQpUaGF0J3MgYSB2ZWN0b3Igb2Ygc3RyaW5ncy4gTmV4dCwgc3BsaXQgZWFjaCBzdHJpbmcgb24gdGhlIHNwYWNlIGFuZCBtYWtlIGEgbGlzdCBvZiBudW1lcmljIHZlY3RvcnM6DQoNCmBgYHtyfQ0KcmVwbCA8LSBzdHJzcGxpdChkYXQsICIgIikgfD4gbWFwKGFzLm51bWVyaWMpDQpoZWFkKHJlcGwpDQpgYGANCg0KIyMjIFBhcnQgMQ0KDQpUaGUgcnVsZXMgYXJlOg0KDQoqIFRoZSBsZXZlbHMgYXJlIGVpdGhlciBhbGwgaW5jcmVhc2luZyBvciBhbGwgZGVjcmVhc2luZy4NCiogQW55IHR3byBhZGphY2VudCBsZXZlbHMgZGlmZmVyIGJ5IGF0IGxlYXN0IG9uZSBhbmQgYXQgbW9zdCB0aHJlZS4NCg0KV3JhcCBpbiBhIGZ1bmN0aW9uOg0KDQpgYGB7cn0NCnRlc3RfcmVwb3J0IDwtIGZ1bmN0aW9uKG51bXMpIHsNCiAgdGVzdF9kaWZmcyA8LSBkaWZmKG51bXMsIGxhZyA9IDEsIGRpZmZlcmVuY2UgPSAxKQ0KICBhbGwodGVzdF9kaWZmcyAlaW4lIDE6MykgfHwgYWxsKC10ZXN0X2RpZmZzICVpbiUgMTozKSANCn0NCmBgYA0KDQpUaGUgYW5zd2VyOg0KDQpgYGB7cn0NCnJlcyA8LSBtYXBfbGdsKHJlcGwsIHRlc3RfcmVwb3J0KQ0Kc3VtKHJlcykNCmBgYA0KDQoNCiMjIyBQYXJ0IDINCg0KSSdtIGdvaW5nIHRvIGJydXRlIGZvcmNlIHRoaXMgb25lIGFuZCB0cnkgZHJvcHBpbmcgZWxlbWVudHMgZXZlbiBpZiBpdCdzIHVubmVjZXNzYXJ5LCBhcyBJIGRvbid0IGhhdmUgdGltZSB0byBiZSBjbGV2ZXIg8J+Zgw0KDQpgYGB7cn0NCnJvYnVzdF90ZXN0X3JlcG9ydCA8LSBmdW5jdGlvbihudW1zKSB7DQogIGlmICh0ZXN0X3JlcG9ydChudW1zKSkgew0KICAgIFRSVUUNCiAgfQ0KICBlbHNlIHsNCiAgICByZXMgPC0gRkFMU0UNCiAgICBmb3IgKGkgaW4gMTpsZW5ndGgobnVtcykpIHsNCiAgICAgIHJlcyA8LSB0ZXN0X3JlcG9ydChudW1zWy1pXSkNCiAgICAgIGlmIChyZXMpDQogICAgICAgIGJyZWFrDQogICAgfQ0KICAgIHJlcw0KICB9DQp9DQpgYGANCg0KQW5kIGl0IHdvcmtlZDoNCg0KYGBge3J9DQpyb2J1c3RfcmVzIDwtIG1hcF9sZ2wocmVwbCwgcm9idXN0X3Rlc3RfcmVwb3J0KQ0Kc3VtKHJvYnVzdF9yZXMpDQpgYGANCg0KDQoNCg0K