strategy that cheats, first pass
This commit is contained in:
parent
a16f490c04
commit
2f6dc571c2
4 changed files with 102 additions and 20 deletions
26
src/game.rs
26
src/game.rs
|
@ -30,7 +30,7 @@ pub fn get_count_for_value(value: &Value) -> usize {
|
||||||
|
|
||||||
pub type Player = u32;
|
pub type Player = u32;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug,Clone,PartialEq)]
|
||||||
pub struct Card {
|
pub struct Card {
|
||||||
pub color: Color,
|
pub color: Color,
|
||||||
pub value: Value,
|
pub value: Value,
|
||||||
|
@ -227,9 +227,9 @@ pub struct GameOptions {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct PlayerState {
|
pub struct PlayerState {
|
||||||
// the player's actual hand
|
// the player's actual hand
|
||||||
hand: Cards,
|
pub hand: Cards,
|
||||||
// represents what is common knowledge about the player's hand
|
// represents what is common knowledge about the player's hand
|
||||||
info: CardsInfo,
|
pub info: CardsInfo,
|
||||||
}
|
}
|
||||||
impl fmt::Display for PlayerState {
|
impl fmt::Display for PlayerState {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
@ -305,7 +305,7 @@ fn new_deck() -> Cards {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
shuffle(&mut deck);
|
shuffle(&mut deck);
|
||||||
debug!("Created deck: {:?}", deck);
|
trace!("Created deck: {:?}", deck);
|
||||||
deck
|
deck
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -542,7 +542,7 @@ impl GameState {
|
||||||
let mut other_player_states = HashMap::new();
|
let mut other_player_states = HashMap::new();
|
||||||
for (other_player, state) in &self.player_states {
|
for (other_player, state) in &self.player_states {
|
||||||
if player != *other_player {
|
if player != *other_player {
|
||||||
other_player_states.insert(player, state);
|
other_player_states.insert(*other_player, state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
GameStateView {
|
GameStateView {
|
||||||
|
@ -558,20 +558,20 @@ impl GameState {
|
||||||
let ref mut state = self.player_states.get_mut(&self.board.player).unwrap();
|
let ref mut state = self.player_states.get_mut(&self.board.player).unwrap();
|
||||||
let (card, _) = state.take(index);
|
let (card, _) = state.take(index);
|
||||||
if let Some(new_card) = self.board.deck.pop() {
|
if let Some(new_card) = self.board.deck.pop() {
|
||||||
info!("Drew new card, {}", new_card);
|
debug!("Drew new card, {}", new_card);
|
||||||
state.place(new_card);
|
state.place(new_card);
|
||||||
}
|
}
|
||||||
card
|
card
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn process_choice(&mut self, choice: &TurnChoice) {
|
pub fn process_choice(&mut self, choice: &TurnChoice) {
|
||||||
info!("Player {}'s move", self.board.player);
|
debug!("Player {}'s move", self.board.player);
|
||||||
match choice {
|
match choice {
|
||||||
&TurnChoice::Hint(ref hint) => {
|
&TurnChoice::Hint(ref hint) => {
|
||||||
assert!(self.board.hints_remaining > 0,
|
assert!(self.board.hints_remaining > 0,
|
||||||
"Tried to hint with no hints remaining");
|
"Tried to hint with no hints remaining");
|
||||||
self.board.hints_remaining -= 1;
|
self.board.hints_remaining -= 1;
|
||||||
info!("Hint to player {}, about {}", hint.player, hint.hinted);
|
debug!("Hint to player {}, about {}", hint.player, hint.hinted);
|
||||||
|
|
||||||
assert!(self.board.player != hint.player,
|
assert!(self.board.player != hint.player,
|
||||||
format!("Player {} gave a hint to himself", hint.player));
|
format!("Player {} gave a hint to himself", hint.player));
|
||||||
|
@ -581,7 +581,7 @@ impl GameState {
|
||||||
}
|
}
|
||||||
&TurnChoice::Discard(index) => {
|
&TurnChoice::Discard(index) => {
|
||||||
let card = self.take_from_hand(index);
|
let card = self.take_from_hand(index);
|
||||||
info!("Discard card in position {}, which is {}", index, card);
|
debug!("Discard card in position {}, which is {}", index, card);
|
||||||
self.board.discard.place(card);
|
self.board.discard.place(card);
|
||||||
|
|
||||||
self.board.try_add_hint();
|
self.board.try_add_hint();
|
||||||
|
@ -589,7 +589,7 @@ impl GameState {
|
||||||
&TurnChoice::Play(index) => {
|
&TurnChoice::Play(index) => {
|
||||||
let card = self.take_from_hand(index);
|
let card = self.take_from_hand(index);
|
||||||
|
|
||||||
info!(
|
debug!(
|
||||||
"Playing card at position {}, which is {}",
|
"Playing card at position {}, which is {}",
|
||||||
index, card
|
index, card
|
||||||
);
|
);
|
||||||
|
@ -599,15 +599,15 @@ impl GameState {
|
||||||
if self.board.is_playable(&card) {
|
if self.board.is_playable(&card) {
|
||||||
let ref mut firework = self.board.fireworks.get_mut(&card.color).unwrap();
|
let ref mut firework = self.board.fireworks.get_mut(&card.color).unwrap();
|
||||||
firework_made = card.value == FINAL_VALUE;
|
firework_made = card.value == FINAL_VALUE;
|
||||||
info!("Successfully played {}!", card);
|
debug!("Successfully played {}!", card);
|
||||||
if firework_made {
|
if firework_made {
|
||||||
info!("Firework complete for {}!", card.color);
|
debug!("Firework complete for {}!", card.color);
|
||||||
}
|
}
|
||||||
firework.place(card);
|
firework.place(card);
|
||||||
} else {
|
} else {
|
||||||
self.board.discard.place(card);
|
self.board.discard.place(card);
|
||||||
self.board.lives_remaining -= 1;
|
self.board.lives_remaining -= 1;
|
||||||
info!(
|
debug!(
|
||||||
"Removing a life! Lives remaining: {}",
|
"Removing a life! Lives remaining: {}",
|
||||||
self.board.lives_remaining
|
self.board.lives_remaining
|
||||||
);
|
);
|
||||||
|
|
19
src/main.rs
19
src/main.rs
|
@ -6,6 +6,7 @@ mod game;
|
||||||
mod simulator;
|
mod simulator;
|
||||||
mod strategies {
|
mod strategies {
|
||||||
pub mod examples;
|
pub mod examples;
|
||||||
|
pub mod cheating;
|
||||||
}
|
}
|
||||||
mod info;
|
mod info;
|
||||||
|
|
||||||
|
@ -27,7 +28,8 @@ impl log::Log for SimpleLogger {
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
log::set_logger(|max_log_level| {
|
log::set_logger(|max_log_level| {
|
||||||
max_log_level.set(log::LogLevelFilter::Trace);
|
//max_log_level.set(log::LogLevelFilter::Trace);
|
||||||
|
max_log_level.set(log::LogLevelFilter::Info);
|
||||||
Box::new(SimpleLogger)
|
Box::new(SimpleLogger)
|
||||||
}).unwrap();
|
}).unwrap();
|
||||||
|
|
||||||
|
@ -37,7 +39,7 @@ fn main() {
|
||||||
num_hints: 8,
|
num_hints: 8,
|
||||||
num_lives: 3,
|
num_lives: 3,
|
||||||
};
|
};
|
||||||
let n = 1;
|
let n = 1000;
|
||||||
// simulator::simulate(&opts, &strategies::examples::AlwaysDiscard, n);
|
// simulator::simulate(&opts, &strategies::examples::AlwaysDiscard, n);
|
||||||
// simulator::simulate_symmetric(&opts, strategies::examples::AlwaysPlayConfig, n);
|
// simulator::simulate_symmetric(&opts, strategies::examples::AlwaysPlayConfig, n);
|
||||||
// simulator::simulate(
|
// simulator::simulate(
|
||||||
|
@ -49,12 +51,17 @@ fn main() {
|
||||||
// Box::new(strategies::examples::AlwaysPlayConfig),
|
// Box::new(strategies::examples::AlwaysPlayConfig),
|
||||||
// ],
|
// ],
|
||||||
// n);
|
// n);
|
||||||
|
// simulator::simulate_symmetric(
|
||||||
|
// &opts,
|
||||||
|
// strategies::examples::RandomStrategyConfig {
|
||||||
|
// hint_probability: 0.4,
|
||||||
|
// play_probability: 0.2,
|
||||||
|
// },
|
||||||
|
// n
|
||||||
|
// );
|
||||||
simulator::simulate_symmetric(
|
simulator::simulate_symmetric(
|
||||||
&opts,
|
&opts,
|
||||||
strategies::examples::RandomStrategyConfig {
|
strategies::cheating::CheatingStrategyConfig::new(),
|
||||||
hint_probability: 0.4,
|
|
||||||
play_probability: 0.2,
|
|
||||||
},
|
|
||||||
n
|
n
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,7 +56,7 @@ pub fn simulate<'a>(opts: &GameOptions, strat_configs: &Vec<Box<StrategyConfig +
|
||||||
let mut total_score = 0;
|
let mut total_score = 0;
|
||||||
for _ in 0..n_trials {
|
for _ in 0..n_trials {
|
||||||
let score = simulate_once(&opts, strat_configs);
|
let score = simulate_once(&opts, strat_configs);
|
||||||
info!("Scored: {:?}", score);
|
debug!("Scored: {:?}", score);
|
||||||
total_score += score;
|
total_score += score;
|
||||||
}
|
}
|
||||||
let average: f32 = (total_score as f32) / (n_trials as f32);
|
let average: f32 = (total_score as f32) / (n_trials as f32);
|
||||||
|
|
75
src/strategies/cheating.rs
Normal file
75
src/strategies/cheating.rs
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
use std::rc::Rc;
|
||||||
|
use std::cell::{RefCell};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use simulator::*;
|
||||||
|
use game::*;
|
||||||
|
|
||||||
|
// strategy that cheats by using Cell
|
||||||
|
// Plays according to the following rules:
|
||||||
|
// - if any card is playable,
|
||||||
|
// play the card with the lowest
|
||||||
|
// - if a card is
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct CheatingStrategyConfig {
|
||||||
|
player_states_cheat: Rc<RefCell<HashMap<Player, Cards>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CheatingStrategyConfig {
|
||||||
|
pub fn new() -> CheatingStrategyConfig {
|
||||||
|
CheatingStrategyConfig {
|
||||||
|
player_states_cheat: Rc::new(RefCell::new(HashMap::new())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl <'a> StrategyConfig for CheatingStrategyConfig {
|
||||||
|
fn initialize(&self, _: &Player, _: &GameStateView) -> Box<Strategy> {
|
||||||
|
Box::new(CheatingStrategy {
|
||||||
|
player_states_cheat: self.player_states_cheat.clone(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub struct CheatingStrategy {
|
||||||
|
player_states_cheat: Rc<RefCell<HashMap<Player, Cards>>>,
|
||||||
|
}
|
||||||
|
impl Strategy for CheatingStrategy {
|
||||||
|
fn decide(&mut self, me: &Player, view: &GameStateView) -> TurnChoice {
|
||||||
|
let next = view.board.player_to_left(&me);
|
||||||
|
self.player_states_cheat.borrow_mut().insert(
|
||||||
|
next, view.other_player_states.get(&next).unwrap().hand.clone()
|
||||||
|
);
|
||||||
|
if view.board.turn == 1 {
|
||||||
|
TurnChoice::Hint(Hint {
|
||||||
|
player: next,
|
||||||
|
hinted: Hinted::Value(1)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
let states = self.player_states_cheat.borrow();
|
||||||
|
let my_cards = states.get(me).unwrap();
|
||||||
|
let mut playable_cards = my_cards.iter().filter(|card| {
|
||||||
|
view.board.is_playable(card)
|
||||||
|
}).peekable();
|
||||||
|
if playable_cards.peek() == None {
|
||||||
|
TurnChoice::Discard(0)
|
||||||
|
} else {
|
||||||
|
let mut play_card = playable_cards.next().unwrap();
|
||||||
|
|
||||||
|
let mut next_card_opt = playable_cards.next();
|
||||||
|
while let Some(next_card) = next_card_opt {
|
||||||
|
if next_card.value < play_card.value {
|
||||||
|
play_card = next_card;
|
||||||
|
}
|
||||||
|
next_card_opt = playable_cards.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
let index = my_cards.iter().position(|card| {
|
||||||
|
card == play_card
|
||||||
|
}).unwrap();
|
||||||
|
TurnChoice::Play(index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn update(&mut self, _: &Turn, _: &GameStateView) {
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue