Skip to content

Commit

Permalink
Refactor teams.
Browse files Browse the repository at this point in the history
  • Loading branch information
vikpe committed May 30, 2024
1 parent 0ca2da5 commit 1a04410
Show file tree
Hide file tree
Showing 4 changed files with 146 additions and 70 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ categories = ["parsing"]
keywords = ["demos", "mvd", "parser", "quake", "quakeworld"]
repository = "https://github.com/vikpe/mvdparser"
authors = ["Viktor Persson <viktor.persson@arcsin.se>"]
version = "0.8.3"
version = "0.9.0"
edition = "2021"
license = "MIT"
include = [
Expand Down
2 changes: 1 addition & 1 deletion src/mvd/mod.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
pub mod io;
pub mod message;
pub mod message;
141 changes: 141 additions & 0 deletions src/team.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::collections::HashMap;

use crate::player::Player;

#[derive(Clone, Debug, Default, PartialEq)]
Expand All @@ -8,3 +10,142 @@ pub struct Team {
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
}

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,
is_bot: false,
};
let red_beta = Player {
name: "Beta".to_string(),
team: "red".to_string(),
color: [4, 3],
frags: 16,
ping: 12,
is_bot: false,
};
let blue_gamma = Player {
name: "Gamma".to_string(),
team: "blue".to_string(),
color: [11, 10],
frags: 29,
ping: 52,
is_bot: false,
};
let players = vec![red_alpha.clone(), red_beta.clone(), blue_gamma.clone()];

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

#[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]));
}
}
}
71 changes: 3 additions & 68 deletions src/teams.rs
Original file line number Diff line number Diff line change
@@ -1,63 +1,10 @@
use std::collections::HashMap;

use anyhow::Result;

use crate::players;
use crate::team::Team;
use crate::team::{teams_from_players, Team};

pub fn teams(data: &[u8]) -> Result<Vec<Team>> {
let players = players(data)?;

let mut tmap: HashMap<String, Team> = HashMap::new();
for player in players.iter() {
let team = tmap.entry(player.team.clone()).or_insert(Team {
name: player.team.clone(),
players: vec![],
frags: 0,
ping: 0,
color: [0, 0],
});

team.players.push(player.clone());
team.frags += player.frags;
team.ping += player.ping;
}

let mut teams: Vec<Team> = tmap.values().cloned().collect();
teams.sort_by(|b, a| a.frags.cmp(&b.frags));

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

Ok(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
Ok(teams_from_players(&players(data)?))
}

#[cfg(test)]
Expand All @@ -72,7 +19,7 @@ mod tests {
use super::*;

#[test]
fn test_players() -> Result<()> {
fn test_teams() -> Result<()> {
{
let demo_data = read("tests/files/4on4_oeks_vs_tsq[dm2]20240426-1716.mvd");
assert_eq!(
Expand Down Expand Up @@ -164,16 +111,4 @@ mod tests {

Ok(())
}

#[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]));
}
}
}

0 comments on commit 1a04410

Please sign in to comment.