1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
use std::{collections::HashSet, convert::TryFrom};
use getset::Getters;
use serde::Serialize;
use crate::data::{database::Linked, types::*};
#[derive(Debug, Clone, Getters, Serialize)]
#[get = "pub"]
pub struct LeaderboardRun {
rank: u64,
time_ms: u64,
is_tied: bool,
tied_rank: u64,
run: Linked<Run>,
}
pub fn leaderboard(runs: &[Linked<Run>], rank_obsoletes: bool) -> Vec<LeaderboardRun> {
if runs.is_empty() {
return vec![]
}
let mut runs: Vec<Linked<Run>> = runs.to_vec();
let game_id = runs[0].game_id;
let category_id = runs[0].category_id;
let level_id = runs[0].level_id;
assert!(
runs.iter().all(|r| r.game_id == game_id
&& r.category_id == category_id
&& r.level_id == level_id),
"runs must all be from same game and category and level"
);
let game = runs[0].game();
runs.sort_by_key(|run| {
let time_ms = run
.times_ms()
.get(game.primary_timing())
.expect("run missing primary timing");
(time_ms, *run.date(), *run.created())
});
let mut ranked_players: HashSet<&Vec<RunPlayer>> = HashSet::new();
let mut leaderboard: Vec<LeaderboardRun> = vec![];
let mut n = 0;
for run in runs.iter() {
if !rank_obsoletes && !ranked_players.insert(run.players()) {
continue
}
n += 1;
let time_ms = run
.times_ms()
.get(game.primary_timing())
.expect("run missing primary timing");
let rank = u64::try_from(n).unwrap();
let mut tied_rank = rank;
let mut is_tied = false;
if let Some(ref mut previous) = leaderboard.last_mut() {
if time_ms == *previous.time_ms() {
is_tied = true;
previous.is_tied = true;
tied_rank = previous.tied_rank;
}
}
let new = LeaderboardRun {
rank,
time_ms,
is_tied,
tied_rank,
run: run.clone(),
};
leaderboard.push(new);
}
leaderboard
}