another layer of indirection, prep for threading

This commit is contained in:
Jeff Wu 2016-03-16 23:07:21 -07:00
parent 44248c8d3e
commit 706f5b52b6
4 changed files with 71 additions and 113 deletions

View file

@ -84,20 +84,10 @@ fn main() {
}; };
// TODO: make this configurable // TODO: make this configurable
// let strategy = strategies::examples::AlwaysPlayConfig; let strategy_config = strategies::examples::RandomStrategyConfig {
// let strategy = strategies::examples::RandomStrategyConfig { hint_probability: 0.4,
// hint_probability: 0.4, play_probability: 0.2,
// play_probability: 0.2, };
// }; // let strategy_config = strategies::cheating::CheatingStrategyConfig::new();
let strategy = strategies::cheating::CheatingStrategyConfig::new(); simulator::simulate(&opts, &strategy_config, seed, n);
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(),
// );
} }

View file

@ -2,19 +2,27 @@ use rand::{self, Rng};
use game::*; use game::*;
use std::collections::HashMap; use std::collections::HashMap;
// Trait to implement for any valid Hanabi strategy // Traits to implement for any valid Hanabi strategy
// State management is done by the simulator, to avoid cheating
pub trait Strategy { // Represents the strategy of a given player
pub trait PlayerStrategy {
fn decide(&mut self, &GameStateView) -> TurnChoice; fn decide(&mut self, &GameStateView) -> TurnChoice;
fn update(&mut self, &Turn, &GameStateView); fn update(&mut self, &Turn, &GameStateView);
} }
pub trait StrategyConfig { // Represents the overall strategy for a game
fn initialize(&self, Player, &GameStateView) -> Box<Strategy>; // Shouldn't do much, except possibility e.g. initialize some shared randomness between players
pub trait GameStrategy {
fn initialize(&self, Player, &GameStateView) -> Box<PlayerStrategy>;
}
// 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<GameStrategy>;
} }
pub fn simulate_once<'a>( pub fn simulate_once(
opts: &GameOptions, opts: &GameOptions,
strat_configs: &Vec<Box<StrategyConfig + 'a>>, game_strategy: Box<GameStrategy>,
seed_opt: Option<u32>, seed_opt: Option<u32>,
) -> Score { ) -> Score {
@ -22,16 +30,12 @@ pub fn simulate_once<'a>(
let mut game = GameState::new(opts, seed); let mut game = GameState::new(opts, seed);
assert_eq!(opts.num_players, (strat_configs.len() as u32)); let mut strategies : HashMap<Player, Box<PlayerStrategy>> = HashMap::new();
let mut strategies : HashMap<Player, Box<Strategy>> = HashMap::new();
let mut i = 0;
for player in game.get_players() { for player in game.get_players() {
strategies.insert( strategies.insert(
player, 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); debug!("Initial state:\n{}", game);
@ -65,11 +69,11 @@ pub fn simulate_once<'a>(
} }
// TODO: multithreaded // TODO: multithreaded
pub fn simulate<'a>( pub fn simulate(
opts: &GameOptions, opts: &GameOptions,
strat_configs: &Vec<Box<StrategyConfig + 'a>>, strat_config: &GameStrategyConfig,
first_seed_opt: Option<u32>,
n_trials: u32, n_trials: u32,
first_seed_opt: Option<u32>
) -> f32 { ) -> f32 {
let mut total_score = 0; 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()); let first_seed = first_seed_opt.unwrap_or(rand::thread_rng().next_u32());
info!("Initial seed: {}\n", first_seed); info!("Initial seed: {}\n", first_seed);
let mut histogram = HashMap::<Score, usize>::new(); let mut histogram = HashMap::<Score, usize>::new();
for i in 0..n_trials { for i in 0..n_trials {
if (i > 0) && (i % 1000 == 0) { if (i > 0) && (i % 1000 == 0) {
let average: f32 = (total_score as f32) / (i as f32); let average: f32 = (total_score as f32) / (i as f32);
info!("Trials: {}, Average so far: {}", i, average); info!("Trials: {}, Average so far: {}", i, average);
} }
let seed = first_seed + i; 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; let count = histogram.get(&score).unwrap_or(&0) + 1;
histogram.insert(score, count); histogram.insert(score, count);
if score != 25 { if score != 25 {
@ -100,30 +105,3 @@ pub fn simulate<'a>(
info!("Average score: {:?}", average); info!("Average score: {:?}", average);
average average
} }
pub fn simulate_symmetric_once<'a, S: StrategyConfig + Clone + 'a>(
opts: &GameOptions,
strat_config: S,
seed_opt: Option<u32>,
) -> Score {
let mut strat_configs = Vec::new();
for _ in 0..opts.num_players {
strat_configs.push(Box::new(strat_config.clone()) as Box<StrategyConfig + 'a>);
}
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<u32>,
) -> f32 {
let mut strat_configs = Vec::new();
for _ in 0..opts.num_players {
strat_configs.push(Box::new(strat_config.clone()) as Box<StrategyConfig + 'a>);
}
simulate(opts, &strat_configs, n_trials, first_seed_opt)
}

View file

@ -18,31 +18,44 @@ use game::*;
// - discard the first card // - discard the first card
#[allow(dead_code)] #[allow(dead_code)]
#[derive(Clone)] pub struct CheatingStrategyConfig;
pub struct CheatingStrategyConfig {
player_states_cheat: Rc<RefCell<HashMap<Player, Cards>>>,
}
impl CheatingStrategyConfig { impl CheatingStrategyConfig {
pub fn new() -> CheatingStrategyConfig { pub fn new() -> CheatingStrategyConfig {
CheatingStrategyConfig { CheatingStrategyConfig
}
}
impl GameStrategyConfig for CheatingStrategyConfig {
fn initialize(&self, _: &GameOptions) -> Box<GameStrategy> {
Box::new(CheatingStrategy::new())
}
}
pub struct CheatingStrategy {
player_states_cheat: Rc<RefCell<HashMap<Player, Cards>>>,
}
impl CheatingStrategy {
pub fn new() -> CheatingStrategy {
CheatingStrategy {
player_states_cheat: Rc::new(RefCell::new(HashMap::new())), player_states_cheat: Rc::new(RefCell::new(HashMap::new())),
} }
} }
} }
impl <'a> StrategyConfig for CheatingStrategyConfig { impl GameStrategy for CheatingStrategy {
fn initialize(&self, player: Player, _: &GameStateView) -> Box<Strategy> { fn initialize(&self, player: Player, _: &GameStateView) -> Box<PlayerStrategy> {
Box::new(CheatingStrategy { Box::new(CheatingPlayerStrategy {
player_states_cheat: self.player_states_cheat.clone(), player_states_cheat: self.player_states_cheat.clone(),
me: player, me: player,
}) })
} }
} }
pub struct CheatingStrategy {
pub struct CheatingPlayerStrategy {
player_states_cheat: Rc<RefCell<HashMap<Player, Cards>>>, player_states_cheat: Rc<RefCell<HashMap<Player, Cards>>>,
me: Player, me: Player,
} }
impl CheatingStrategy { impl CheatingPlayerStrategy {
// help next player cheat! // help next player cheat!
fn inform_next_player_cards(&self, view: &GameStateView) { fn inform_next_player_cards(&self, view: &GameStateView) {
let next = view.board.player_to_left(&self.me); let next = view.board.player_to_left(&self.me);
@ -131,7 +144,7 @@ impl CheatingStrategy {
false false
} }
} }
impl Strategy for CheatingStrategy { impl PlayerStrategy for CheatingPlayerStrategy {
fn decide(&mut self, view: &GameStateView) -> TurnChoice { fn decide(&mut self, view: &GameStateView) -> TurnChoice {
self.inform_next_player_cards(view); self.inform_next_player_cards(view);
if view.board.turn <= view.board.num_players { if view.board.turn <= view.board.num_players {

View file

@ -2,44 +2,7 @@ use simulator::*;
use game::*; use game::*;
use rand::{self, Rng}; use rand::{self, Rng};
// dummy, terrible strategy // dummy, terrible strategy, as an example
#[allow(dead_code)]
#[derive(Clone)]
pub struct AlwaysPlayConfig;
impl StrategyConfig for AlwaysPlayConfig {
fn initialize(&self, _: Player, _: &GameStateView) -> Box<Strategy> {
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<Strategy> {
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
#[allow(dead_code)] #[allow(dead_code)]
#[derive(Clone)] #[derive(Clone)]
pub struct RandomStrategyConfig { pub struct RandomStrategyConfig {
@ -47,22 +10,36 @@ pub struct RandomStrategyConfig {
pub play_probability: f64, pub play_probability: f64,
} }
impl StrategyConfig for RandomStrategyConfig { impl GameStrategyConfig for RandomStrategyConfig {
fn initialize(&self, player: Player, _: &GameStateView) -> Box<Strategy> { fn initialize(&self, _: &GameOptions) -> Box<GameStrategy> {
Box::new(RandomStrategy { Box::new(RandomStrategy {
hint_probability: self.hint_probability, hint_probability: self.hint_probability,
play_probability: self.play_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<PlayerStrategy> {
Box::new(RandomStrategyPlayer {
hint_probability: self.hint_probability,
play_probability: self.play_probability,
me: player, me: player,
}) })
} }
} }
pub struct RandomStrategy {
pub struct RandomStrategyPlayer {
hint_probability: f64, hint_probability: f64,
play_probability: f64, play_probability: f64,
me: Player, me: Player,
} }
impl Strategy for RandomStrategy { impl PlayerStrategy for RandomStrategyPlayer {
fn decide(&mut self, view: &GameStateView) -> TurnChoice { fn decide(&mut self, view: &GameStateView) -> TurnChoice {
let p = rand::random::<f64>(); let p = rand::random::<f64>();
if p < self.hint_probability { if p < self.hint_probability {