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
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
use std::collections::HashMap;

use getset::Getters;
use itertools::Itertools;
use serde::Serialize;

use crate::data::{
    database::Linked,
    leaderboard::{leaderboard, LeaderboardRun},
    types::*,
};

#[derive(Debug, Clone, Getters, Serialize)]
#[get = "pub"]
pub struct ProgressionRun {
    progress_ms:     u64,
    run:             Linked<Run>,
    leaderboard_run: Option<LeaderboardRun>,
}

pub fn progression(runs: &[Linked<Run>]) -> Vec<ProgressionRun> {
    if runs.is_empty() {
        return vec![]
    }

    let game_id = runs[0].game_id;
    let category_id = runs[0].category_id;
    assert!(
        runs.iter()
            .all(|r| r.game_id == game_id && r.category_id == category_id),
        "runs must all be from same game and category"
    );

    let runs_by_level: HashMap<Option<u64>, Vec<Linked<Run>>> = runs
        .iter()
        .sorted_by(|a, b| {
            a.date()
                .cmp(&b.date())
                .then(a.created().cmp(&b.created()))
                .then(a.id().cmp(&b.id()))
        })
        .map(|run| (run.level_id, run.clone()))
        .into_group_map();

    let mut progression: Vec<ProgressionRun> = Vec::new();

    for (_level_id, runs) in runs_by_level.iter().sorted_by(|a, b| a.0.cmp(b.0)) {
        let mut best_ms: Option<u64> = None;

        let mut leaderboard_runs_by_id: HashMap<u64, LeaderboardRun> = HashMap::new();
        for leaderboard_run in leaderboard(&runs.to_vec(), false) {
            let id = *leaderboard_run.run().id();
            leaderboard_runs_by_id.insert(id, leaderboard_run);
        }

        for run in runs.iter() {
            let is_progress;
            let mut progress_ms = 0;
            match best_ms {
                None => {
                    is_progress = true;
                }
                Some(best_ms) => {
                    is_progress = run.time_ms() < best_ms;
                    if is_progress {
                        progress_ms = best_ms - run.time_ms();
                    }
                }
            }

            if is_progress {
                progression.push(ProgressionRun {
                    progress_ms,
                    run: run.clone(),
                    leaderboard_run: leaderboard_runs_by_id.remove(run.id()),
                });
                best_ms = Some(run.time_ms());
            }
        }
    }

    // let runs: Vec<Linked<Run>> = runs.to_vec();

    // if runs.is_empty() {
    //     return vec![];
    // }

    // let mut progression: Vec<ProgressionRun> = vec![];

    // for run in runs.iter() {
    //     // let new = ProgressionRun { run: run.clone() };

    //     // progression.push(new);
    // }

    // reverse-chronologial
    progression.sort_by(|a, b| {
        b.run
            .date()
            .cmp(&a.run.date())
            .then(b.run.created().cmp(&a.run.created()))
    });
    progression
}