use core::num::NonZeroU8; use rand::{Rng, seq::SliceRandom}; use serde::{Deserialize, Serialize}; use super::Result; use crate::{ error::GameError, game::{DateTime, GameOver, GameSettings}, message::{Identification, Target}, player::{Character, CharacterId, PlayerId}, role::{Role, RoleTitle}, }; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Village { characters: Vec, date_time: DateTime, } impl Village { pub fn new(players: &[Identification], settings: GameSettings) -> Result { if settings.min_players_needed() > players.len() { return Err(GameError::TooManyRoles { players: players.len() as u8, roles: settings.min_players_needed() as u8, }); } settings.check()?; let roles_spread = settings.spread(); let potential_apprentice_havers = roles_spread .iter() .filter(|r| r.is_mentor()) .map(|r| r.title_to_role_excl_apprentice()) .collect::>(); let mut roles = roles_spread .into_iter() .chain( (0..settings.villagers_needed_for_player_count(players.len())?) .map(|_| RoleTitle::Villager), ) .map(|title| match title { RoleTitle::Apprentice => Role::Apprentice(Box::new( potential_apprentice_havers [rand::rng().random_range(0..potential_apprentice_havers.len())] .clone(), )), _ => title.title_to_role_excl_apprentice(), }) .collect::>(); assert_eq!(players.len(), roles.len()); roles.shuffle(&mut rand::rng()); Ok(Self { characters: players .iter() .cloned() .zip(roles) .map(|(player, role)| Character::new(player, role)) .collect(), date_time: DateTime::Night { number: 0 }, }) } pub const fn date_time(&self) -> DateTime { self.date_time } pub fn find_by_character_id(&self, character_id: &CharacterId) -> Option<&Character> { self.characters .iter() .find(|c| c.character_id() == character_id) } pub fn find_by_character_id_mut( &mut self, character_id: &CharacterId, ) -> Option<&mut Character> { self.characters .iter_mut() .find(|c| c.character_id() == character_id) } fn living_wolves_count(&self) -> usize { self.characters .iter() .filter(|c| c.is_wolf() && c.alive()) .count() } fn living_villager_count(&self) -> usize { self.characters .iter() .filter(|c| c.is_village() && c.alive()) .count() } pub fn is_game_over(&self) -> Option { let wolves = self.living_wolves_count(); let villagers = self.living_villager_count(); if wolves == 0 { return Some(GameOver::VillageWins); } if wolves >= villagers { return Some(GameOver::WolvesWin); } None } pub fn execute(&mut self, characters: &[CharacterId]) -> Result> { let day = match self.date_time { DateTime::Day { number } => number, DateTime::Night { number: _ } => return Err(GameError::NoExecutionsAtNight), }; if characters.is_empty() { return Err(GameError::NoTrialNotAllowed); } let targets = self .characters .iter_mut() .filter(|c| characters.contains(c.character_id())) .collect::>(); if targets.len() != characters.len() { return Err(GameError::CannotFindTargetButShouldBeThere); } for t in targets { t.execute(day)?; } self.date_time = self.date_time.next(); Ok(self.is_game_over()) } pub fn to_day(&mut self) -> Result { if self.date_time.is_day() { return Err(GameError::AlreadyDaytime); } self.date_time = self.date_time.next(); Ok(self.date_time) } pub fn living_wolf_pack_players(&self) -> Box<[Character]> { self.characters .iter() .filter(|c| c.role().wolf() && c.alive()) .cloned() .collect() } pub fn living_players(&self) -> Box<[Target]> { self.characters .iter() .filter(|c| c.alive()) .map(Character::target) .collect() } pub fn target_by_id(&self, character_id: &CharacterId) -> Option { self.character_by_id(character_id).map(Character::target) } pub fn living_villagers(&self) -> Box<[Target]> { self.characters .iter() .filter(|c| c.alive() && c.is_village()) .map(Character::target) .collect() } pub fn living_players_excluding(&self, exclude: &CharacterId) -> Box<[Target]> { self.characters .iter() .filter(|c| c.alive() && c.character_id() != exclude) .map(Character::target) .collect() } pub fn dead_targets(&self) -> Box<[Target]> { self.characters .iter() .filter(|c| !c.alive()) .map(Character::target) .collect() } pub fn dead_characters(&self) -> Box<[&Character]> { self.characters.iter().filter(|c| !c.alive()).collect() } pub fn living_characters_by_role(&self, role: RoleTitle) -> Box<[Character]> { self.characters .iter() .filter(|c| c.role().title() == role) .cloned() .collect() } pub fn characters(&self) -> Box<[Character]> { self.characters.iter().cloned().collect() } pub fn character_by_id_mut(&mut self, character_id: &CharacterId) -> Option<&mut Character> { self.characters .iter_mut() .find(|c| c.character_id() == character_id) } pub fn character_by_id(&self, character_id: &CharacterId) -> Option<&Character> { self.characters .iter() .find(|c| c.character_id() == character_id) } pub fn character_by_player_id(&self, player_id: &PlayerId) -> Option<&Character> { self.characters.iter().find(|c| c.player_id() == player_id) } } impl RoleTitle { pub fn title_to_role_excl_apprentice(self) -> Role { match self { RoleTitle::Villager => Role::Villager, RoleTitle::Scapegoat => Role::Scapegoat, RoleTitle::Seer => Role::Seer, RoleTitle::Arcanist => Role::Arcanist, RoleTitle::Elder => Role::Elder { knows_on_night: NonZeroU8::new(rand::rng().random_range(1u8..3)).unwrap(), }, RoleTitle::Werewolf => Role::Werewolf, RoleTitle::AlphaWolf => Role::AlphaWolf { killed: None }, RoleTitle::DireWolf => Role::DireWolf, RoleTitle::Shapeshifter => Role::Shapeshifter { shifted_into: None }, RoleTitle::Apprentice => panic!("title_to_role_excl_apprentice got an apprentice role"), RoleTitle::Protector => Role::Protector { last_protected: None, }, RoleTitle::Gravedigger => Role::Gravedigger, RoleTitle::Hunter => Role::Hunter { target: None }, RoleTitle::Militia => Role::Militia { targeted: None }, RoleTitle::MapleWolf => Role::MapleWolf { last_kill_on_night: 0, }, RoleTitle::Guardian => Role::Guardian { last_protected: None, }, } } }