another layer of indirection, prep for threading
This commit is contained in:
parent
44248c8d3e
commit
706f5b52b6
4 changed files with 71 additions and 113 deletions
22
src/main.rs
22
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);
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in a new issue