vignettes/ocarina-of-time-100-.Rmd
      ocarina-of-time-100-.RmdI like Ocarina of Time 100%. So let’s take a look at that.
First up we need the game’s ID and category ID. That’s easy:
library(speedrunr)
get_games("Ocarina of Time")
#> # A tibble: 7 x 9
#>   id    name_internatio… name_twitch name_abbr weblink released  
#>   <chr> <chr>            <chr>       <chr>     <chr>   <date>    
#> 1 j1l9… The Legend of Z… The Legend… oot       https:… 1998-11-21
#> 2 kdkj… The Legend of Z… The Legend… ootmq     https:… 2002-01-01
#> 3 268v… The Legend of Z… The Legend… oot3d     https:… 2011-06-16
#> 4 nd2q… Roblox Ocarina … ROBLOX      root      https:… 2008-10-24
#> 5 76rk… Ocarina of Time… The Legend… ootextras https:… 1998-11-21
#> 6 m1zr… Ocarina of Time… Ocarina of… ootbq     https:… 2015-11-13
#> 7 v1po… SM64: Ocarina o… SM64: Ocar… sm64oot   https:… 2018-03-26
#> # … with 3 more variables: released_year <int>, romhack <lgl>,
#> #   created <dttm>get_categories("j1l9qz1g")
#> # A tibble: 12 x 6
#>    id     name    link            type  miscellaneous rules                
#>    <chr>  <chr>   <chr>           <chr> <lgl>         <chr>                
#>  1 q255j… 100%    https://www.sp… per-… FALSE         "**100% Rules**:\r\n…
#>  2 824qn… 100%    https://www.sp… per-… FALSE         "General Rules:\r\n-…
#>  3 zdnoz… All Du… https://www.sp… per-… FALSE         "**All Dungeons Rule…
#>  4 z275w… Any%    https://www.sp… per-… FALSE         "**Any% Rules**:\r\n…
#>  5 02qe4… Any%    https://www.sp… per-… FALSE         "General Rules:\r\n-…
#>  6 9kvr8… Ganonl… https://www.sp… per-… TRUE          "**Ganonless Rules**…
#>  7 zd35j… Glitch… https://www.sp… per-… FALSE         ""                   
#>  8 ndxlw… Glitch… https://www.sp… per-… FALSE         "General Rules:\r\n-…
#>  9 jdrwr… MST     https://www.sp… per-… FALSE         "**Medallions/Stones…
#> 10 9d85y… No IM/… https://www.sp… per-… FALSE         "**No IM/WW Rules**:…
#> 11 xd1wj… No Wro… https://www.sp… per-… TRUE          "**No Wrong Warp Rul…
#> 12 rklm8… Restri… https://www.sp… per-… TRUE          "General Rules:\r\n-…So we’re good.
game <- "j1l9qz1g"
category <- "q255jw2o"
oot100 <- get_runs(game = "j1l9qz1g", category = "q255jw2o", max = Inf)
#> Warning in sr_get("runs", game = game, category = category, max = max,
#> status = status, : Bad Gateway (HTTP 502).
#> Warning: Unknown or uninitialised column: 'rel'.Note that I’m useing max = Inf to make sure I get all the runs.
str(oot100)
#> Classes 'tbl_df', 'tbl' and 'data.frame':    440 obs. of  22 variables:
#>  $ id             : chr  "y43vwl3z" "y430d2dz" "yd3orkxz" "mk9rgxxz" ...
#>  $ weblink        : chr  "https://www.speedrun.com/oot/run/y43vwl3z" "https://www.speedrun.com/oot/run/y430d2dz" "https://www.speedrun.com/oot/run/yd3orkxz" "https://www.speedrun.com/oot/run/mk9rgxxz" ...
#>  $ game           : chr  "j1l9qz1g" "j1l9qz1g" "j1l9qz1g" "j1l9qz1g" ...
#>  $ level          : logi  NA NA NA NA NA NA ...
#>  $ category       : chr  "q255jw2o" "q255jw2o" "q255jw2o" "q255jw2o" ...
#>  $ videos         : chr  "https://www.twitch.tv/videos/398083535" "https://www.twitch.tv/videos/396193532" "https://www.twitch.tv/videos/391649283" "https://www.twitch.tv/videos/391630492" ...
#>  $ status         : chr  "verified" "verified" "verified" "verified" ...
#>  $ comment        : chr  NA "didn't expect my second valid run to be a real pb, i think i'm gonna really try to get into it again" "i almost give up but i go get the heart piece i forget and then run up the castle" NA ...
#>  $ player_id      : chr  "kj9m2d7x" "68wvk24x" "kj9m2d7x" "68wvk24x" ...
#>  $ player_url     : chr  "https://www.speedrun.com/user/Kneeper" "https://www.speedrun.com/user/gummee" "https://www.speedrun.com/user/Kneeper" "https://www.speedrun.com/user/gummee" ...
#>  $ player_name    : chr  "Kneeper" "gummee" "Kneeper" "gummee" ...
#>  $ player_role    : chr  "user" "user" "user" "user" ...
#>  $ player_signup  : POSIXct, format: "2019-03-07 08:22:06" "2019-03-07 06:43:08" ...
#>  $ date           : Date, format: "2019-03-19" "2019-03-15" ...
#>  $ submitted      : POSIXct, format: "2019-03-20 03:05:21" "2019-03-16 03:20:52" ...
#>  $ time_primary   : int  19937 18773 21572 19416 16156 15282 16304 15389 16317 20014 ...
#>  $ time_realtime  : int  19937 18773 21572 19416 16156 15282 16304 15389 16317 20014 ...
#>  $ time_ingame    : int  0 0 0 0 0 0 0 0 0 0 ...
#>  $ time_hms       : 'hms' num  05:32:17 05:12:53 05:59:32 05:23:36 ...
#>   ..- attr(*, "units")= chr "secs"
#>  $ system_platform: chr  "nzelreqp" "w89rwelk" "nzelreqp" "w89rwelk" ...
#>  $ system_emulated: logi  FALSE FALSE FALSE FALSE FALSE FALSE ...
#>  $ system_region  : chr  "o316x197" "o316x197" "o316x197" "o316x197" ...We want some additional data:
library(dplyr)
library(knitr)
oot100 <- oot100 %>%
  add_platforms() %>%
  add_regions() %>%
  find_records()
