openskill 0.0.1

Rust implementation of OpenSkill (license free TrueSkill)
Documentation
use crate::constant::{DEFAULT_MU, DEFAULT_SIGMA};
use std::{fmt::Display, iter::zip};

#[derive(Debug, Clone)]
pub struct Rating {
    pub mu: f64,
    pub sigma: f64,
}

impl Display for Rating {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "(mu: {}, sigma: {})", self.mu, self.sigma)
    }
}

impl Default for Rating {
    fn default() -> Self {
        Self {
            mu: DEFAULT_MU,
            sigma: DEFAULT_SIGMA,
        }
    }
}

impl Rating {
    pub fn new(mu: f64, sigma: f64) -> Self {
        Self { mu, sigma }
    }
}

#[derive(Debug, Clone)]
pub struct TeamRating {
    pub members: Vec<Rating>,
    pub mu: f64,
    pub sigma_sq: f64,
    pub rank: usize,
}

impl TeamRating {
    pub fn new(members: Vec<Rating>, rank: usize) -> Self {
        let mu = members.iter().map(|m| m.mu).sum::<f64>();
        let sigma_sq = members.iter().map(|m| m.sigma.powi(2)).sum::<f64>();
        Self {
            members,
            mu,
            sigma_sq,
            rank,
        }
    }

    pub fn team_size(&self) -> usize {
        self.members.len()
    }
}

pub(crate) fn to_team_ratings(
    teams: &Vec<Vec<Rating>>,
    ranks: Option<Vec<usize>>,
) -> Vec<TeamRating> {
    let ranks = ranks.unwrap_or_else(|| (0..teams.len()).collect());
    zip(teams.iter(), ranks.iter())
        .map(|(team, rank)| TeamRating::new(team.clone(), *rank))
        .collect()
}

pub(crate) fn calc_total_player(team_ratings: &Vec<TeamRating>) -> usize {
    team_ratings.iter().map(|it| it.team_size()).sum::<usize>()
}

pub struct GameResult {
    pub teams: Vec<Vec<Rating>>,
    pub ranks: Vec<usize>,
}

impl GameResult {
    pub fn new(teams: Vec<Vec<Rating>>, ranks: Vec<usize>) -> Self {
        Self { teams, ranks }
    }
}

pub type GammaFunc = fn(f64, f64, team: &TeamRating) -> f64;

pub fn default_gamma(c: f64, _: f64, team: &TeamRating) -> f64 {
    team.sigma_sq.sqrt() / c
}

pub type OrdinalFunc = fn(&Rating, f64) -> f64;

pub fn default_ordinal(rating: &Rating, z: f64) -> f64 {
    rating.mu - (z * rating.sigma)
}

pub fn ladder_pairs<T>(ranks: &Vec<T>) -> Vec<Vec<T>>
where
    T: Clone,
{
    let o_ranks: Vec<Option<T>> = ranks.clone().into_iter().map(Some).collect();
    let left: Vec<_> = [None]
        .iter()
        .cloned()
        .chain(o_ranks[..o_ranks.len() - 1].iter().cloned())
        .collect();
    let right = vec![&o_ranks[1..], &[None]].concat();
    return zip(left, right)
        .map(|(l, r)| match (l, r) {
            (Some(l), Some(r)) => vec![l, r],
            (Some(l), None) => vec![l],
            (None, Some(r)) => vec![r],
            _ => vec![],
        })
        .collect();
}