library(tidyverse)
library(stringr)
library(tictoc)
library(gmp)
dat <- read_lines("aoc09.txt") |>
  strsplit("") |>
  first() |>
  as.numeric()

spec <- data.frame(n = dat) |>
  mutate(type = c(rep(c("data", "free"), floor(n()/2)), "data"),
         i = 1:n(),
         id = floor((i + 1)/2) - 1)

head(spec)
build_disk <- function(id, type, n) {
  data.frame(dat = rep(ifelse(type == "data", id, NA), n))
}
the_disk <- pmap(list(spec$id, spec$type, spec$n), build_disk) |>
  bind_rows()
head(the_disk)

Part 1

Move the blocks:

tic()
start <- 1
end   <- nrow(the_disk)

while (start < end) {
  if (!is.na(the_disk[start, "dat"])) {
    # Find the next free space
    start <- start + 1
  }
  else {
    while (start < end && is.na(the_disk[end, "dat"])) {
      # Find the next data
      end <- end - 1
    }
    if (start < end) {
      # Finally move the data!
      the_disk[start, "dat"] <- the_disk[end, "dat"]
      the_disk[end, "dat"] <- NA
      end <- end - 1
      start <- start + 1
    }
  }
}
toc()
10.42 sec elapsed

Calculate the checksums:

pos <- 0
the_disk$pos <- rep(NA, nrow(the_disk))

for (i in 1:nrow(the_disk)) {
  if (!is.na(the_disk[i, "dat"])) {
    the_disk[i, "pos"] <- pos
    pos <- pos + 1
  }
}

the_disk <- the_disk |>
  mutate(check = pos * dat)
summary(the_disk$check)
     Min.   1st Qu.    Median      Mean   3rd Qu.      Max.      NA's 
        0  41929318 123670738 125973979 208262546 261908897     44944 
sum(the_disk$check |> as.bigz(), na.rm = TRUE)
Big Integer ('bigz') :
[1] 6242766523059

Part 2

TBC…

LS0tDQp0aXRsZTogIkRheSA5OiBEaXNrIEZyYWdtZW50ZXIiDQphdXRob3I6IEFuZGlGDQpkYXRlOiA5IERlYyAyMDI0DQpvdXRwdXQ6IA0KICBodG1sX25vdGVib29rOiANCiAgICBjb2RlX2ZvbGRpbmc6IG5vbmUNCi0tLQ0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KHN0cmluZ3IpDQpsaWJyYXJ5KHRpY3RvYykNCmxpYnJhcnkoZ21wKQ0KYGBgDQoNCmBgYHtyfQ0KZGF0IDwtIHJlYWRfbGluZXMoImFvYzA5LnR4dCIpIHw+DQogIHN0cnNwbGl0KCIiKSB8Pg0KICBmaXJzdCgpIHw+DQogIGFzLm51bWVyaWMoKQ0KDQpzcGVjIDwtIGRhdGEuZnJhbWUobiA9IGRhdCkgfD4NCiAgbXV0YXRlKHR5cGUgPSBjKHJlcChjKCJkYXRhIiwgImZyZWUiKSwgZmxvb3IobigpLzIpKSwgImRhdGEiKSwNCiAgICAgICAgIGkgPSAxOm4oKSwNCiAgICAgICAgIGlkID0gZmxvb3IoKGkgKyAxKS8yKSAtIDEpDQoNCmhlYWQoc3BlYykNCmBgYA0KDQoNCmBgYHtyfQ0KYnVpbGRfZGlzayA8LSBmdW5jdGlvbihpZCwgdHlwZSwgbikgew0KICBkYXRhLmZyYW1lKGRhdCA9IHJlcChpZmVsc2UodHlwZSA9PSAiZGF0YSIsIGlkLCBOQSksIG4pKQ0KfQ0KYGBgDQoNCg0KYGBge3J9DQp0aGVfZGlzayA8LSBwbWFwKGxpc3Qoc3BlYyRpZCwgc3BlYyR0eXBlLCBzcGVjJG4pLCBidWlsZF9kaXNrKSB8Pg0KICBiaW5kX3Jvd3MoKQ0KYGBgDQoNCg0KYGBge3J9DQpoZWFkKHRoZV9kaXNrKQ0KYGBgDQoNCg0KIyMjIFBhcnQgMQ0KDQpNb3ZlIHRoZSBibG9ja3M6DQoNCmBgYHtyfQ0KdGljKCkNCnN0YXJ0IDwtIDENCmVuZCAgIDwtIG5yb3codGhlX2Rpc2spDQoNCndoaWxlIChzdGFydCA8IGVuZCkgew0KICBpZiAoIWlzLm5hKHRoZV9kaXNrW3N0YXJ0LCAiZGF0Il0pKSB7DQogICAgIyBGaW5kIHRoZSBuZXh0IGZyZWUgc3BhY2UNCiAgICBzdGFydCA8LSBzdGFydCArIDENCiAgfQ0KICBlbHNlIHsNCiAgICB3aGlsZSAoc3RhcnQgPCBlbmQgJiYgaXMubmEodGhlX2Rpc2tbZW5kLCAiZGF0Il0pKSB7DQogICAgICAjIEZpbmQgdGhlIG5leHQgZGF0YQ0KICAgICAgZW5kIDwtIGVuZCAtIDENCiAgICB9DQogICAgaWYgKHN0YXJ0IDwgZW5kKSB7DQogICAgICAjIEZpbmFsbHkgbW92ZSB0aGUgZGF0YSENCiAgICAgIHRoZV9kaXNrW3N0YXJ0LCAiZGF0Il0gPC0gdGhlX2Rpc2tbZW5kLCAiZGF0Il0NCiAgICAgIHRoZV9kaXNrW2VuZCwgImRhdCJdIDwtIE5BDQogICAgICBlbmQgPC0gZW5kIC0gMQ0KICAgICAgc3RhcnQgPC0gc3RhcnQgKyAxDQogICAgfQ0KICB9DQp9DQp0b2MoKQ0KYGBgDQoNCkNhbGN1bGF0ZSB0aGUgY2hlY2tzdW1zOg0KDQpgYGB7cn0NCnBvcyA8LSAwDQp0aGVfZGlzayRwb3MgPC0gcmVwKE5BLCBucm93KHRoZV9kaXNrKSkNCg0KZm9yIChpIGluIDE6bnJvdyh0aGVfZGlzaykpIHsNCiAgaWYgKCFpcy5uYSh0aGVfZGlza1tpLCAiZGF0Il0pKSB7DQogICAgdGhlX2Rpc2tbaSwgInBvcyJdIDwtIHBvcw0KICAgIHBvcyA8LSBwb3MgKyAxDQogIH0NCn0NCg0KdGhlX2Rpc2sgPC0gdGhlX2Rpc2sgfD4NCiAgbXV0YXRlKGNoZWNrID0gcG9zICogZGF0KQ0KYGBgDQoNCg0KYGBge3J9DQpzdW1tYXJ5KHRoZV9kaXNrJGNoZWNrKQ0KYGBgDQoNCmBgYHtyfQ0Kc3VtKHRoZV9kaXNrJGNoZWNrIHw+IGFzLmJpZ3ooKSwgbmEucm0gPSBUUlVFKQ0KYGBgDQoNCiMjIyBQYXJ0IDINCg0KVEJDLi4u