Refacotr to small files dedicated to gamemaster sub ops
This commit is contained in:
@@ -13,3 +13,11 @@ pub fn players_to_facists(n: usize) -> Result<usize, String> {
|
|||||||
_ => Err("unsupported number of players".to_string()),
|
_ => Err("unsupported number of players".to_string()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn players_to_policies_facist(n: usize) -> usize {
|
||||||
|
11 - (n-5)/2
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn players_to_policies_liberal(_: usize) -> usize {
|
||||||
|
6
|
||||||
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ pub enum GameEventType {
|
|||||||
ElectionPend,
|
ElectionPend,
|
||||||
ElectionSet,
|
ElectionSet,
|
||||||
VoteSet,
|
VoteSet,
|
||||||
|
VoteFailed,
|
||||||
CardPend,
|
CardPend,
|
||||||
CardPick,
|
CardPick,
|
||||||
PolicySet,
|
PolicySet,
|
||||||
@@ -26,7 +27,7 @@ pub enum GameEventType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl GameEventType {
|
impl GameEventType {
|
||||||
fn from_string(s: String) -> GameEventType {
|
pub fn from_string(s: String) -> GameEventType {
|
||||||
let cases = vec![
|
let cases = vec![
|
||||||
GameEventType::Null,
|
GameEventType::Null,
|
||||||
GameEventType::GameStart,
|
GameEventType::GameStart,
|
||||||
@@ -51,7 +52,7 @@ impl GameEventType {
|
|||||||
GameEventType::Null
|
GameEventType::Null
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build(&self) -> GameEvent {
|
pub fn build(&self) -> GameEvent {
|
||||||
let d = json::object!{
|
let d = json::object!{
|
||||||
"body": json::object!{
|
"body": json::object!{
|
||||||
GameEventType: format!("{:?}", self),
|
GameEventType: format!("{:?}", self),
|
||||||
@@ -103,7 +104,7 @@ impl GameEvent {
|
|||||||
self.str_vec("params")
|
self.str_vec("params")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn str_vec(&self, key: &str) -> Vec<String> {
|
pub fn str_vec(&self, key: &str) -> Vec<String> {
|
||||||
let mut out = vec![];
|
let mut out = vec![];
|
||||||
if self.d[key].is_string() {
|
if self.d[key].is_string() {
|
||||||
out.push(self.d[key].as_str().unwrap().to_string());
|
out.push(self.d[key].as_str().unwrap().to_string());
|
||||||
|
|||||||
@@ -1,339 +0,0 @@
|
|||||||
use super::super::super::model::state::room::Room;
|
|
||||||
use super::super::super::config;
|
|
||||||
use super::lobby::Lobby;
|
|
||||||
use super::player::Player;
|
|
||||||
use super::gameevent::GameEvent;
|
|
||||||
use super::gameevent::GameEventType;
|
|
||||||
use super::role::Role;
|
|
||||||
|
|
||||||
use std::thread;
|
|
||||||
use std::time;
|
|
||||||
use log::{info, debug, error, LevelFilter};
|
|
||||||
use rand::{self, Rng};
|
|
||||||
|
|
||||||
pub struct GameMaster {
|
|
||||||
room: Box<dyn Room>,
|
|
||||||
lobby: Lobby,
|
|
||||||
candidate_presidents: Vec<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GameMaster {
|
|
||||||
pub fn new(room: Box<dyn Room>) -> GameMaster {
|
|
||||||
info!("created for room {}", room.room_id());
|
|
||||||
GameMaster{
|
|
||||||
room: room,
|
|
||||||
lobby: Lobby::new(),
|
|
||||||
candidate_presidents: vec![],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn run(&mut self) -> Result<String, String> {
|
|
||||||
loop {
|
|
||||||
let r = self.run_lobby();
|
|
||||||
if r.is_ok() {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
error!("error running lobby: {:?}", r);
|
|
||||||
}
|
|
||||||
self.run_game_setup()?;
|
|
||||||
self.run_game()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run_lobby(&mut self) -> Result<usize, String> {
|
|
||||||
loop {
|
|
||||||
let r = self.run_lobby_scrape().clone();
|
|
||||||
if r.clone().is_err() {
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
if r.clone().unwrap_or(0) != 0 {
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
thread::sleep(time::Duration::new(1, 0));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run_lobby_scrape(&mut self) -> Result<usize, String> {
|
|
||||||
debug!("run_lobby_scrape:");
|
|
||||||
let events = self.room.sync();
|
|
||||||
for e in &events {
|
|
||||||
let ge = GameEvent::new(e.clone());
|
|
||||||
debug!("run_lobby_scrape: ge: {:?}", ge);
|
|
||||||
if ge.mode() == GameEventType::GameStart {
|
|
||||||
self.room.rollback(e.since.clone());
|
|
||||||
self.lobby.lock();
|
|
||||||
return self.lobby.ready();
|
|
||||||
}
|
|
||||||
self.lobby.eat(e.clone());
|
|
||||||
}
|
|
||||||
return self.lobby.ready();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run_game_setup(&mut self) -> Result<String, String> {
|
|
||||||
self.setup_gather_candidates()?;
|
|
||||||
debug!("players: {:?}", self.candidate_presidents);
|
|
||||||
self.setup_set_roles()?;
|
|
||||||
debug!("/players: {:?}", self.candidate_presidents);
|
|
||||||
self.setup_order_candidates()?;
|
|
||||||
debug!("/players: {:?}", self.candidate_presidents);
|
|
||||||
Ok("ok".to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run_game(&mut self) -> Result<String, String> {
|
|
||||||
Err("not impl".to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn players(&self) -> Vec<String> {
|
|
||||||
let mut players = vec![];
|
|
||||||
for k in self.lobby.players.keys() {
|
|
||||||
players.push(k.clone());
|
|
||||||
}
|
|
||||||
players
|
|
||||||
}
|
|
||||||
|
|
||||||
fn player(&mut self, id: String) -> Option<&mut Player> {
|
|
||||||
self.lobby.players.get_mut(&id)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn setup_gather_candidates(&mut self) -> Result<String, String> {
|
|
||||||
for player in self.players() {
|
|
||||||
let p = self.player(player.clone());
|
|
||||||
if p.is_none() {
|
|
||||||
return Err(format!("missing player {}", player));
|
|
||||||
}
|
|
||||||
self.candidate_presidents.push(player.clone());
|
|
||||||
debug!("player = {}", player);
|
|
||||||
}
|
|
||||||
Ok("ok".to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn setup_set_roles(&mut self) -> Result<String, String> {
|
|
||||||
for player in self.players() {
|
|
||||||
self.player(player.clone()).unwrap().set_role(Role::Liberal);
|
|
||||||
}
|
|
||||||
let n = config::players_to_facists(self.players().len())?;
|
|
||||||
for i in 0..n {
|
|
||||||
debug!("picking facist {}/{} for {} players", i, n, self.players().len());
|
|
||||||
loop {
|
|
||||||
let j = rand_usize(self.players().len());
|
|
||||||
let id = self.candidate_presidents[j].clone();
|
|
||||||
let player = self.player(id.clone()).unwrap();
|
|
||||||
if player.get_role() != Role::Liberal {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let role = match i {
|
|
||||||
0 => Role::Hitler,
|
|
||||||
_ => Role::Facist,
|
|
||||||
};
|
|
||||||
player.set_role(role);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok("ok".to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn setup_order_candidates(&mut self) -> Result<String, String> {
|
|
||||||
shuffle(&mut self.candidate_presidents);
|
|
||||||
let n = self.candidate_presidents.len();
|
|
||||||
for _ in 0..5 {
|
|
||||||
for i in 0..n {
|
|
||||||
self.candidate_presidents.push(self.candidate_presidents[i].clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok("ok".to_string())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn shuffle<T: Clone>(v: &mut Vec<T>) {
|
|
||||||
for _ in 0..v.len()*2 {
|
|
||||||
let a = rand_usize(v.len());
|
|
||||||
let b = rand_usize(v.len());
|
|
||||||
let t: T = v[a].clone();
|
|
||||||
v[a] = v[b].clone();
|
|
||||||
v[b] = t.clone();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn rand_usize(n: usize) -> usize {
|
|
||||||
rand::thread_rng().gen_range(0, n)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use super::super::super::super::model::state::mockroom::MockRoom;
|
|
||||||
use super::super::super::super::model::state::mockrooms::MockRooms;
|
|
||||||
use super::super::super::super::model::state::rooms::Rooms;
|
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
fn init() {
|
|
||||||
let _ = env_logger::builder()
|
|
||||||
.is_test(true)
|
|
||||||
.filter_level(LevelFilter::Trace)
|
|
||||||
.try_init();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn _rand_usize() {
|
|
||||||
init();
|
|
||||||
let mut unique: HashMap<usize, bool> = HashMap::new();
|
|
||||||
for _ in 0..100 {
|
|
||||||
unique.insert(rand_usize(100), true);
|
|
||||||
}
|
|
||||||
assert!(unique.len() > 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn _shuffle() {
|
|
||||||
init();
|
|
||||||
let mut items: Vec<usize> = vec![];
|
|
||||||
let n: usize = 50;
|
|
||||||
for i in 0..n {
|
|
||||||
items.push(i);
|
|
||||||
}
|
|
||||||
assert!(items.len() == n);
|
|
||||||
shuffle(&mut items);
|
|
||||||
assert!(items.len() == n);
|
|
||||||
let mut found = false;
|
|
||||||
for i in 1..items.len() {
|
|
||||||
found = found || items[i] < items[i-1];
|
|
||||||
}
|
|
||||||
assert!(found);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn new_mockroom() {
|
|
||||||
let _ = GameMaster::new(Box::new(MockRoom::create()));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn new_rooms_mockrooms() {
|
|
||||||
fn get() -> impl Rooms {
|
|
||||||
let mrs = MockRooms::new();
|
|
||||||
mrs
|
|
||||||
}
|
|
||||||
let mut mrs = get();
|
|
||||||
let r = mrs.create();
|
|
||||||
let _ = GameMaster::new(r);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn new_mockrooms() {
|
|
||||||
let mut mrs = MockRooms::new();
|
|
||||||
let r = mrs.create();
|
|
||||||
let _ = GameMaster::new(r);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn run_lobby() {
|
|
||||||
init();
|
|
||||||
let mut mrs = MockRooms::new();
|
|
||||||
let r1 = mrs.create();
|
|
||||||
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();
|
|
||||||
r2.send(format!(r#"{{
|
|
||||||
"msgtype": "m.text",
|
|
||||||
"body": "{{\"GameEventType\": \"GameStart\"}}"
|
|
||||||
}}"#)).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());
|
|
||||||
}
|
|
||||||
assert!(gm.lobby.players.len() == 5, "first run_lobby players: {:?}, sync: {:?}", gm.lobby.players, gm.room.sync());
|
|
||||||
let mut players1 = vec![];
|
|
||||||
for k in gm.lobby.players.keys() {
|
|
||||||
players1.push(k.clone());
|
|
||||||
}
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn run_game_setup() {
|
|
||||||
init();
|
|
||||||
let mut mrs = MockRooms::new();
|
|
||||||
let r1 = mrs.create();
|
|
||||||
let mut gm = GameMaster::new(r1);
|
|
||||||
let r = gm.run_game_setup();
|
|
||||||
assert!(!r.is_ok());
|
|
||||||
for i in 0..config::MIN_PLAYERS {
|
|
||||||
let id = format!("{}", i);
|
|
||||||
gm.lobby.players.insert(id.clone(), Player::new(id.clone()));
|
|
||||||
}
|
|
||||||
let r = gm.run_game_setup();
|
|
||||||
assert!(r.is_ok(), "failed to start game after sufficient players joined: {:?}", r);
|
|
||||||
assert!(gm.candidate_presidents.len() > gm.lobby.players.len());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn run_game_fail() {
|
|
||||||
assert!(false, "not impl");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn setup_gather_candidates() {
|
|
||||||
init();
|
|
||||||
let mut mrs = MockRooms::new();
|
|
||||||
let r1 = mrs.create();
|
|
||||||
let mut gm = GameMaster::new(r1);
|
|
||||||
assert!(gm.setup_gather_candidates().is_ok());
|
|
||||||
for i in 0..config::MIN_PLAYERS {
|
|
||||||
let id = format!("{}", i);
|
|
||||||
gm.lobby.players.insert(id.clone(), Player::new(id.clone()));
|
|
||||||
}
|
|
||||||
assert!(gm.setup_gather_candidates().is_ok());
|
|
||||||
for i in config::MIN_PLAYERS..config::MAX_PLAYERS+1 {
|
|
||||||
let id = format!("{}", i);
|
|
||||||
gm.lobby.players.insert(id.clone(), Player::new(id.clone()));
|
|
||||||
}
|
|
||||||
assert!(gm.setup_gather_candidates().is_ok());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn setup_set_roles() {
|
|
||||||
init();
|
|
||||||
let mut mrs = MockRooms::new();
|
|
||||||
let r1 = mrs.create();
|
|
||||||
let mut gm = GameMaster::new(r1);
|
|
||||||
assert!(gm.setup_set_roles().is_err());
|
|
||||||
for i in 0..config::MIN_PLAYERS {
|
|
||||||
let id = format!("{}", i);
|
|
||||||
gm.lobby.players.insert(id.clone(), Player::new(id.clone()));
|
|
||||||
gm.candidate_presidents.push(id.clone());
|
|
||||||
}
|
|
||||||
assert!(gm.setup_set_roles().is_ok());
|
|
||||||
for i in config::MIN_PLAYERS..config::MAX_PLAYERS+1 {
|
|
||||||
let id = format!("{}", i);
|
|
||||||
gm.lobby.players.insert(id.clone(), Player::new(id.clone()));
|
|
||||||
gm.candidate_presidents.push(id.clone());
|
|
||||||
}
|
|
||||||
assert!(gm.setup_set_roles().is_err());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn setup_order_candidates() {
|
|
||||||
init();
|
|
||||||
let mut mrs = MockRooms::new();
|
|
||||||
let r1 = mrs.create();
|
|
||||||
let mut gm = GameMaster::new(r1);
|
|
||||||
assert!(gm.setup_order_candidates().is_ok());
|
|
||||||
gm.candidate_presidents = ["1".to_string()].to_vec();
|
|
||||||
assert!(gm.setup_order_candidates().is_ok());
|
|
||||||
assert!(gm.candidate_presidents.len() > 1);
|
|
||||||
gm.candidate_presidents = [].to_vec();
|
|
||||||
for i in 0..50 {
|
|
||||||
gm.candidate_presidents.push(format!("{}", i));
|
|
||||||
}
|
|
||||||
let was = format!("{:?}", gm.candidate_presidents);
|
|
||||||
assert!(gm.setup_order_candidates().is_ok());
|
|
||||||
assert!(gm.candidate_presidents.len() > 50);
|
|
||||||
assert!(was != format!("{:?}", gm.candidate_presidents));
|
|
||||||
let mut found = false;
|
|
||||||
for i in 1..gm.candidate_presidents.len() {
|
|
||||||
found = found || gm.candidate_presidents[i] < gm.candidate_presidents[i-1];
|
|
||||||
}
|
|
||||||
assert!(found);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
56
secert-hitler/src/controller/gamemaster/gamemaster/game.rs
Normal file
56
secert-hitler/src/controller/gamemaster/gamemaster/game.rs
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
use super::gamemaster::GameMaster;
|
||||||
|
use super::super::gameevent::GameEvent;
|
||||||
|
use super::super::policy::Policy;
|
||||||
|
|
||||||
|
use log::{LevelFilter};
|
||||||
|
|
||||||
|
impl GameMaster {
|
||||||
|
pub fn game_is_over(&mut self) -> Result<GameEvent, String> {
|
||||||
|
Err("not impl".to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn game_election(&mut self) -> Result<GameEvent, String> {
|
||||||
|
Err("not impl".to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn game_election_vote(&mut self) -> Result<GameEvent, String> {
|
||||||
|
Err("not impl".to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn game_policy_select_random(&mut self) -> Result<Policy, String> {
|
||||||
|
Err("not impl".to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn game_policy_select(&mut self) -> Result<Policy, String> {
|
||||||
|
Err("not impl".to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn game_policy_veto(&mut self, _p: Policy) -> Result<GameEvent, String> {
|
||||||
|
Err("not impl".to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn game_ends_with(&mut self, _p: Policy) -> Result<GameEvent, String> {
|
||||||
|
Err("not impl".to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn game_policy_enact(&mut self, _p: Policy) -> Result<GameEvent, String> {
|
||||||
|
Err("not impl".to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
fn init() {
|
||||||
|
let _ = env_logger::builder()
|
||||||
|
.is_test(true)
|
||||||
|
.filter_level(LevelFilter::Trace)
|
||||||
|
.try_init();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn game() {
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
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::gameevent::GameEventType;
|
||||||
|
|
||||||
|
use log::{info, error};
|
||||||
|
|
||||||
|
pub struct GameMaster {
|
||||||
|
pub room: Box<dyn Room>,
|
||||||
|
pub lobby: Lobby,
|
||||||
|
pub candidate_presidents: Vec<String>,
|
||||||
|
pub deck: Vec<Policy>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GameMaster {
|
||||||
|
|
||||||
|
pub fn new(room: Box<dyn Room>) -> GameMaster {
|
||||||
|
info!("created for room {}", room.room_id());
|
||||||
|
GameMaster{
|
||||||
|
room: room,
|
||||||
|
lobby: Lobby::new(),
|
||||||
|
candidate_presidents: vec![],
|
||||||
|
deck: vec![],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run(&mut self) -> Result<String, String> {
|
||||||
|
loop {
|
||||||
|
let r = self.run_lobby();
|
||||||
|
if r.is_ok() {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
error!("error running lobby: {:?}", r);
|
||||||
|
}
|
||||||
|
self.run_game_setup()?;
|
||||||
|
self.run_game()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run_game(&mut self) -> Result<String, String> {
|
||||||
|
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()),
|
||||||
|
_ => 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn players(&self) -> Vec<String> {
|
||||||
|
let mut players = vec![];
|
||||||
|
for k in self.lobby.players.keys() {
|
||||||
|
players.push(k.clone());
|
||||||
|
}
|
||||||
|
players
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn player(&mut self, id: String) -> Option<&mut Player> {
|
||||||
|
self.lobby.players.get_mut(&id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use super::super::super::super::super::model::state::mockroom::MockRoom;
|
||||||
|
use super::super::super::super::super::model::state::mockrooms::MockRooms;
|
||||||
|
use super::super::super::super::super::model::state::rooms::Rooms;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn new_mockroom() {
|
||||||
|
let _ = GameMaster::new(Box::new(MockRoom::create()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn new_rooms_mockrooms() {
|
||||||
|
fn get() -> impl Rooms {
|
||||||
|
let mrs = MockRooms::new();
|
||||||
|
mrs
|
||||||
|
}
|
||||||
|
let mut mrs = get();
|
||||||
|
let r = mrs.create();
|
||||||
|
let _ = GameMaster::new(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn new_mockrooms() {
|
||||||
|
let mut mrs = MockRooms::new();
|
||||||
|
let r = mrs.create();
|
||||||
|
let _ = GameMaster::new(r);
|
||||||
|
}
|
||||||
|
}
|
||||||
81
secert-hitler/src/controller/gamemaster/gamemaster/lobby.rs
Normal file
81
secert-hitler/src/controller/gamemaster/gamemaster/lobby.rs
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
use super::gamemaster::GameMaster;
|
||||||
|
|
||||||
|
use super::super::gameevent::GameEvent;
|
||||||
|
use super::super::gameevent::GameEventType;
|
||||||
|
|
||||||
|
use std::thread;
|
||||||
|
use std::time;
|
||||||
|
use log::{debug, LevelFilter};
|
||||||
|
|
||||||
|
impl GameMaster {
|
||||||
|
pub fn run_lobby(&mut self) -> Result<usize, String> {
|
||||||
|
loop {
|
||||||
|
let r = self.run_lobby_scrape().clone();
|
||||||
|
if r.clone().is_err() {
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
if r.clone().unwrap_or(0) != 0 {
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
thread::sleep(time::Duration::new(1, 0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run_lobby_scrape(&mut self) -> Result<usize, String> {
|
||||||
|
debug!("run_lobby_scrape:");
|
||||||
|
let events = self.room.sync();
|
||||||
|
for e in &events {
|
||||||
|
let ge = GameEvent::new(e.clone());
|
||||||
|
debug!("run_lobby_scrape: ge: {:?}", ge);
|
||||||
|
if ge.mode() == GameEventType::GameStart {
|
||||||
|
self.room.rollback(e.since.clone());
|
||||||
|
self.lobby.lock();
|
||||||
|
return self.lobby.ready();
|
||||||
|
}
|
||||||
|
self.lobby.eat(e.clone());
|
||||||
|
}
|
||||||
|
return self.lobby.ready();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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;
|
||||||
|
|
||||||
|
fn init() {
|
||||||
|
let _ = env_logger::builder()
|
||||||
|
.is_test(true)
|
||||||
|
.filter_level(LevelFilter::Trace)
|
||||||
|
.try_init();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn run_lobby() {
|
||||||
|
init();
|
||||||
|
let mut mrs = MockRooms::new();
|
||||||
|
let r1 = mrs.create();
|
||||||
|
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();
|
||||||
|
r2.send(format!(r#"{{
|
||||||
|
"msgtype": "m.text",
|
||||||
|
"body": "{{\"GameEventType\": \"GameStart\"}}"
|
||||||
|
}}"#)).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());
|
||||||
|
}
|
||||||
|
assert!(gm.lobby.players.len() == 5, "first run_lobby players: {:?}, sync: {:?}", gm.lobby.players, gm.room.sync());
|
||||||
|
let mut players1 = vec![];
|
||||||
|
for k in gm.lobby.players.keys() {
|
||||||
|
players1.push(k.clone());
|
||||||
|
}
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
pub mod gamemaster;
|
||||||
|
mod lobby;
|
||||||
|
mod setup;
|
||||||
|
mod game;
|
||||||
181
secert-hitler/src/controller/gamemaster/gamemaster/setup.rs
Normal file
181
secert-hitler/src/controller/gamemaster/gamemaster/setup.rs
Normal file
@@ -0,0 +1,181 @@
|
|||||||
|
use super::gamemaster::GameMaster;
|
||||||
|
|
||||||
|
use super::super::super::super::config;
|
||||||
|
use super::super::role::Role;
|
||||||
|
use super::super::policy::Policy;
|
||||||
|
use super::super::rand::rand_usize;
|
||||||
|
use super::super::rand::shuffle;
|
||||||
|
|
||||||
|
use log::{debug, LevelFilter};
|
||||||
|
|
||||||
|
impl GameMaster {
|
||||||
|
pub fn run_game_setup(&mut self) -> Result<String, String> {
|
||||||
|
self.setup_gather_candidates()?;
|
||||||
|
debug!("players: {:?}", self.candidate_presidents);
|
||||||
|
self.setup_set_roles()?;
|
||||||
|
debug!("/players: {:?}", self.candidate_presidents);
|
||||||
|
self.setup_order_candidates()?;
|
||||||
|
debug!("/players: {:?}", self.candidate_presidents);
|
||||||
|
self.setup_deck()?;
|
||||||
|
Ok("ok".to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setup_gather_candidates(&mut self) -> Result<String, String> {
|
||||||
|
for player in self.players() {
|
||||||
|
let p = self.player(player.clone());
|
||||||
|
if p.is_none() {
|
||||||
|
return Err(format!("missing player {}", player));
|
||||||
|
}
|
||||||
|
self.candidate_presidents.push(player.clone());
|
||||||
|
debug!("player = {}", player);
|
||||||
|
}
|
||||||
|
Ok("ok".to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setup_set_roles(&mut self) -> Result<String, String> {
|
||||||
|
for player in self.players() {
|
||||||
|
self.player(player.clone()).unwrap().set_role(Role::Liberal);
|
||||||
|
}
|
||||||
|
let n = config::players_to_facists(self.players().len())?;
|
||||||
|
for i in 0..n {
|
||||||
|
debug!("picking facist {}/{} for {} players", i, n, self.players().len());
|
||||||
|
loop {
|
||||||
|
let j = rand_usize(self.players().len());
|
||||||
|
let id = self.candidate_presidents[j].clone();
|
||||||
|
let player = self.player(id.clone()).unwrap();
|
||||||
|
if player.get_role() != Role::Liberal {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let role = match i {
|
||||||
|
0 => Role::Hitler,
|
||||||
|
_ => Role::Facist,
|
||||||
|
};
|
||||||
|
player.set_role(role);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok("ok".to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setup_order_candidates(&mut self) -> Result<String, String> {
|
||||||
|
shuffle(&mut self.candidate_presidents);
|
||||||
|
let n = self.candidate_presidents.len();
|
||||||
|
for _ in 0..5 {
|
||||||
|
for i in 0..n {
|
||||||
|
self.candidate_presidents.push(self.candidate_presidents[i].clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok("ok".to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setup_deck(&mut self) -> Result<String, String> {
|
||||||
|
for _ in 0..config::players_to_policies_facist(self.players().len()) {
|
||||||
|
self.deck.push(Policy::Facist);
|
||||||
|
}
|
||||||
|
|
||||||
|
for _ in 0..config::players_to_policies_liberal(self.players().len()) {
|
||||||
|
self.deck.push(Policy::Liberal);
|
||||||
|
}
|
||||||
|
|
||||||
|
shuffle(&mut self.deck);
|
||||||
|
|
||||||
|
Ok("deck is loaded".to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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::player::Player;
|
||||||
|
|
||||||
|
fn init() {
|
||||||
|
let _ = env_logger::builder()
|
||||||
|
.is_test(true)
|
||||||
|
.filter_level(LevelFilter::Trace)
|
||||||
|
.try_init();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn run_game_setup() {
|
||||||
|
init();
|
||||||
|
let mut mrs = MockRooms::new();
|
||||||
|
let r1 = mrs.create();
|
||||||
|
let mut gm = GameMaster::new(r1);
|
||||||
|
let r = gm.run_game_setup();
|
||||||
|
assert!(!r.is_ok());
|
||||||
|
for i in 0..config::MIN_PLAYERS {
|
||||||
|
let id = format!("{}", i);
|
||||||
|
gm.lobby.players.insert(id.clone(), Player::new(id.clone()));
|
||||||
|
}
|
||||||
|
let r = gm.run_game_setup();
|
||||||
|
assert!(r.is_ok(), "failed to start game after sufficient players joined: {:?}", r);
|
||||||
|
assert!(gm.candidate_presidents.len() > gm.lobby.players.len());
|
||||||
|
assert!(gm.deck.len() > 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn setup_gather_candidates() {
|
||||||
|
init();
|
||||||
|
let mut mrs = MockRooms::new();
|
||||||
|
let r1 = mrs.create();
|
||||||
|
let mut gm = GameMaster::new(r1);
|
||||||
|
assert!(gm.setup_gather_candidates().is_ok());
|
||||||
|
for i in 0..config::MIN_PLAYERS {
|
||||||
|
let id = format!("{}", i);
|
||||||
|
gm.lobby.players.insert(id.clone(), Player::new(id.clone()));
|
||||||
|
}
|
||||||
|
assert!(gm.setup_gather_candidates().is_ok());
|
||||||
|
for i in config::MIN_PLAYERS..config::MAX_PLAYERS+1 {
|
||||||
|
let id = format!("{}", i);
|
||||||
|
gm.lobby.players.insert(id.clone(), Player::new(id.clone()));
|
||||||
|
}
|
||||||
|
assert!(gm.setup_gather_candidates().is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn setup_set_roles() {
|
||||||
|
init();
|
||||||
|
let mut mrs = MockRooms::new();
|
||||||
|
let r1 = mrs.create();
|
||||||
|
let mut gm = GameMaster::new(r1);
|
||||||
|
assert!(gm.setup_set_roles().is_err());
|
||||||
|
for i in 0..config::MIN_PLAYERS {
|
||||||
|
let id = format!("{}", i);
|
||||||
|
gm.lobby.players.insert(id.clone(), Player::new(id.clone()));
|
||||||
|
gm.candidate_presidents.push(id.clone());
|
||||||
|
}
|
||||||
|
assert!(gm.setup_set_roles().is_ok());
|
||||||
|
for i in config::MIN_PLAYERS..config::MAX_PLAYERS+1 {
|
||||||
|
let id = format!("{}", i);
|
||||||
|
gm.lobby.players.insert(id.clone(), Player::new(id.clone()));
|
||||||
|
gm.candidate_presidents.push(id.clone());
|
||||||
|
}
|
||||||
|
assert!(gm.setup_set_roles().is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn setup_order_candidates() {
|
||||||
|
init();
|
||||||
|
let mut mrs = MockRooms::new();
|
||||||
|
let r1 = mrs.create();
|
||||||
|
let mut gm = GameMaster::new(r1);
|
||||||
|
assert!(gm.setup_order_candidates().is_ok());
|
||||||
|
gm.candidate_presidents = ["1".to_string()].to_vec();
|
||||||
|
assert!(gm.setup_order_candidates().is_ok());
|
||||||
|
assert!(gm.candidate_presidents.len() > 1);
|
||||||
|
gm.candidate_presidents = [].to_vec();
|
||||||
|
for i in 0..50 {
|
||||||
|
gm.candidate_presidents.push(format!("{}", i));
|
||||||
|
}
|
||||||
|
let was = format!("{:?}", gm.candidate_presidents);
|
||||||
|
assert!(gm.setup_order_candidates().is_ok());
|
||||||
|
assert!(gm.candidate_presidents.len() > 50);
|
||||||
|
assert!(was != format!("{:?}", gm.candidate_presidents));
|
||||||
|
let mut found = false;
|
||||||
|
for i in 1..gm.candidate_presidents.len() {
|
||||||
|
found = found || gm.candidate_presidents[i] < gm.candidate_presidents[i-1];
|
||||||
|
}
|
||||||
|
assert!(found);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,3 +3,5 @@ pub mod player;
|
|||||||
pub mod role;
|
pub mod role;
|
||||||
pub mod lobby;
|
pub mod lobby;
|
||||||
pub mod gameevent;
|
pub mod gameevent;
|
||||||
|
pub mod policy;
|
||||||
|
pub mod rand;
|
||||||
|
|||||||
37
secert-hitler/src/controller/gamemaster/policy.rs
Normal file
37
secert-hitler/src/controller/gamemaster/policy.rs
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub enum Policy {
|
||||||
|
Null,
|
||||||
|
Facist,
|
||||||
|
Liberal,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Policy {
|
||||||
|
pub fn new() -> Policy {
|
||||||
|
Policy::Null
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set(&mut self, policy: Policy) {
|
||||||
|
*self = policy.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn new_role() {
|
||||||
|
let _ = Policy::new();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn set() {
|
||||||
|
let mut r = Policy::new();
|
||||||
|
r.set(Policy::Facist);
|
||||||
|
assert!(r == Policy::Facist);
|
||||||
|
|
||||||
|
let mut r = Policy::new();
|
||||||
|
r.set(Policy::Liberal);
|
||||||
|
assert!(r == Policy::Liberal);
|
||||||
|
}
|
||||||
|
}
|
||||||
47
secert-hitler/src/controller/gamemaster/rand.rs
Normal file
47
secert-hitler/src/controller/gamemaster/rand.rs
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
use rand::{self, Rng};
|
||||||
|
|
||||||
|
pub fn shuffle<T: Clone>(v: &mut Vec<T>) {
|
||||||
|
for _ in 0..v.len()*2 {
|
||||||
|
let a = rand_usize(v.len());
|
||||||
|
let b = rand_usize(v.len());
|
||||||
|
let t: T = v[a].clone();
|
||||||
|
v[a] = v[b].clone();
|
||||||
|
v[b] = t.clone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rand_usize(n: usize) -> usize {
|
||||||
|
rand::thread_rng().gen_range(0, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn _rand_usize() {
|
||||||
|
let mut unique: HashMap<usize, bool> = HashMap::new();
|
||||||
|
for _ in 0..100 {
|
||||||
|
unique.insert(rand_usize(100), true);
|
||||||
|
}
|
||||||
|
assert!(unique.len() > 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn _shuffle() {
|
||||||
|
let mut items: Vec<usize> = vec![];
|
||||||
|
let n: usize = 50;
|
||||||
|
for i in 0..n {
|
||||||
|
items.push(i);
|
||||||
|
}
|
||||||
|
assert!(items.len() == n);
|
||||||
|
shuffle(&mut items);
|
||||||
|
assert!(items.len() == n);
|
||||||
|
let mut found = false;
|
||||||
|
for i in 1..items.len() {
|
||||||
|
found = found || items[i] < items[i-1];
|
||||||
|
}
|
||||||
|
assert!(found);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user