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
// 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);
}

View file

@ -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<Strategy>;
// 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<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,
strat_configs: &Vec<Box<StrategyConfig + 'a>>,
game_strategy: Box<GameStrategy>,
seed_opt: Option<u32>,
) -> 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<Player, Box<Strategy>> = HashMap::new();
let mut i = 0;
let mut strategies : HashMap<Player, Box<PlayerStrategy>> = 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<Box<StrategyConfig + 'a>>,
strat_config: &GameStrategyConfig,
first_seed_opt: Option<u32>,
n_trials: u32,
first_seed_opt: Option<u32>
) -> 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::<Score, usize>::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<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
#[allow(dead_code)]
#[derive(Clone)]
pub struct CheatingStrategyConfig {
player_states_cheat: Rc<RefCell<HashMap<Player, Cards>>>,
}
pub struct CheatingStrategyConfig;
impl 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())),
}
}
}
impl <'a> StrategyConfig for CheatingStrategyConfig {
fn initialize(&self, player: Player, _: &GameStateView) -> Box<Strategy> {
Box::new(CheatingStrategy {
impl GameStrategy for CheatingStrategy {
fn initialize(&self, player: Player, _: &GameStateView) -> Box<PlayerStrategy> {
Box::new(CheatingPlayerStrategy {
player_states_cheat: self.player_states_cheat.clone(),
me: player,
})
}
}
pub struct CheatingStrategy {
pub struct CheatingPlayerStrategy {
player_states_cheat: Rc<RefCell<HashMap<Player, Cards>>>,
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 {

View file

@ -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<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
// 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<Strategy> {
impl GameStrategyConfig for RandomStrategyConfig {
fn initialize(&self, _: &GameOptions) -> Box<GameStrategy> {
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<PlayerStrategy> {
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::<f64>();
if p < self.hint_probability {