mvdparser 0.18.1

Extract information from QuakeWorld MVD demos.
Documentation
use std::collections::HashMap;

use crate::player::Player;

#[derive(Clone, Debug, Default, PartialEq)]
pub struct Team {
    pub name: String,
    pub color: [u8; 2],
    pub frags: i32,
    pub ping: u32,
    pub players: Vec<Player>,
}

impl From<&[Player]> for Team {
    fn from(players: &[Player]) -> Self {
        if players.is_empty() {
            return Self::default();
        }

        let mut team = Team {
            name: players[0].team.to_string(),
            players: vec![],
            frags: 0,
            ping: 0,
            color: [0, 0],
        };

        for player in players.iter() {
            team.players.push(player.clone());
            team.frags += player.frags;
            team.ping += player.ping;
        }

        team.ping = (team.ping as f32 / team.players.len() as f32).round() as u32;
        let player_colors: Vec<[u8; 2]> = team.players.iter().map(|p| p.color).collect();
        team.color = majority_color(&player_colors).unwrap_or(players[0].color);

        team
    }
}

pub fn teams_from_players(players: &[Player]) -> Vec<Team> {
    let mut tmap: HashMap<String, Vec<Player>> = HashMap::new();
    for player in players.iter() {
        let teamplayers = tmap.entry(player.team.clone()).or_default();
        teamplayers.push(player.clone());
    }

    let mut teams: Vec<Team> = vec![];

    for teamplayers in tmap.values() {
        teams.push(Team::from(teamplayers.as_slice()))
    }

    teams.sort_by(|a, b| a.name.to_lowercase().cmp(&b.name.to_lowercase()));

    teams
}

fn majority_color(colors: &[[u8; 2]]) -> Option<[u8; 2]> {
    match colors.len() {
        0 => return None,
        1 | 2 => return Some(colors[0]),
        _ => {}
    };

    let mut color_count: HashMap<[u8; 2], u8> = HashMap::new();
    for color in colors.iter() {
        let count = color_count.entry(*color).or_insert(0);
        *count += 1;
    }

    let mut max_color = None;
    let mut max_count = 0;
    for (color, count) in color_count.iter() {
        if *count > max_count {
            max_color = Some(*color);
            max_count = *count;
        }
    }

    max_color
}

#[cfg(test)]
mod tests {
    use pretty_assertions::assert_eq;

    use super::*;

    #[test]
    fn test_teams_from_players() {
        {
            let red_alpha = Player {
                name: "Alpha".to_string(),
                team: "red".to_string(),
                color: [4, 3],
                frags: 54,
                ping: 25,
                auth_username: None,
                auth_cc: None,
                is_bot: false,
            };
            let red_beta = Player {
                name: "Beta".to_string(),
                team: "red".to_string(),
                color: [4, 3],
                frags: 16,
                ping: 12,
                auth_username: None,
                auth_cc: None,
                is_bot: false,
            };
            let blue_gamma = Player {
                name: "Gamma".to_string(),
                team: "blue".to_string(),
                color: [11, 10],
                frags: 29,
                ping: 52,
                auth_username: None,
                auth_cc: None,
                is_bot: false,
            };
            let players = vec![red_alpha.clone(), red_beta.clone(), blue_gamma.clone()];

            assert_eq!(
                teams_from_players(&players),
                vec![
                    Team {
                        name: "blue".to_string(),
                        color: [11, 10],
                        frags: 29,
                        ping: 52,
                        players: vec![blue_gamma,],
                    },
                    Team {
                        name: "red".to_string(),
                        color: [4, 3],
                        frags: 70,
                        ping: 19,
                        players: vec![red_alpha, red_beta,],
                    },
                ]
            );
        }
    }

    #[test]
    fn test_majority_color() {
        {
            let colors = vec![[4, 3], [11, 10]];
            assert_eq!(majority_color(&colors), Some([4, 3]));
        }
        {
            let colors = vec![[4, 3], [11, 10], [0, 0], [11, 10]];
            assert_eq!(majority_color(&colors), Some([11, 10]));
        }
    }
}