diff --git a/secert-hitler/src/controller/gamemaster/gamemaster/game.rs b/secert-hitler/src/controller/gamemaster/gamemaster/game.rs index 319ecba..faab016 100644 --- a/secert-hitler/src/controller/gamemaster/gamemaster/game.rs +++ b/secert-hitler/src/controller/gamemaster/gamemaster/game.rs @@ -1,46 +1,67 @@ use super::gamemaster::GameMaster; use super::super::gameevent::GameEvent; +use super::super::gameevent::GameEventType; use super::super::policy::Policy; - -use log::{LevelFilter}; +use super::super::role::Role; +use super::super::player::Player; impl GameMaster { - pub fn game_is_over(&mut self) -> Result { - Err("not impl".to_string()) + pub fn game_is_over(&mut self) -> Result { + if self.policies[&Policy::Facist] >= 3 { + let chancellor = self.chancellor.clone(); + if chancellor.is_some() { + let player = self.player(chancellor.unwrap()); + if player.is_some() && player.unwrap().get_role() == Role::Hitler { + return Err(Role::Facist); + } + } + } + if self.policies[&Policy::Facist] > 5 { + return Err(Role::Facist); + } + if self.policies[&Policy::Liberal] > 5 { + return Err(Role::Liberal); + } + Ok(GameEventType::Null.build()) } - pub fn game_election(&mut self) -> Result { - Err("not impl".to_string()) + pub fn game_election(&mut self) -> Result { + Err(Role::Null) } - pub fn game_election_vote(&mut self) -> Result { - Err("not impl".to_string()) + pub fn game_election_vote(&mut self) -> Result { + Err(Role::Null) } - pub fn game_policy_select_random(&mut self) -> Result { - Err("not impl".to_string()) + pub fn game_policy_select_random(&mut self) -> Result { + Err(Role::Null) } - pub fn game_policy_select(&mut self) -> Result { - Err("not impl".to_string()) + pub fn game_policy_select(&mut self) -> Result { + Err(Role::Null) } - pub fn game_policy_veto(&mut self, _p: Policy) -> Result { - Err("not impl".to_string()) + pub fn game_policy_veto(&mut self, _p: Policy) -> Result { + Err(Role::Null) } - pub fn game_ends_with(&mut self, _p: Policy) -> Result { - Err("not impl".to_string()) + pub fn game_ends_with(&mut self, _p: Policy) -> Result { + Err(Role::Null) } - pub fn game_policy_enact(&mut self, _p: Policy) -> Result { - Err("not impl".to_string()) + pub fn game_policy_enact(&mut self, _p: Policy) -> Result { + Err(Role::Null) } } #[cfg(test)] mod tests { use super::*; + use super::super::super::super::super::model::state::mockrooms::MockRooms; + use super::super::super::super::super::model::state::rooms::Rooms; + use super::super::super::super::super::config; + + use log::{debug, LevelFilter}; fn init() { let _ = env_logger::builder() @@ -49,8 +70,83 @@ mod tests { .try_init(); } - #[test] - fn game() { + fn dummy() -> GameMaster { init(); + let mut mrs = MockRooms::new(); + let r = mrs.create(); + let room_id = r.room_id().clone(); + let mut gm = GameMaster::new(r); + for _ in 0..config::MIN_PLAYERS-2 { + assert!(mrs.join(room_id.clone()).is_ok()); + } + mrs.join(room_id).unwrap().send(format!(r#"{{ + "msgtype": "m.text", + "body": "{{\"GameEventType\": \"GameStart\"}}" + }}"#)).unwrap(); + assert!(gm.run_lobby().is_ok()); + assert!(gm.run_game_setup().is_ok()); + gm + } + + #[test] + fn test_dummy() { + let gm = dummy(); + assert!(gm.players().len() == config::MIN_PLAYERS); + assert!(config::players_to_policies_facist(gm.players().len()) + config::players_to_policies_liberal(gm.players().len()) == gm.deck.len()); + assert!(gm.players().len() < gm.candidate_presidents.len()); + assert!(gm.policies[&Policy::Facist] == 0); + assert!(gm.policies[&Policy::Liberal] == 0); + let mut found = false; + for i in 1..gm.candidate_presidents.len() { + found = found || gm.candidate_presidents[i-1] > gm.candidate_presidents[i]; + } + assert!(found); + debug!("gamemaster dummy: {:?}", gm); + } + + #[test] + fn game_is_over() { + let mut gm = dummy(); + assert!(gm.game_is_over().is_ok()); + + let mut gm = dummy(); + gm.policies.insert(Policy::Facist, 10); + assert!(gm.game_is_over().is_err()); + assert!(gm.game_is_over().err().unwrap() == Role::Facist); + + let mut gm = dummy(); + gm.policies.insert(Policy::Liberal, 10); + assert!(gm.game_is_over().err().unwrap() == Role::Liberal); + + let mut gm = dummy(); + gm.policies.insert(Policy::Facist, 3); + assert!(gm.game_is_over().is_ok()); + gm.chancellor = Some("123".to_string()); + assert!(gm.game_is_over().is_ok()); + let mut p = Player::new("123".to_string()); + p.set_role(Role::Liberal); + gm.lobby.players.insert("123".to_string(), p); + assert!(gm.game_is_over().is_ok()); + + let mut gm = dummy(); + gm.policies.insert(Policy::Facist, 3); + assert!(gm.game_is_over().is_ok()); + gm.chancellor = Some("123".to_string()); + assert!(gm.game_is_over().is_ok()); + let mut p = Player::new("123".to_string()); + p.set_role(Role::Facist); + gm.lobby.players.insert("123".to_string(), p); + assert!(gm.game_is_over().is_ok()); + + let mut gm = dummy(); + gm.policies.insert(Policy::Facist, 3); + assert!(gm.game_is_over().is_ok()); + gm.chancellor = Some("123".to_string()); + assert!(gm.game_is_over().is_ok()); + let mut p = Player::new("123".to_string()); + p.set_role(Role::Hitler); + gm.lobby.players.insert("123".to_string(), p); + assert!(gm.game_is_over().is_err()); + assert!(gm.game_is_over().err().unwrap() == Role::Facist); } } diff --git a/secert-hitler/src/controller/gamemaster/gamemaster/gamemaster.rs b/secert-hitler/src/controller/gamemaster/gamemaster/gamemaster.rs index c201dfb..36da93a 100644 --- a/secert-hitler/src/controller/gamemaster/gamemaster/gamemaster.rs +++ b/secert-hitler/src/controller/gamemaster/gamemaster/gamemaster.rs @@ -2,19 +2,25 @@ use super::super::super::super::model::state::room::Room; use super::super::lobby::Lobby; use super::super::player::Player; use super::super::policy::Policy; +use super::super::role::Role; use super::super::gameevent::GameEventType; +use super::super::gameevent::GameEvent; use log::{info, error}; +use std::collections::HashMap; +#[derive(Debug)] pub struct GameMaster { pub room: Box, pub lobby: Lobby, pub candidate_presidents: Vec, pub deck: Vec, + pub policies: HashMap, + pub president: Option, + pub chancellor: Option, } impl GameMaster { - pub fn new(room: Box) -> GameMaster { info!("created for room {}", room.room_id()); GameMaster{ @@ -22,10 +28,13 @@ impl GameMaster { lobby: Lobby::new(), candidate_presidents: vec![], deck: vec![], + policies: HashMap::new(), + president: None, + chancellor: None, } } - pub fn run(&mut self) -> Result { + pub fn run(&mut self) -> Result { loop { let r = self.run_lobby(); if r.is_ok() { @@ -37,19 +46,31 @@ impl GameMaster { self.run_game() } - pub fn run_game(&mut self) -> Result { + pub fn run_game(&mut self) -> Result { loop { self.game_is_over()?; self.game_election()?; self.game_is_over()?; let p = match self.game_election_vote().unwrap_or(GameEventType::GameStop.build()).mode() { GameEventType::VoteFailed => self.game_policy_select_random(), - GameEventType::GameStop => Err("game ended".to_string()), + GameEventType::GameStop => self.game_is_over(), _ => self.game_policy_select(), }?; - self.game_policy_veto(p.clone())?; - self.game_ends_with(p.clone())?; - self.game_policy_enact(p.clone())?; // todo facist policies special + if p.mode() != GameEventType::PolicySet { + error!("unexpected game event type after election vote followup: {:?}", p); + return Err(Role::Null); + } + let params = p.params(); + let param = params.first(); + if param.is_none() { + error!("unexpected missing param on {:?}: {:?}", p, param); + return Err(Role::Null); + } + let param = param.unwrap(); + let policy = Policy::from_string(param.to_string()); + self.game_policy_veto(policy.clone())?; + self.game_ends_with(policy.clone())?; + self.game_policy_enact(policy.clone())?; } } diff --git a/secert-hitler/src/controller/gamemaster/gamemaster/setup.rs b/secert-hitler/src/controller/gamemaster/gamemaster/setup.rs index 45aecbd..3574066 100644 --- a/secert-hitler/src/controller/gamemaster/gamemaster/setup.rs +++ b/secert-hitler/src/controller/gamemaster/gamemaster/setup.rs @@ -6,10 +6,10 @@ use super::super::policy::Policy; use super::super::rand::rand_usize; use super::super::rand::shuffle; -use log::{debug, LevelFilter}; +use log::{debug, error, LevelFilter}; impl GameMaster { - pub fn run_game_setup(&mut self) -> Result { + pub fn run_game_setup(&mut self) -> Result { self.setup_gather_candidates()?; debug!("players: {:?}", self.candidate_presidents); self.setup_set_roles()?; @@ -20,11 +20,12 @@ impl GameMaster { Ok("ok".to_string()) } - pub fn setup_gather_candidates(&mut self) -> Result { + pub fn setup_gather_candidates(&mut self) -> Result { for player in self.players() { let p = self.player(player.clone()); if p.is_none() { - return Err(format!("missing player {}", player)); + error!("missing player {}", player); + return Err(Role::Null); } self.candidate_presidents.push(player.clone()); debug!("player = {}", player); @@ -32,11 +33,15 @@ impl GameMaster { Ok("ok".to_string()) } - pub fn setup_set_roles(&mut self) -> Result { + pub fn setup_set_roles(&mut self) -> Result { for player in self.players() { self.player(player.clone()).unwrap().set_role(Role::Liberal); } - let n = config::players_to_facists(self.players().len())?; + let n = config::players_to_facists(self.players().len()); + if n.is_err() { + return Err(Role::Null); + } + let n = n.unwrap(); for i in 0..n { debug!("picking facist {}/{} for {} players", i, n, self.players().len()); loop { @@ -54,10 +59,12 @@ impl GameMaster { break; } } + self.president = None; + self.chancellor = None; Ok("ok".to_string()) } - pub fn setup_order_candidates(&mut self) -> Result { + pub fn setup_order_candidates(&mut self) -> Result { shuffle(&mut self.candidate_presidents); let n = self.candidate_presidents.len(); for _ in 0..5 { @@ -68,7 +75,7 @@ impl GameMaster { Ok("ok".to_string()) } - pub fn setup_deck(&mut self) -> Result { + pub fn setup_deck(&mut self) -> Result { for _ in 0..config::players_to_policies_facist(self.players().len()) { self.deck.push(Policy::Facist); } @@ -79,6 +86,9 @@ impl GameMaster { shuffle(&mut self.deck); + self.policies.insert(Policy::Facist, 0); // todo start iwth 1 if 5 players? + self.policies.insert(Policy::Liberal, 0); // todo start iwth 1 if 5 players? + Ok("deck is loaded".to_string()) } } diff --git a/secert-hitler/src/controller/gamemaster/policy.rs b/secert-hitler/src/controller/gamemaster/policy.rs index 816c4e5..b5198b9 100644 --- a/secert-hitler/src/controller/gamemaster/policy.rs +++ b/secert-hitler/src/controller/gamemaster/policy.rs @@ -1,4 +1,4 @@ -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq, Hash)] pub enum Policy { Null, Facist, @@ -6,6 +6,20 @@ pub enum Policy { } impl Policy { + pub fn from_string(s: String) -> Policy { + let cases = [ + Policy::Null, + Policy::Facist, + Policy::Liberal, + ]; + for c in &cases { + if format!("{:?}", c) == s { + return c.clone(); + } + } + Policy::Null + } + pub fn new() -> Policy { Policy::Null } @@ -34,4 +48,10 @@ mod tests { r.set(Policy::Liberal); assert!(r == Policy::Liberal); } + + fn from_string() { + assert!(Policy::from_string("".to_string()) == Policy::Null); + assert!(Policy::from_string("Liberal".to_string()) == Policy::Liberal); + assert!(Policy::from_string("Facist".to_string()) == Policy::Facist); + } } diff --git a/secert-hitler/src/model/state/room.rs b/secert-hitler/src/model/state/room.rs index b5bb4c1..9ec45f3 100644 --- a/secert-hitler/src/model/state/room.rs +++ b/secert-hitler/src/model/state/room.rs @@ -1,6 +1,8 @@ use super::event; -pub trait Room { +use std::fmt; + +pub trait Room: fmt::Debug { fn rollback(&mut self, since: String); fn sync(&mut self) -> Vec; fn send(&mut self, message: String) -> Result;