vignettes/ocarina-of-time-100-.Rmd
ocarina-of-time-100-.Rmd
I 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).