diff --git a/src/main.rs b/src/main.rs index 9b0b8e0..a99a20d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -84,20 +84,10 @@ fn main() { }; // TODO: make this configurable - // let strategy = strategies::examples::AlwaysPlayConfig; - // let strategy = strategies::examples::RandomStrategyConfig { - // hint_probability: 0.4, - // play_probability: 0.2, - // }; - let strategy = strategies::cheating::CheatingStrategyConfig::new(); - if n == 1 { - simulator::simulate_symmetric_once(&opts, strategy, seed); - } else { - simulator::simulate_symmetric(&opts, strategy, n, seed); - } - - // simulator::simulate_symmetric_once( - // &opts, Some(999), - // strategies::cheating::CheatingStrategyConfig::new(), - // ); + let strategy_config = strategies::examples::RandomStrategyConfig { + hint_probability: 0.4, + play_probability: 0.2, + }; + // let strategy_config = strategies::cheating::CheatingStrategyConfig::new(); + simulator::simulate(&opts, &strategy_config, seed, n); } diff --git a/src/simulator.rs b/src/simulator.rs index e17dabe..89862dd 100644 --- a/src/simulator.rs +++ b/src/simulator.rs @@ -2,19 +2,27 @@ use rand::{self, Rng}; use game::*; use std::collections::HashMap; -// Trait to implement for any valid Hanabi strategy -// State management is done by the simulator, to avoid cheating -pub trait Strategy { +// Traits to implement for any valid Hanabi strategy + +// Represents the strategy of a given player +pub trait PlayerStrategy { fn decide(&mut self, &GameStateView) -> TurnChoice; fn update(&mut self, &Turn, &GameStateView); } -pub trait StrategyConfig { - fn initialize(&self, Player, &GameStateView) -> Box; +// Represents the overall strategy for a game +// Shouldn't do much, except possibility e.g. initialize some shared randomness between players +pub trait GameStrategy { + fn initialize(&self, Player, &GameStateView) -> Box; +} +// Represents configuration for a strategy. +// Acts as a factory for game strategies, so we can play many rounds +pub trait GameStrategyConfig { + fn initialize(&self, &GameOptions) -> Box; } -pub fn simulate_once<'a>( +pub fn simulate_once( opts: &GameOptions, - strat_configs: &Vec>, + game_strategy: Box, seed_opt: Option, ) -> Score { @@ -22,16 +30,12 @@ pub fn simulate_once<'a>( let mut game = GameState::new(opts, seed); - assert_eq!(opts.num_players, (strat_configs.len() as u32)); - - let mut strategies : HashMap> = HashMap::new(); - let mut i = 0; + let mut strategies : HashMap> = HashMap::new(); for player in game.get_players() { strategies.insert( player, - (*strat_configs[i]).initialize(player.clone(), &game.get_view(player)), + game_strategy.initialize(player.clone(), &game.get_view(player)), ); - i += 1; } debug!("Initial state:\n{}", game); @@ -65,11 +69,11 @@ pub fn simulate_once<'a>( } // TODO: multithreaded -pub fn simulate<'a>( +pub fn simulate( opts: &GameOptions, - strat_configs: &Vec>, + strat_config: &GameStrategyConfig, + first_seed_opt: Option, n_trials: u32, - first_seed_opt: Option ) -> f32 { let mut total_score = 0; @@ -78,13 +82,14 @@ pub fn simulate<'a>( let first_seed = first_seed_opt.unwrap_or(rand::thread_rng().next_u32()); info!("Initial seed: {}\n", first_seed); let mut histogram = HashMap::::new(); + for i in 0..n_trials { if (i > 0) && (i % 1000 == 0) { let average: f32 = (total_score as f32) / (i as f32); info!("Trials: {}, Average so far: {}", i, average); } let seed = first_seed + i; - let score = simulate_once(&opts, strat_configs, Some(seed)); + let score = simulate_once(&opts, strat_config.initialize(&opts), Some(seed)); let count = histogram.get(&score).unwrap_or(&0) + 1; histogram.insert(score, count); if score != 25 { @@ -100,30 +105,3 @@ pub fn simulate<'a>( info!("Average score: {:?}", average); average } - -pub fn simulate_symmetric_once<'a, S: StrategyConfig + Clone + 'a>( - opts: &GameOptions, - strat_config: S, - seed_opt: Option, - ) -> Score { - - let mut strat_configs = Vec::new(); - for _ in 0..opts.num_players { - strat_configs.push(Box::new(strat_config.clone()) as Box); - } - simulate_once(opts, &strat_configs, seed_opt) -} - -pub fn simulate_symmetric<'a, S: StrategyConfig + Clone + 'a>( - opts: &GameOptions, - strat_config: S, - n_trials: u32, - first_seed_opt: Option, - ) -> f32 { - - let mut strat_configs = Vec::new(); - for _ in 0..opts.num_players { - strat_configs.push(Box::new(strat_config.clone()) as Box); - } - simulate(opts, &strat_configs, n_trials, first_seed_opt) -} diff --git a/src/strategies/cheating.rs b/src/strategies/cheating.rs index 1857886..867cfaf 100644 --- a/src/strategies/cheating.rs +++ b/src/strategies/cheating.rs @@ -18,31 +18,44 @@ use game::*; // - discard the first card #[allow(dead_code)] -#[derive(Clone)] -pub struct CheatingStrategyConfig { - player_states_cheat: Rc>>, -} +pub struct CheatingStrategyConfig; impl CheatingStrategyConfig { pub fn new() -> CheatingStrategyConfig { - CheatingStrategyConfig { + CheatingStrategyConfig + } +} +impl GameStrategyConfig for CheatingStrategyConfig { + fn initialize(&self, _: &GameOptions) -> Box { + Box::new(CheatingStrategy::new()) + } +} + +pub struct CheatingStrategy { + player_states_cheat: Rc>>, +} + +impl CheatingStrategy { + pub fn new() -> CheatingStrategy { + CheatingStrategy { player_states_cheat: Rc::new(RefCell::new(HashMap::new())), } } } -impl <'a> StrategyConfig for CheatingStrategyConfig { - fn initialize(&self, player: Player, _: &GameStateView) -> Box { - Box::new(CheatingStrategy { +impl GameStrategy for CheatingStrategy { + fn initialize(&self, player: Player, _: &GameStateView) -> Box { + Box::new(CheatingPlayerStrategy { player_states_cheat: self.player_states_cheat.clone(), me: player, }) } } -pub struct CheatingStrategy { + +pub struct CheatingPlayerStrategy { player_states_cheat: Rc>>, me: Player, } -impl CheatingStrategy { +impl CheatingPlayerStrategy { // help next player cheat! fn inform_next_player_cards(&self, view: &GameStateView) { let next = view.board.player_to_left(&self.me); @@ -131,7 +144,7 @@ impl CheatingStrategy { false } } -impl Strategy for CheatingStrategy { +impl PlayerStrategy for CheatingPlayerStrategy { fn decide(&mut self, view: &GameStateView) -> TurnChoice { self.inform_next_player_cards(view); if view.board.turn <= view.board.num_players { diff --git a/src/strategies/examples.rs b/src/strategies/examples.rs index 16137ba..d141c49 100644 --- a/src/strategies/examples.rs +++ b/src/strategies/examples.rs @@ -2,44 +2,7 @@ use simulator::*; use game::*; use rand::{self, Rng}; -// dummy, terrible strategy -#[allow(dead_code)] -#[derive(Clone)] -pub struct AlwaysPlayConfig; -impl StrategyConfig for AlwaysPlayConfig { - fn initialize(&self, _: Player, _: &GameStateView) -> Box { - Box::new(AlwaysPlay) - } -} -pub struct AlwaysPlay; -impl Strategy for AlwaysPlay { - fn decide(&mut self, _: &GameStateView) -> TurnChoice { - TurnChoice::Play(0) - } - fn update(&mut self, _: &Turn, _: &GameStateView) { - } -} - -// dummy, terrible strategy -#[allow(dead_code)] -#[derive(Clone)] -pub struct AlwaysDiscardConfig; -impl StrategyConfig for AlwaysDiscardConfig { - fn initialize(&self, _: Player, _: &GameStateView) -> Box { - Box::new(AlwaysDiscard) - } -} -pub struct AlwaysDiscard; -impl Strategy for AlwaysDiscard { - fn decide(&mut self, _: &GameStateView) -> TurnChoice { - TurnChoice::Discard(0) - } - fn update(&mut self, _: &Turn, _: &GameStateView) { - } -} - - -// dummy, terrible strategy +// dummy, terrible strategy, as an example #[allow(dead_code)] #[derive(Clone)] pub struct RandomStrategyConfig { @@ -47,22 +10,36 @@ pub struct RandomStrategyConfig { pub play_probability: f64, } -impl StrategyConfig for RandomStrategyConfig { - fn initialize(&self, player: Player, _: &GameStateView) -> Box { +impl GameStrategyConfig for RandomStrategyConfig { + fn initialize(&self, _: &GameOptions) -> Box { Box::new(RandomStrategy { hint_probability: self.hint_probability, play_probability: self.play_probability, + }) + } +} + +pub struct RandomStrategy { + hint_probability: f64, + play_probability: f64, +} +impl GameStrategy for RandomStrategy { + fn initialize(&self, player: Player, _: &GameStateView) -> Box { + Box::new(RandomStrategyPlayer { + hint_probability: self.hint_probability, + play_probability: self.play_probability, me: player, }) } } -pub struct RandomStrategy { + +pub struct RandomStrategyPlayer { hint_probability: f64, play_probability: f64, me: Player, } -impl Strategy for RandomStrategy { +impl PlayerStrategy for RandomStrategyPlayer { fn decide(&mut self, view: &GameStateView) -> TurnChoice { let p = rand::random::(); if p < self.hint_probability {