oot100 %>% 
  arrange(time_hms) %>%
  select(time_hms, player_name, date, system_platform, system_region) %>%
  head(10) %>%
  kable()| time_hms | player_name | date | system_platform | system_region | 
|---|---|---|---|---|
| 03:53:33 | zfg | 2018-11-27 | Wii Virtual Console | JPN / NTSC | 
| 03:54:52 | zfg | 2018-10-30 | Wii Virtual Console | JPN / NTSC | 
| 03:56:08 | zfg | 2018-09-22 | Wii Virtual Console | JPN / NTSC | 
| 03:57:38 | zfg | 2018-08-20 | Wii Virtual Console | JPN / NTSC | 
| 03:58:28 | Marco | 2018-10-11 | Wii Virtual Console | JPN / NTSC | 
| 03:58:45 | zfg | 2018-08-01 | Wii Virtual Console | JPN / NTSC | 
| 03:59:17 | Marco | 2018-09-29 | Wii Virtual Console | JPN / NTSC | 
| 04:00:47 | Marco | 2018-09-17 | Wii Virtual Console | JPN / NTSC | 
| 04:01:05 | zfg | 2018-07-27 | Wii Virtual Console | JPN / NTSC | 
| 04:01:39 | Marco | 2018-09-01 | Wii Virtual Console | JPN / NTSC | 
Now we can take a look at the categories (recent) history, with highlighted records:
library(ggplot2)
library(ggrepel)
library(hrbrthemes)
library(hms)
oot100 %>%
  filter(time_hms < hms::hms(hours = 6)) %>%
  {
    ggplot(., aes(date, time_hms)) +
      geom_point(size = 1, alpha = .75) +
      geom_point(size = 2, data = filter(., record), aes(color = player_name)) +
      geom_label_repel(data = filter(., record),
                       aes(label = time_hms, color = player_name),
                       fill = "white", show.legend = F) +
      scale_x_date(date_breaks = "6 months", date_labels = "%b '%y") +
      scale_y_time(breaks = seq(2 * 60^2, 20 * 60^2,  1/3 * 60^2),
                   minor_breaks = seq(2 * 60^2, 20 * 60^2, 5 * 60)) +
      scale_color_brewer(palette = "Dark2") +
      labs(title = "Ocarina of Time: 100% Speedrun Record History",
           subtitle = paste0("All data from speedrun.com (n = ", nrow(.), ")"),
           x = "Date of Run", y = "Time",
           color = "Runner", caption = "Data limited to sub 6h runs") +
      theme_ipsum() +
      theme(legend.position = "top")
  }
Please note that the data on speedrun.com does not cover the whole history. There are lots of older runs missing here, and unless the mods come together und do some historic backlogging, that’s as good as it gets for now.
We can also take a look at the recent developments:
library(lubridate)
tmp <- oot100 %>%
  filter(time_hms < hms::hms(hours = 4, minutes = 30),
         date >= ymd("2018-01-01")) %>%
  select(player_name, time_hms, date)
bind_rows(
  tmp,
  tmp %>%
    group_by(player_name) %>%
    summarize(time_hms = min(time_hms), date = today()) %>%
    ungroup
) %>%
  {
    ggplot(., aes(date, time_hms, color = player_name, fill = player_name)) +
      geom_point(size = 1, alpha = .75) +
      geom_step() +
      geom_label_repel(data = .
                       %>% group_by(player_name) %>%
                         summarize(y = min(time_hms), x = max(date)),
                       aes(label = paste0(player_name, ": ", y), x = x, y = y),
                       color = "black", alpha = .75, show.legend = F,
                       hjust = 1, direction = "y", nudge_x = 60^2) +
      scale_x_date(date_breaks = "1 month", date_labels = "%b '%y",
                   limits = c(as.Date(NA), today() + days(45))) +
      scale_y_time(breaks = seq(0, 20 * 60^2,  1/12 * 60^2),
                   minor_breaks = seq(0, 20 * 60^2, 1/24 * 60)) +
      scale_color_brewer(palette = "Dark2", guide = F) +
      scale_fill_brewer(palette = "Dark2", guide = F) +
      labs(title = "Ocarina of Time: 100% Speedruns in 2018",
           subtitle = "All data from speedrun.com",
           x = "Date of Run", y = "Time (H:M:S)") +
      theme_ipsum(grid = "X") + theme(axis.text.x = element_text(hjust = 0))
  }
#> Warning in RColorBrewer::brewer.pal(n, pal): n too large, allowed maximum for palette Dark2 is 8
#> Returning the palette you asked for with that many colors
#> Warning in RColorBrewer::brewer.pal(n, pal): n too large, allowed maximum for palette Dark2 is 8
#> Returning the palette you asked for with that many colors
#> Warning: Removed 42 rows containing missing values (geom_point).
#> Warning: Removed 42 rows containing missing values (geom_path).