finish up cheating strategy

This commit is contained in:
Jeff Wu 2016-03-13 11:02:08 -07:00
parent 2f6dc571c2
commit 02ffb781b3
4 changed files with 124 additions and 62 deletions

View File

@ -377,6 +377,29 @@ impl BoardState {
} }
} }
// best possible value we can get for firework of that color,
// based on looking at discard + fireworks
pub fn highest_attainable(&self, color: &Color) -> Value {
let firework = self.fireworks.get(color).unwrap();
if firework.complete() {
return FINAL_VALUE;
}
let desired = firework.desired_value().unwrap();
for value in VALUES.iter() {
if *value < desired {
// already have these cards
continue
}
let needed_card = Card::new(color, value.clone());
if self.discard.has_all(&needed_card) {
// already discarded all of these
return value - 1;
}
}
return FINAL_VALUE;
}
// is never going to play, based on discard + fireworks // is never going to play, based on discard + fireworks
pub fn is_unplayable(&self, card: &Card) -> bool { pub fn is_unplayable(&self, card: &Card) -> bool {
let firework = self.fireworks.get(card.color).unwrap(); let firework = self.fireworks.get(card.color).unwrap();
@ -387,26 +410,28 @@ impl BoardState {
if card.value < desired { if card.value < desired {
true true
} else { } else {
let mut playable = true; card.value > self.highest_attainable(&card.color)
for value in VALUES.iter() { }
if *value < desired { }
// already have these cards }
continue
} else if *value > card.value { // cannot be discarded without sacrificing score, based on discard + fireworks
// don't care about these cards pub fn is_undiscardable(&self, card: &Card) -> bool {
break let firework = self.fireworks.get(card.color).unwrap();
if firework.complete() {
false
} else { } else {
// need these cards let desired = firework.desired_value().unwrap();
let needed_card = Card::new(card.color, value.clone()); if card.value < desired {
if self.discard.has_all(&needed_card) { false
// already discarded all of these } else {
playable = false; if card.value > self.highest_attainable(&card.color) {
false
} else {
self.discard.remaining(&card) == 1
} }
} }
} }
playable
}
}
} }
pub fn get_players(&self) -> Vec<Player> { pub fn get_players(&self) -> Vec<Player> {

View File

@ -4,11 +4,11 @@ use std::collections::HashMap;
// Trait to implement for any valid Hanabi strategy // Trait to implement for any valid Hanabi strategy
// State management is done by the simulator, to avoid cheating // State management is done by the simulator, to avoid cheating
pub trait Strategy { pub trait Strategy {
fn decide(&mut self, &Player, &GameStateView) -> TurnChoice; fn decide(&mut self, &GameStateView) -> TurnChoice;
fn update(&mut self, &Turn, &GameStateView); fn update(&mut self, &Turn, &GameStateView);
} }
pub trait StrategyConfig { pub trait StrategyConfig {
fn initialize(&self, &Player, &GameStateView) -> Box<Strategy>; fn initialize(&self, Player, &GameStateView) -> Box<Strategy>;
} }
pub fn simulate_once<'a>(opts: &GameOptions, strat_configs: &Vec<Box<StrategyConfig + 'a>>) -> Score { pub fn simulate_once<'a>(opts: &GameOptions, strat_configs: &Vec<Box<StrategyConfig + 'a>>) -> Score {
@ -21,7 +21,7 @@ pub fn simulate_once<'a>(opts: &GameOptions, strat_configs: &Vec<Box<StrategyCon
for player in game.get_players() { for player in game.get_players() {
strategies.insert( strategies.insert(
player, player,
(*strat_configs[i]).initialize(&player, &game.get_view(player)), (*strat_configs[i]).initialize(player.clone(), &game.get_view(player)),
); );
i += 1; i += 1;
} }
@ -31,7 +31,7 @@ pub fn simulate_once<'a>(opts: &GameOptions, strat_configs: &Vec<Box<StrategyCon
let player = game.board.player; let player = game.board.player;
let choice = { let choice = {
let mut strategy = strategies.get_mut(&player).unwrap(); let mut strategy = strategies.get_mut(&player).unwrap();
strategy.decide(&player, &game.get_view(player)) strategy.decide(&game.get_view(player))
}; };
game.process_choice(&choice); game.process_choice(&choice);

View File

@ -5,11 +5,11 @@ use std::collections::HashMap;
use simulator::*; use simulator::*;
use game::*; use game::*;
// strategy that cheats by using Cell // strategy that cheats by using Rc/RefCell
// Plays according to the following rules: // Plays according to the following rules:
// - if any card is playable, // - if any card is playable,
// play the card with the lowest // play the card with the lowest value
// - if a card is // - if a card is dead, discard it
#[allow(dead_code)] #[allow(dead_code)]
#[derive(Clone)] #[derive(Clone)]
pub struct CheatingStrategyConfig { pub struct CheatingStrategyConfig {
@ -24,35 +24,71 @@ impl CheatingStrategyConfig {
} }
} }
impl <'a> StrategyConfig for CheatingStrategyConfig { impl <'a> StrategyConfig for CheatingStrategyConfig {
fn initialize(&self, _: &Player, _: &GameStateView) -> Box<Strategy> { fn initialize(&self, player: Player, _: &GameStateView) -> Box<Strategy> {
Box::new(CheatingStrategy { Box::new(CheatingStrategy {
player_states_cheat: self.player_states_cheat.clone(), player_states_cheat: self.player_states_cheat.clone(),
me: player,
}) })
} }
} }
pub struct CheatingStrategy { pub struct CheatingStrategy {
player_states_cheat: Rc<RefCell<HashMap<Player, Cards>>>, player_states_cheat: Rc<RefCell<HashMap<Player, Cards>>>,
me: Player,
} }
impl Strategy for CheatingStrategy { impl CheatingStrategy {
fn decide(&mut self, me: &Player, view: &GameStateView) -> TurnChoice { // help next player cheat!
let next = view.board.player_to_left(&me); fn inform_next_player_cards(&self, view: &GameStateView) {
let next = view.board.player_to_left(&self.me);
self.player_states_cheat.borrow_mut().insert( self.player_states_cheat.borrow_mut().insert(
next, view.other_player_states.get(&next).unwrap().hand.clone() next, view.other_player_states.get(&next).unwrap().hand.clone()
); );
if view.board.turn == 1 { }
// give a throwaway hint - we only do this when we have nothing to do
fn throwaway_hint(&self, view: &GameStateView) -> TurnChoice {
TurnChoice::Hint(Hint { TurnChoice::Hint(Hint {
player: next, player: view.board.player_to_left(&self.me),
hinted: Hinted::Value(1) hinted: Hinted::Value(1)
}) })
} else { }
}
impl Strategy for CheatingStrategy {
fn decide(&mut self, view: &GameStateView) -> TurnChoice {
self.inform_next_player_cards(view);
if view.board.turn == 1 {
// don't know my cards yet, just give a random hint
return self.throwaway_hint(view);
}
let states = self.player_states_cheat.borrow(); let states = self.player_states_cheat.borrow();
let my_cards = states.get(me).unwrap(); let my_cards = states.get(&self.me).unwrap();
let mut playable_cards = my_cards.iter().filter(|card| { let mut playable_cards = my_cards.iter().filter(|card| {
view.board.is_playable(card) view.board.is_playable(card)
}).peekable(); }).peekable();
if playable_cards.peek() == None { if playable_cards.peek() == None {
for card in my_cards {
if view.board.is_unplayable(card) {
let index = my_cards.iter().position(|iter_card| {
card == iter_card
}).unwrap();
return TurnChoice::Discard(index);
}
}
for card in my_cards {
if !view.board.is_undiscardable(card) {
let index = my_cards.iter().position(|iter_card| {
card == iter_card
}).unwrap();
return TurnChoice::Discard(index);
}
}
// all my cards are undiscardable!
if view.board.hints_remaining > 0 {
return self.throwaway_hint(view);
}
TurnChoice::Discard(0) TurnChoice::Discard(0)
} else { } else {
// play the lowest playable card
let mut play_card = playable_cards.next().unwrap(); let mut play_card = playable_cards.next().unwrap();
let mut next_card_opt = playable_cards.next(); let mut next_card_opt = playable_cards.next();
@ -69,7 +105,6 @@ impl Strategy for CheatingStrategy {
TurnChoice::Play(index) TurnChoice::Play(index)
} }
} }
}
fn update(&mut self, _: &Turn, _: &GameStateView) { fn update(&mut self, _: &Turn, _: &GameStateView) {
} }
} }

View File

@ -7,13 +7,13 @@ use rand::{self, Rng};
#[derive(Clone)] #[derive(Clone)]
pub struct AlwaysPlayConfig; pub struct AlwaysPlayConfig;
impl StrategyConfig for AlwaysPlayConfig { impl StrategyConfig for AlwaysPlayConfig {
fn initialize(&self, _: &Player, _: &GameStateView) -> Box<Strategy> { fn initialize(&self, _: Player, _: &GameStateView) -> Box<Strategy> {
Box::new(AlwaysPlay) Box::new(AlwaysPlay)
} }
} }
pub struct AlwaysPlay; pub struct AlwaysPlay;
impl Strategy for AlwaysPlay { impl Strategy for AlwaysPlay {
fn decide(&mut self, _: &Player, _: &GameStateView) -> TurnChoice { fn decide(&mut self, _: &GameStateView) -> TurnChoice {
TurnChoice::Play(0) TurnChoice::Play(0)
} }
fn update(&mut self, _: &Turn, _: &GameStateView) { fn update(&mut self, _: &Turn, _: &GameStateView) {
@ -25,13 +25,13 @@ impl Strategy for AlwaysPlay {
#[derive(Clone)] #[derive(Clone)]
pub struct AlwaysDiscardConfig; pub struct AlwaysDiscardConfig;
impl StrategyConfig for AlwaysDiscardConfig { impl StrategyConfig for AlwaysDiscardConfig {
fn initialize(&self, _: &Player, _: &GameStateView) -> Box<Strategy> { fn initialize(&self, _: Player, _: &GameStateView) -> Box<Strategy> {
Box::new(AlwaysDiscard) Box::new(AlwaysDiscard)
} }
} }
pub struct AlwaysDiscard; pub struct AlwaysDiscard;
impl Strategy for AlwaysDiscard { impl Strategy for AlwaysDiscard {
fn decide(&mut self, _: &Player, _: &GameStateView) -> TurnChoice { fn decide(&mut self, _: &GameStateView) -> TurnChoice {
TurnChoice::Discard(0) TurnChoice::Discard(0)
} }
fn update(&mut self, _: &Turn, _: &GameStateView) { fn update(&mut self, _: &Turn, _: &GameStateView) {
@ -48,20 +48,22 @@ pub struct RandomStrategyConfig {
} }
impl StrategyConfig for RandomStrategyConfig { impl StrategyConfig for RandomStrategyConfig {
fn initialize(&self, _: &Player, _: &GameStateView) -> Box<Strategy> { fn initialize(&self, player: Player, _: &GameStateView) -> Box<Strategy> {
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,
me: player,
}) })
} }
} }
pub struct RandomStrategy { pub struct RandomStrategy {
pub hint_probability: f64, hint_probability: f64,
pub play_probability: f64, play_probability: f64,
me: Player,
} }
impl Strategy for RandomStrategy { impl Strategy for RandomStrategy {
fn decide(&mut self, me: &Player, 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 {
if view.board.hints_remaining > 0 { if view.board.hints_remaining > 0 {
@ -74,7 +76,7 @@ impl Strategy for RandomStrategy {
} }
}; };
TurnChoice::Hint(Hint { TurnChoice::Hint(Hint {
player: view.board.player_to_left(&me), player: view.board.player_to_left(&self.me),
hinted: hinted, hinted: hinted,
}) })
} else { } else {