mod kill; mod night; mod settings; mod village; use core::{ fmt::Debug, num::NonZeroU8, ops::{Deref, Range, RangeBounds}, }; use rand::{Rng, seq::SliceRandom}; use serde::{Deserialize, Serialize}; use crate::{ error::GameError, game::night::{Night, ServerAction}, message::{ CharacterState, Identification, host::{HostDayMessage, HostGameMessage, HostNightMessage, ServerToHostMessage}, }, player::CharacterId, }; pub use {settings::GameSettings, village::Village}; type Result = core::result::Result; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Game { previous: Vec, next: Vec, state: GameState, } impl Game { pub fn new(players: &[Identification], settings: GameSettings) -> Result { Ok(Self { next: Vec::new(), previous: Vec::new(), state: GameState::Night { night: Night::new(Village::new(players, settings)?)?, }, }) } pub const fn village(&self) -> &Village { match &self.state { GameState::Day { village, marked: _ } => village, GameState::Night { night } => night.village(), } } pub fn process(&mut self, message: HostGameMessage) -> Result { match (&mut self.state, message) { (GameState::Night { night }, HostGameMessage::Night(HostNightMessage::Next)) => { night.next()?; self.process(HostGameMessage::GetState) } ( GameState::Day { village: _, marked }, HostGameMessage::Day(HostDayMessage::MarkForExecution(target)), ) => { match marked .iter() .enumerate() .find_map(|(idx, mark)| (mark == &target).then_some(idx)) { Some(idx) => { marked.swap_remove(idx); } None => marked.push(target), } self.process(HostGameMessage::GetState) } (GameState::Day { village, marked }, HostGameMessage::Day(HostDayMessage::Execute)) => { if let Some(outcome) = village.execute(marked)? { return Ok(ServerToHostMessage::GameOver(outcome)); } let night = Night::new(village.clone())?; self.previous.push(self.state.clone()); self.state = GameState::Night { night }; self.process(HostGameMessage::GetState) } (GameState::Day { village, marked }, HostGameMessage::GetState) => { if let Some(outcome) = village.is_game_over() { return Ok(ServerToHostMessage::GameOver(outcome)); } Ok(ServerToHostMessage::Daytime { marked: marked.clone().into_boxed_slice(), characters: village .characters() .into_iter() .map(|c| CharacterState { player_id: c.player_id().clone(), identity: c.identity(), role: c.role().title(), died_to: c.died_to().cloned(), }) .collect(), day: match village.date_time() { DateTime::Day { number } => number, DateTime::Night { number: _ } => unreachable!(), }, }) } (GameState::Night { night }, HostGameMessage::GetState) => { if let Some(res) = night.current_result() { return Ok(ServerToHostMessage::ActionResult( night.current_character().map(|c| c.identity()), res.clone(), )); } if let Some(prompt) = night.current_prompt() { return Ok(ServerToHostMessage::ActionPrompt(prompt.clone())); } match night.next() { Ok(_) => self.process(HostGameMessage::GetState), Err(GameError::NightOver) => { let village = night.collect_completed()?; self.previous.push(self.state.clone()); self.state = GameState::Day { village, marked: Vec::new(), }; self.process(HostGameMessage::GetState) } Err(err) => Err(err), } } ( GameState::Night { night }, HostGameMessage::Night(HostNightMessage::ActionResponse(resp)), ) => match night.received_response(resp.clone()) { Ok(ServerAction::Prompt(prompt)) => Ok(ServerToHostMessage::ActionPrompt(prompt)), Ok(ServerAction::Result(res)) => Ok(ServerToHostMessage::ActionResult( night.current_character().map(|c| c.identity()), res, )), Err(GameError::NightNeedsNext) => match night.next() { Ok(_) => self.process(HostGameMessage::Night( HostNightMessage::ActionResponse(resp), )), Err(GameError::NightOver) => { // since the block handling HostGameMessage::GetState for night // already manages the NightOver state, just invoke it self.process(HostGameMessage::GetState) } Err(err) => Err(err), }, Err(err) => Err(err), }, (GameState::Night { night: _ }, HostGameMessage::Day(_)) | ( GameState::Day { village: _, marked: _, }, HostGameMessage::Night(_), ) => Err(GameError::InvalidMessageForGameState), ( GameState::Day { village: _, marked: _, }, HostGameMessage::PreviousState, ) => { let mut prev = self.previous.pop().ok_or(GameError::NoPreviousState)?; log::info!("previous state loaded: {prev:?}"); core::mem::swap(&mut prev, &mut self.state); self.next.push(prev); self.process(HostGameMessage::GetState) } (GameState::Night { night }, HostGameMessage::PreviousState) => { night.previous_state()?; self.process(HostGameMessage::GetState) } } } pub fn game_over(&self) -> Option { self.state.game_over() } pub fn game_state(&self) -> &GameState { &self.state } pub fn previous_game_states(&self) -> &[GameState] { &self.previous } } #[allow(clippy::large_enum_variant)] #[derive(Debug, Clone, Serialize, Deserialize)] pub enum GameState { Day { village: Village, marked: Vec, }, Night { night: Night, }, } impl GameState { pub fn game_over(&self) -> Option { match self { GameState::Day { village, marked: _ } => village.is_game_over(), GameState::Night { night: _ } => None, } } } #[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)] pub enum GameOver { VillageWins, WolvesWin, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Pool where T: Debug + Clone, { pub pool: Vec, pub range: Range, } impl Pool where T: Debug + Clone, { pub const fn new(pool: Vec, range: Range) -> Self { Self { pool, range } } pub fn collapse(mut self, rng: &mut impl Rng, max: u8) -> Vec { let range = match self.range.end_bound() { core::ops::Bound::Included(end) => { if max < *end { self.range.start..max + 1 } else { self.range.clone() } } core::ops::Bound::Excluded(end) => { if max <= *end { self.range.start..max + 1 } else { self.range.clone() } } core::ops::Bound::Unbounded => self.range.start..max + 1, }; let count = rng.random_range(range); self.pool.shuffle(rng); self.pool.truncate(count as _); self.pool } } impl Deref for Pool where T: Debug + Clone, { type Target = [T]; fn deref(&self) -> &Self::Target { &self.pool } } #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] pub enum Maybe { Yes, No, Maybe, } #[derive(Debug, Clone, Copy, Serialize, Deserialize)] pub enum DateTime { Day { number: NonZeroU8 }, Night { number: u8 }, } impl Default for DateTime { fn default() -> Self { DateTime::Day { number: NonZeroU8::new(1).unwrap(), } } } impl DateTime { pub const fn is_day(&self) -> bool { matches!(self, DateTime::Day { number: _ }) } pub const fn is_night(&self) -> bool { matches!(self, DateTime::Night { number: _ }) } pub const fn next(self) -> Self { match self { DateTime::Day { number } => DateTime::Night { number: number.get(), }, DateTime::Night { number } => DateTime::Day { number: NonZeroU8::new(number + 1).unwrap(), }, } } }