diff --git a/secert-hitler/src/controller/gamemaster/gameevent.rs b/secert-hitler/src/controller/gamemaster/gameevent.rs index 802dd1a..ee4313d 100644 --- a/secert-hitler/src/controller/gamemaster/gameevent.rs +++ b/secert-hitler/src/controller/gamemaster/gameevent.rs @@ -61,7 +61,7 @@ impl GameEventType { params: [], }.dump(), }; - GameEvent::new(Event{sender: "".to_string(), body: d.dump(), since: "".to_string()}) + GameEvent::new(Event{sender: "__gm__".to_string(), body: d.dump(), since: "".to_string()}) } } @@ -75,9 +75,15 @@ impl GameEvent { if !top_level["body"].is_string() { return GameEvent{d: json::Null, sender: event.sender.clone()}; } + let body = top_level["body"].as_str().unwrap(); + let d = json::parse(&body).unwrap_or(json::Null); + let sender = event.sender.clone(); + if d.is_object() && d.has_key("sender") && d["sender"].is_string() { + // sender = d["sender"].as_str().unwrap(); + } GameEvent{ - d: json::parse(&top_level["body"].as_str().unwrap()).unwrap_or(json::Null), - sender: event.sender.clone(), + d: d, + sender: sender, } } diff --git a/secert-hitler/src/controller/gamemaster/gamemaster/game.rs b/secert-hitler/src/controller/gamemaster/gamemaster/game.rs index 60264e9..17b258c 100644 --- a/secert-hitler/src/controller/gamemaster/gamemaster/game.rs +++ b/secert-hitler/src/controller/gamemaster/gamemaster/game.rs @@ -6,6 +6,7 @@ use super::super::role::Role; use log::debug; use json; +use std::collections::HashMap; impl GameMaster { pub fn game_is_over(&mut self) -> Result { @@ -30,6 +31,7 @@ impl GameMaster { pub fn game_election(&mut self) -> Result { let mut ge = GameEventType::ElectionPend.build(); let president_candidate = self.candidate_presidents.pop().unwrap(); + self.president = Some(president_candidate.clone()); ge.d["targets"] = json::array![president_candidate.clone()]; ge.d["params"] = json::array!["president"]; if self.room.send(ge.serialize()).is_err() { @@ -51,11 +53,55 @@ impl GameMaster { debug!("invalid chancellor candidates found in election set"); return self.game_election(); } + self.chancellor = Some(chancellor_candidate.clone()); Ok(chancellor_candidate_event) } pub fn game_election_vote(&mut self) -> Result { - Err(Role::Null) + let mut votes: HashMap = HashMap::new(); + while votes.len() < self.players().len() { + debug!("votes: {:?}", votes); + let events = self.scrape_until_gameeventtype(GameEventType::VoteSet).unwrap(); + debug!("scrape until vote set found {:?}", events); + if events.len() > 0 { + let ge = GameEvent::new(events.last().unwrap().clone()); + let sources = ge.sources(); + if sources.len() > 0 { + let player = sources.first().unwrap(); + if self.player(player.clone()).is_some() { + let params = ge.params(); + if params.len() > 0 { + votes.insert(player.clone(), params.first().unwrap() == "y"); + } + } + } + } + } + debug!("game election vote yielded: {:?}", votes); + + let mut ge = GameEventType::VoteSet.build(); + ge.d["sources"] = json::array!["__gm__"]; + let mut yays = 0; + for (player, vote) in &votes { + if *vote { + yays += 1; + } + } + let gm_vote = yays > self.players().len() / 2; + ge.d["params"] = json::array![format!("{:?}", gm_vote)]; + for (player, vote) in &votes { + ge.d["sources"].push(player.clone()); + ge.d["params"].push(format!("{:?}", vote)); + } + self.room.send(ge.serialize()); + + if gm_vote { + return Ok(GameEventType::Null.build()); // todo how to emit continue? + } else { + self.president = None; + self.chancellor = None; + return Ok(GameEventType::VoteFailed.build()); + } } pub fn game_policy_select_random(&mut self) -> Result { @@ -100,13 +146,13 @@ mod tests { fn dummy() -> GameMaster { init(); let mut mrs = MockRooms::new(); - let r = mrs.create(); + let r = mrs.create("__gm__".to_string()); 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()); + for i in 0..config::MIN_PLAYERS-2 { + assert!(mrs.join(i.to_string(), room_id.clone()).is_ok()); } - mrs.join(room_id).unwrap().send(format!(r#"{{ + mrs.join((config::MIN_PLAYERS-1).to_string(), room_id).unwrap().send(format!(r#"{{ "msgtype": "m.text", "body": "{{\"GameEventType\": \"GameStart\"}}" }}"#)).unwrap(); @@ -235,4 +281,65 @@ mod tests { let election_pends = gm.room.sync(); assert!(election_pends.len() == 6, "election_pends: {:?}", election_pends); } + + #[test] + fn game_election_vote() { + let mut gm = dummy(); + gm.lobby.players = HashMap::new(); + + debug!("sending a null event"); + let ge = GameEventType::Null.build(); + assert!(ge.mode() == GameEventType::Null); + assert!(gm.room.send(ge.serialize()).is_ok()); + debug!("sent: {}", ge.serialize()); + + debug!("sending a valid vote from a valid player"); + let mut ge = GameEventType::VoteSet.build(); + assert!(ge.mode() == GameEventType::VoteSet); + ge.d["sources"] = json::array!["1"]; + ge.d["params"] = json::array!["n"]; + assert!(gm.room.send(ge.serialize()).is_ok()); + debug!("sent: {}", ge.serialize()); + gm.lobby.players.insert("1".to_string(), Player::new("1".to_string())); + + debug!("sending a valid repeat vote from a valid player"); + let mut ge = GameEventType::VoteSet.build(); + assert!(ge.mode() == GameEventType::VoteSet); + ge.d["sources"] = json::array!["1"]; + ge.d["params"] = json::array!["y"]; + assert!(gm.room.send(ge.serialize()).is_ok()); + debug!("sent: {}", ge.serialize()); + + debug!("sending an invalid vote from a valid player"); + let mut ge = GameEventType::VoteSet.build(); + assert!(ge.mode() == GameEventType::VoteSet); + ge.d["sources"] = json::array![]; + ge.d["params"] = json::array!["n"]; + assert!(gm.room.send(ge.serialize()).is_ok()); + debug!("sent: {}", ge.serialize()); + gm.lobby.players.insert("2".to_string(), Player::new("2".to_string())); + + debug!("correcting an invalid vote from a valid player"); + let mut ge = GameEventType::VoteSet.build(); + assert!(ge.mode() == GameEventType::VoteSet); + ge.d["sources"] = json::array!["2"]; + ge.d["params"] = json::array!["y"]; + assert!(gm.room.send(ge.serialize()).is_ok()); + debug!("sent: {}", ge.serialize()); + + let ge = gm.game_election_vote(); + debug!("game election vote result: {:?}", ge); + assert!(ge.is_ok()); + + let events = gm.room.sync(); + assert!(events.len() == 1); + let e = events.last().unwrap().clone(); + let ge = GameEvent::new(e.clone()); + assert!(ge.sender == "__gm__", "post-game election vote ge: {:?} from {:?}", ge, e); + assert!(ge.mode() == GameEventType::VoteSet); + assert!(ge.sources()[0] == "__gm__"); + assert!(ge.params()[0] == "true"); + assert!(ge.params()[1] == "true"); + assert!(ge.params()[2] == "true"); + } } diff --git a/secert-hitler/src/controller/gamemaster/gamemaster/gamemaster.rs b/secert-hitler/src/controller/gamemaster/gamemaster/gamemaster.rs index d4db46c..3935c62 100644 --- a/secert-hitler/src/controller/gamemaster/gamemaster/gamemaster.rs +++ b/secert-hitler/src/controller/gamemaster/gamemaster/gamemaster.rs @@ -51,14 +51,18 @@ impl GameMaster { pub fn run_game(&mut self) -> Result { loop { - self.game_is_over()?; - self.game_election()?; - self.game_is_over()?; + let ge = self.game_is_over()?; + self.room.send(ge.serialize()); + let ge = self.game_election()?; + self.room.send(ge.serialize()); + let ge = self.game_is_over()?; + self.room.send(ge.serialize()); let p = match self.game_election_vote().unwrap_or(GameEventType::GameStop.build()).mode() { GameEventType::VoteFailed => self.game_policy_select_random(), GameEventType::GameStop => self.game_is_over(), _ => self.game_policy_select(), }?; + self.room.send(p.serialize()); if p.mode() != GameEventType::PolicySet { error!("unexpected game event type after election vote followup: {:?}", p); return Err(Role::Null); @@ -71,9 +75,12 @@ impl GameMaster { } 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())?; + let ge = self.game_policy_veto(policy.clone())?; + self.room.send(ge.serialize()); + let ge = self.game_ends_with(policy.clone())?; + self.room.send(ge.serialize()); + let ge = self.game_policy_enact(policy.clone())?; + self.room.send(ge.serialize()); } } @@ -117,7 +124,7 @@ mod tests { #[test] fn new_mockroom() { - let _ = GameMaster::new(Box::new(MockRoom::create())); + let _ = GameMaster::new(Box::new(MockRoom::create("__gm__".to_string()))); } #[test] @@ -127,23 +134,23 @@ mod tests { mrs } let mut mrs = get(); - let r = mrs.create(); + let r = mrs.create("__gm__".to_string()); let _ = GameMaster::new(r); } #[test] fn new_mockrooms() { let mut mrs = MockRooms::new(); - let r = mrs.create(); + let r = mrs.create("__gm__".to_string()); let _ = GameMaster::new(r); } #[test] fn scrape_until_get() { let mut mrs = MockRooms::new(); - let r = mrs.create(); + let r = mrs.create("__gm__".to_string()); let room_id = r.room_id().clone(); - let r2 = mrs.join(room_id); + let r2 = mrs.join("r2".to_string(), room_id); assert!(r2.is_ok()); let mut r2 = r2.unwrap(); let mut gm = GameMaster::new(r); diff --git a/secert-hitler/src/controller/gamemaster/gamemaster/lobby.rs b/secert-hitler/src/controller/gamemaster/gamemaster/lobby.rs index 64d0fa8..4061344 100644 --- a/secert-hitler/src/controller/gamemaster/gamemaster/lobby.rs +++ b/secert-hitler/src/controller/gamemaster/gamemaster/lobby.rs @@ -55,15 +55,15 @@ mod tests { fn run_lobby() { init(); let mut mrs = MockRooms::new(); - let r1 = mrs.create(); + let r1 = mrs.create("__gm__".to_string()); let room_id = r1.room_id(); let mut gm = GameMaster::new(r1); for i in 0..config::MIN_PLAYERS-1 { - let mut r2 = mrs.join(room_id.clone()).unwrap(); + let mut r2 = mrs.join(i.to_string(), room_id.clone()).unwrap(); r2.send(format!(r#"{{ "msgtype": "m.text", "body": "{{\"GameEventType\": \"GameStart\"}}" - }}"#)).unwrap(); + }}"#)).unwrap(); let ready = gm.run_lobby(); assert!(ready.is_err() == (i != 3), "want {:?} for ready.is_err #{:?}, which is {:?}", i != 3, i, ready.is_err()); } @@ -72,12 +72,12 @@ mod tests { for k in gm.lobby.players.keys() { players1.push(k.clone()); } - let mut r2 = mrs.join(room_id.clone()).unwrap(); + let mut r2 = mrs.join("r2".to_string(), room_id.clone()).unwrap(); r2.send(format!(r#"{{ "msgtype": "m.text", "body": "{{\"GameEventType\": \"GameStart\"}}" - }}"#)).unwrap(); - assert!(gm.run_lobby().is_ok()); + }}"#)).unwrap(); + assert!(gm.run_lobby().is_ok()); assert!(gm.lobby.players.len() == 5, "secnd run_lobby players: {:?}, sync: {:?}", gm.lobby.players, gm.room.sync()); let players2 = gm.lobby.players.keys(); assert!(format!("{:?}", players1) == format!("{:?}", players2)); diff --git a/secert-hitler/src/controller/gamemaster/gamemaster/setup.rs b/secert-hitler/src/controller/gamemaster/gamemaster/setup.rs index 3574066..871056d 100644 --- a/secert-hitler/src/controller/gamemaster/gamemaster/setup.rs +++ b/secert-hitler/src/controller/gamemaster/gamemaster/setup.rs @@ -110,7 +110,7 @@ mod tests { fn run_game_setup() { init(); let mut mrs = MockRooms::new(); - let r1 = mrs.create(); + let r1 = mrs.create("__gm__".to_string()); let mut gm = GameMaster::new(r1); let r = gm.run_game_setup(); assert!(!r.is_ok()); @@ -128,7 +128,7 @@ mod tests { fn setup_gather_candidates() { init(); let mut mrs = MockRooms::new(); - let r1 = mrs.create(); + let r1 = mrs.create("__gm__".to_string()); let mut gm = GameMaster::new(r1); assert!(gm.setup_gather_candidates().is_ok()); for i in 0..config::MIN_PLAYERS { @@ -147,7 +147,7 @@ mod tests { fn setup_set_roles() { init(); let mut mrs = MockRooms::new(); - let r1 = mrs.create(); + let r1 = mrs.create("__gm__".to_string()); let mut gm = GameMaster::new(r1); assert!(gm.setup_set_roles().is_err()); for i in 0..config::MIN_PLAYERS { @@ -168,7 +168,7 @@ mod tests { fn setup_order_candidates() { init(); let mut mrs = MockRooms::new(); - let r1 = mrs.create(); + let r1 = mrs.create("__gm__".to_string()); let mut gm = GameMaster::new(r1); assert!(gm.setup_order_candidates().is_ok()); gm.candidate_presidents = ["1".to_string()].to_vec(); diff --git a/secert-hitler/src/model/state/mockroom.rs b/secert-hitler/src/model/state/mockroom.rs index 43adeeb..9bd02b4 100644 --- a/secert-hitler/src/model/state/mockroom.rs +++ b/secert-hitler/src/model/state/mockroom.rs @@ -11,14 +11,15 @@ pub struct MockRoom { room_id: String, events_s: Sender>, events_r: Receiver>, + pub sender: String, } impl MockRoom { - pub fn create() -> MockRoom { - MockRoom::join(rands()) + pub fn create(sender: String) -> MockRoom { + MockRoom::join(sender, rands()) } - pub fn join(room_id: String) -> MockRoom { + pub fn join(sender: String, room_id: String) -> MockRoom { let (s, r) = unbounded(); s.send(vec![]).ok().unwrap(); let mut mr = MockRoom { @@ -26,12 +27,12 @@ impl MockRoom { room_id: room_id.clone(), events_s: s, events_r: r, + sender: sender, }; - let id = rands(); - mr.send_as(id.clone(), format!(r#"{{ + mr.send_as(mr.sender.clone(), format!(r#"{{ "displayname": "{}", "membership": "join" - }}"#, id.clone())).unwrap(); + }}"#, mr.sender.clone())).unwrap(); mr } @@ -111,13 +112,13 @@ mod tests { use super::rands; fn _dummy() -> MockRoom { - let mut r = MockRoom::create(); + let mut r = MockRoom::create(rands()); r.since = "1".to_string(); let mut events = r.events_r.recv().ok().unwrap(); for i in 0..5 { events.push(Event{ - sender: i.to_string(), - since: i.to_string(), + sender: i.to_string(), + since: i.to_string(), body: i.to_string(), }); } @@ -134,14 +135,14 @@ mod tests { #[test] fn create() { - let mut r: MockRoom = MockRoom::create(); + let mut r: MockRoom = MockRoom::create(rands()); println!("{:?}", r.sync()); } #[test] fn join() { let rid = "a".to_string(); - let mut r: MockRoom = MockRoom::join(rid.to_string()); + let mut r: MockRoom = MockRoom::join(rands(), rid.to_string()); assert!(r.room_id == rid); let events = r.sync(); let mut found = false; diff --git a/secert-hitler/src/model/state/mockrooms.rs b/secert-hitler/src/model/state/mockrooms.rs index 17d4404..968b4bd 100644 --- a/secert-hitler/src/model/state/mockrooms.rs +++ b/secert-hitler/src/model/state/mockrooms.rs @@ -17,16 +17,18 @@ impl MockRooms { } impl Rooms for MockRooms { - fn create(&mut self) -> Box { - let room = MockRoom::create(); + fn create(&mut self, sender: String) -> Box { + let room = MockRoom::create(sender); let _room = room.room(); self.rooms.push(room); Box::new(_room) } - fn join(&self, room_id: String) -> Result, &str> { + fn join(&self, sender: String, room_id: String) -> Result, &str> { for r in &self.rooms { if r.room_id() == room_id { + let mut r = r.clone(); + r.sender = sender; let mut r = r.room(); r.send(format!(r#"{{ "displayname": "{}", @@ -48,9 +50,9 @@ mod tests { fn _dummy() -> MockRooms { let mut mrs = MockRooms::new(); for i in 0..5 { - let random = MockRoom::create(); + let random = MockRoom::create(i.to_string()); mrs.rooms.push(random); - let joined = MockRoom::join(i.to_string()); + let joined = MockRoom::join(i.to_string(), i.to_string()); mrs.rooms.push(joined); } assert!(mrs.rooms.len() == 10); @@ -67,7 +69,7 @@ mod tests { fn create() { let mut mrs = _dummy(); let was = mrs.rooms.len(); - let _ = mrs.create(); + let _ = mrs.create("abc".to_string()); let is = mrs.rooms.len(); assert!(was+1 == is, "was {} rooms, want {} rooms, got {} rooms", was, was+1, is); } @@ -76,7 +78,7 @@ mod tests { fn join_404() { let mrs = _dummy(); let was = mrs.rooms.len(); - let r = mrs.join("does not exist".to_string()); + let r = mrs.join("?".to_string(), "does not exist".to_string()); let is = mrs.rooms.len(); assert!(was == is, "was {} rooms, want {} rooms, got {} rooms", was, was+1, is); assert!(!r.is_ok()); @@ -86,7 +88,7 @@ mod tests { fn join_found() { let mrs = _dummy(); let was = mrs.rooms.len(); - let r = mrs.join("0".to_string()); + let r = mrs.join("?".to_string(), "0".to_string()); let is = mrs.rooms.len(); assert!(was == is, "was {} rooms, want {} rooms, got {} rooms", was, was+1, is); assert!(r.is_ok()); @@ -96,8 +98,8 @@ mod tests { #[test] fn join_clobber() { let mrs = _dummy(); - let mut a = mrs.join("0".to_string()).ok().unwrap(); - let mut b = mrs.join("0".to_string()).ok().unwrap(); + let mut a = mrs.join("?".to_string(), "0".to_string()).ok().unwrap(); + let mut b = mrs.join("?".to_string(), "0".to_string()).ok().unwrap(); assert!(a.room_id() == b.room_id()); diff --git a/secert-hitler/src/model/state/room.rs b/secert-hitler/src/model/state/room.rs index 9ec45f3..8babf46 100644 --- a/secert-hitler/src/model/state/room.rs +++ b/secert-hitler/src/model/state/room.rs @@ -18,7 +18,7 @@ mod tests { #[test] fn mockroom() { fn gen() -> impl Room { - let r = MockRoom::create(); + let r = MockRoom::create("123".to_string()); r } gen(); diff --git a/secert-hitler/src/model/state/rooms.rs b/secert-hitler/src/model/state/rooms.rs index d5945dd..e73be2a 100644 --- a/secert-hitler/src/model/state/rooms.rs +++ b/secert-hitler/src/model/state/rooms.rs @@ -1,8 +1,8 @@ use super::room::Room; pub trait Rooms { - fn create(&mut self) -> Box; - fn join(&self, room_id: String) -> Result, &str>; + fn create(&mut self, sender: String) -> Box; + fn join(&self, sender: String, room_id: String) -> Result, &str>; } #[cfg(test)] @@ -18,7 +18,7 @@ mod tests { r } let mut rooms = gen(); - let mut room_ptr: Box = rooms.create(); + let mut room_ptr: Box = rooms.create("abc".to_string()); assert!(room_ptr.send("hi".to_string()).is_ok()); } }