diff --git a/src/bot/commands/lobby.rs b/src/bot/commands/lobby.rs index b0c710f..0411d2a 100644 --- a/src/bot/commands/lobby.rs +++ b/src/bot/commands/lobby.rs @@ -12,6 +12,7 @@ use serenity::model::id::{ChannelId, RoleId, UserId}; use serenity::model::Permissions; use crate::bot::commands::MixerCommand; use crate::database::DatabaseContainer; +use crate::mixer::mixer::Mixer; #[derive(Clone)] pub struct LobbyCommand; @@ -149,9 +150,19 @@ impl LobbyCommand { let members = main_channel.members(ctx.cache().unwrap()).await?; let users = members.iter().map(|m| m.user.id).collect::>(); let players = db.get_players(users).await; + let players = db.get_players((0..10).map(|id| UserId(id)).collect()).await; println!("{:?}", players); + let mixer = Mixer::new(players); + if let Some((mut team1, mut team2)) = mixer.select_teams() { + team1.sort_by(|a, b| a.role.cmp(&b.role)); + team2.sort_by(|a, b| a.role.cmp(&b.role)); + let team1: Vec<_> = team1.iter().map(|p| p.player.bn_name.clone()).collect(); + let team2: Vec<_> = team2.iter().map(|p| p.player.bn_name.clone()).collect(); + println!("{:?}\n{:?}", team1, team2); + } + interaction.create_interaction_response(ctx.http(), |response| { response.kind(InteractionResponseType::ChannelMessageWithSource) .interaction_response_data(|message| { diff --git a/src/mixer/mixer.rs b/src/mixer/mixer.rs index d82cf9d..6604308 100644 --- a/src/mixer/mixer.rs +++ b/src/mixer/mixer.rs @@ -29,15 +29,32 @@ impl Mixer { let mut team1 = Vec::new(); let mut team2 = Vec::new(); - for i in 0..10 { + 'outer: for i in 0..10 { let priorities = self.get_players_priority(&players, &team1, &team2); + // let priorities = Self::priorities_to_array(priorities, &players); if let Some(player) = self.get_highest_priority_player(&players, &priorities) { - if i % 2 == 0 { - team1.push(player.clone()); + if Self::full_team_rank(&team1) > Self::full_team_rank(&team2) { + if Self::has_slot_for(&team2, player.role) { + team2.push(player.clone()); + } + else if Self::has_slot_for(&team1, player.role) { + team1.push(player.clone()); + } + else { + continue; + } } else { - team2.push(player.clone()); + if Self::has_slot_for(&team1, player.role) { + team1.push(player.clone()); + } + else if Self::has_slot_for(&team2, player.role) { + team2.push(player.clone()); + } + else { + continue; + } } players.retain(|p| p.id != player.player.id); @@ -72,6 +89,10 @@ impl Mixer { let selected_players = team1.iter().filter(|player| player.role == role) .chain(team2.iter().filter(|player| player.role == role)).clone().collect::>(); + if selected_players.len() <= 0 { + return 0.0; + } + selected_players.iter().map(|player| { match player.role { Role::Tank => player.player.tank, @@ -82,15 +103,47 @@ impl Mixer { }).sum::() / selected_players.len() as f32 } + fn full_team_rank(team: &Vec) -> f32 { + if team.len() <= 0 { + return 0.0; + } + + team.iter().map(|p| { + match p.role { + Role::Tank => p.player.tank, + Role::Dps => p.player.dps, + Role::Support => p.player.support, + _ => 0.0 + } + }).sum::() + } + + fn has_slot_for(team: &Vec, role: Role) -> bool { + let team_role_count = team.iter().filter(|p| p.role == role).count(); + + match role { + Role::Tank => team_role_count < 1, + Role::Dps | Role::Support => team_role_count < 2, + _ => false + } + } + fn calculate_priorities(players: &Vec, team1: &Vec, team2: &Vec, expected: Role, priorities: &mut HashMap<(Role, i32), f32>) -> bool { - let group_coefficients = vec![10.0, 50.0, 100.0, 150.0, 125.0]; + let group_coefficients = vec![1.0, 5.0, 7.5, 10.0, 6.0]; let team1_roles = team1.iter().map(|player| player.role).collect::>(); let team2_roles = team2.iter().map(|player| player.role).collect::>(); let team1_role_count = team1_roles.iter().filter(|role| **role == expected).count(); let team2_role_count = team2_roles.iter().filter(|role| **role == expected).count(); - let prioritize_role = team1_role_count == 0 || team2_role_count == 0; + + let max_role_players = match expected { + Role::Tank => 2, + Role::Dps | Role::Support => 4, + _ => 0 + }; + + let prioritize_role = (team1_role_count + team2_role_count) < max_role_players; if !prioritize_role { return false; @@ -102,7 +155,7 @@ impl Mixer { ).cloned().collect::>(); let primary = players.iter().filter(|player| - player.primary_role == expected && (player.secondary_role != Role::None || player.tertiary_role == Role::None) + player.primary_role == expected && (player.secondary_role != Role::None || player.tertiary_role != Role::None) ).cloned().collect::>(); let secondary = players.iter().filter(|player| @@ -118,7 +171,7 @@ impl Mixer { ).cloned().collect::>(); - let average_tank_skill = Self::average_rank(team1, team2, expected); + let average_role_rank = Self::average_rank(team1, team2, expected); for player in otp { let rank = match expected { @@ -127,7 +180,7 @@ impl Mixer { Role::Support => player.support, _ => 0.0 }; - let skill_difference = (rank - average_tank_skill).abs(); + let skill_difference = Self::calculate_rank_difference(average_role_rank, rank); priorities.insert((expected, player.id), rank / group_coefficients[0] / (skill_difference + 1.0)); } @@ -139,7 +192,7 @@ impl Mixer { Role::Support => player.support, _ => 0.0 }; - let skill_difference = (rank - average_tank_skill).abs(); + let skill_difference = Self::calculate_rank_difference(average_role_rank, rank); priorities.insert((expected, player.id), rank / group_coefficients[1] / (skill_difference + 1.0)); } @@ -151,7 +204,7 @@ impl Mixer { Role::Support => player.support, _ => 0.0 }; - let skill_difference = (rank - average_tank_skill).abs(); + let skill_difference = Self::calculate_rank_difference(average_role_rank, rank); priorities.insert((expected, player.id), rank / group_coefficients[2] / (skill_difference + 1.0)); } @@ -163,7 +216,7 @@ impl Mixer { Role::Support => player.support, _ => 0.0 }; - let skill_difference = (rank - average_tank_skill).abs(); + let skill_difference = Self::calculate_rank_difference(average_role_rank, rank); priorities.insert((expected, player.id), rank / group_coefficients[3] / (skill_difference + 1.0)); } @@ -175,7 +228,7 @@ impl Mixer { Role::Support => player.support, _ => 0.0 }; - let skill_difference = (rank - average_tank_skill).abs(); + let skill_difference = Self::calculate_rank_difference(average_role_rank, rank); priorities.insert((expected, player.id), rank / group_coefficients[4] / (skill_difference + 1.0)); } @@ -183,7 +236,14 @@ impl Mixer { true } - fn get_highest_priority_player(&self, players: &Vec, priorities: &HashMap<(Role, i32), f32>) -> Option { + fn calculate_rank_difference(average_rank: f32, rank: f32) -> f32 { + match average_rank { + 0.0 => 0.0, + _ => (rank - average_rank).abs() + } + } + + fn get_highest_priority_player(&self, players: &Vec, priorities: &HashMap<(Role, i32), f32>) -> Option { let mut highest_priority = 0.0; let mut highest_priority_player = None; @@ -203,4 +263,23 @@ impl Mixer { highest_priority_player } + fn priorities_to_array(priorities: HashMap<(Role, i32), f32>, players: &Vec) -> Vec { + let mut sorted: Vec = vec![]; + for role in vec![Role::Tank, Role::Dps, Role::Support] { + for player in players { + if let Some(priority) = priorities.get(&(role, player.id)) { + sorted.push(MixedPlayer { + role, + player: player.clone() + }) + } + } + } + + sorted.sort_by(|p1, p2| priorities.get(&(p2.role, p2.player.id)).unwrap() + .total_cmp(priorities.get(&(p1.role, p1.player.id)).unwrap()) + ); + + sorted + } } \ No newline at end of file diff --git a/src/mixer/role.rs b/src/mixer/role.rs index c5c4792..2a768c8 100644 --- a/src/mixer/role.rs +++ b/src/mixer/role.rs @@ -1,7 +1,7 @@ use std::hash::{Hash, Hasher}; use std::str::FromStr; -#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] +#[derive(Debug, Ord, PartialOrd, Clone, Copy, Hash, PartialEq, Eq)] pub enum Role { Tank, Dps, @@ -9,6 +9,7 @@ pub enum Role { None } + impl PartialEq for Role { fn eq(&self, other: &i32) -> bool { match self {