mod settings_role; use std::collections::HashMap; use rand::seq::SliceRandom; pub use settings_role::*; use super::Result; use serde::{Deserialize, Serialize}; use crate::{error::GameError, message::Identification, player::Character, role::RoleTitle}; #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct GameSettings { roles: Vec, next_order: u32, } impl Default for GameSettings { fn default() -> Self { Self { roles: vec![SetupSlot::new(RoleTitle::Werewolf, 0)], next_order: 1, } } } impl GameSettings { pub fn wolves_count(&self) -> usize { self.roles .iter() .filter(|s| s.role.category().is_wolves()) .count() } pub fn slots(&self) -> &[SetupSlot] { &self.roles } pub fn village_roles_count(&self) -> usize { log::warn!( "wolves: {} total: {}", self.wolves_count(), self.roles.len() ); self.roles.len() - self.wolves_count() } pub fn remove_assignments_not_in_list(&mut self, list: &[Identification]) { self.roles.iter_mut().for_each(|r| { if let Some(pid) = &r.assign_to && !list.iter().any(|i| i.player_id == *pid) { r.assign_to.take(); } }) } pub fn remove_duplicate_assignments(&mut self) { let assignments = self .roles .iter() .filter_map(|r| r.assign_to.as_ref()) .cloned() .collect::>(); let mut assignment_counter = HashMap::new(); for assign in assignments { if let Some(counter) = assignment_counter.get_mut(&assign) { *counter += 1; } else { assignment_counter.insert(assign, 1usize); } } let to_remove = assignment_counter .into_iter() .filter_map(|(pid, cnt)| (cnt > 1).then_some(pid)) .collect::>(); for role in self.roles.iter_mut() { if let Some(pid) = role.assign_to.as_ref() && to_remove.contains(pid) { role.assign_to.take(); } } } pub fn assign(&self, players: &[Identification]) -> Result> { self.check_with_player_list(players)?; let roles_in_game = self .roles .iter() .map(|r| r.role.clone().into()) .collect::>(); let with_assigned_roles = self .roles .iter() .filter_map(|s| { s.assign_to.as_ref().map(|assign_to| { players .iter() .find(|pid| pid.player_id == *assign_to) .ok_or(GameError::AssignedPlayerMissing(assign_to.clone())) .map(|id| (id, s)) }) }) .collect::>>()?; let mut random_assign_players = players .iter() .filter(|p| { !with_assigned_roles .iter() .any(|(r, _)| r.player_id == p.player_id) }) .collect::>(); random_assign_players.shuffle(&mut rand::rng()); with_assigned_roles .into_iter() .chain( random_assign_players .into_iter() .zip(self.roles.iter().filter(|s| s.assign_to.is_none())), ) .map(|(id, slot)| slot.clone().into_character(id.clone(), &roles_in_game)) .collect::>>() } pub fn check_with_player_list(&self, players: &[Identification]) -> Result<()> { self.check()?; let (p_len, r_len) = (players.len(), self.roles.len()); if p_len > r_len { return Err(GameError::TooManyPlayers { got: p_len.min(0xFF) as u8, need: self.roles.len().min(0xFF) as u8, }); } else if p_len < r_len { return Err(GameError::TooManyRoles { players: p_len.min(0xFF) as u8, roles: r_len.min(0xFF) as u8, }); } for role in self.roles.iter() { if let Some(assigned) = role.assign_to.as_ref() && !players.iter().any(|p| p.player_id == *assigned) { return Err(GameError::AssignedPlayerMissing(assigned.clone())); } } let assignments = self .roles .iter() .filter_map(|r| r.assign_to.as_ref()) .cloned() .collect::>(); let mut assignment_counter = HashMap::new(); for assign in assignments { if let Some(counter) = assignment_counter.get_mut(&assign) { *counter += 1; } else { assignment_counter.insert(assign, 1usize); } } if let Some((assign, cnt)) = assignment_counter.into_iter().find(|(_, cnt)| *cnt > 1) { let ident = players .iter() .find(|i| i.player_id == assign) .ok_or(GameError::AssignedPlayerMissing(assign))?; return Err(GameError::AssignedMultipleTimes(ident.public.clone(), cnt)); } Ok(()) } pub fn check(&self) -> Result<()> { if self.wolves_count() == 0 { return Err(GameError::NoWolves); } let mentor_count = self .roles .iter() .filter(|r| Into::::into(r.role.clone()).is_mentor()) .count(); self.roles.iter().try_for_each(|s| match &s.role { SetupRole::Apprentice { specifically: None } => (mentor_count > 0) .then_some(()) .ok_or(GameError::NoApprenticeMentor), SetupRole::Apprentice { specifically: Some(role), } => role .is_mentor() .then_some(()) .ok_or(GameError::NotAMentor(*role)), _ => Ok(()), })?; Ok(()) } pub fn min_players_needed(&self) -> usize { let (wolves, villagers) = (self.wolves_count(), self.village_roles_count()); if wolves > villagers { wolves + 1 + wolves } else if wolves < villagers { wolves + villagers } else { wolves + villagers + 1 } } pub fn new_slot(&mut self, role: RoleTitle) -> SlotId { let slot = SetupSlot::new(role, self.next_order); self.next_order += 1; let slot_id = slot.slot_id; self.roles.push(slot); self.sort_roles(); slot_id } pub fn update_slot(&mut self, slot: SetupSlot) { if let Some(old_slot) = self.roles.iter_mut().find(|r| r.slot_id == slot.slot_id) { *old_slot = slot; } } pub fn remove_slot(&mut self, slot_id: SlotId) { if let Some(idx) = self .roles .iter() .enumerate() .find_map(|(idx, slot)| (slot.slot_id == slot_id).then_some(idx)) { self.roles.swap_remove(idx); self.sort_roles(); } } fn sort_roles(&mut self) { self.roles .sort_by(|l, r| l.created_order.cmp(&r.created_order).reverse()); } }