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
|
// 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(),
|
|
||||||
// );
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
|
||||||
}
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
Loading…
Reference in a new issue