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
pub fn is_unplayable(&self, card: &Card) -> bool {
let firework = self.fireworks.get(card.color).unwrap();
@ -387,24 +410,26 @@ impl BoardState {
if card.value < desired {
true
} else {
let mut playable = true;
for value in VALUES.iter() {
if *value < desired {
// already have these cards
continue
} else if *value > card.value {
// don't care about these cards
break
} else {
// need these cards
let needed_card = Card::new(card.color, value.clone());
if self.discard.has_all(&needed_card) {
// already discarded all of these
playable = false;
}
}
card.value > self.highest_attainable(&card.color)
}
}
}
// cannot be discarded without sacrificing score, based on discard + fireworks
pub fn is_undiscardable(&self, card: &Card) -> bool {
let firework = self.fireworks.get(card.color).unwrap();
if firework.complete() {
false
} else {
let desired = firework.desired_value().unwrap();
if card.value < desired {
false
} else {
if card.value > self.highest_attainable(&card.color) {
false
} else {
self.discard.remaining(&card) == 1
}
playable
}
}
}

View file

@ -4,11 +4,11 @@ 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 {
fn decide(&mut self, &Player, &GameStateView) -> TurnChoice;
fn decide(&mut self, &GameStateView) -> TurnChoice;
fn update(&mut self, &Turn, &GameStateView);
}
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 {
@ -21,7 +21,7 @@ pub fn simulate_once<'a>(opts: &GameOptions, strat_configs: &Vec<Box<StrategyCon
for player in game.get_players() {
strategies.insert(
player,
(*strat_configs[i]).initialize(&player, &game.get_view(player)),
(*strat_configs[i]).initialize(player.clone(), &game.get_view(player)),
);
i += 1;
}
@ -31,7 +31,7 @@ pub fn simulate_once<'a>(opts: &GameOptions, strat_configs: &Vec<Box<StrategyCon
let player = game.board.player;
let choice = {
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);

View file

@ -5,11 +5,11 @@ use std::collections::HashMap;
use simulator::*;
use game::*;
// strategy that cheats by using Cell
// strategy that cheats by using Rc/RefCell
// Plays according to the following rules:
// - if any card is playable,
// play the card with the lowest
// - if a card is
// play the card with the lowest value
// - if a card is dead, discard it
#[allow(dead_code)]
#[derive(Clone)]
pub struct CheatingStrategyConfig {
@ -24,50 +24,85 @@ impl CheatingStrategyConfig {
}
}
impl <'a> StrategyConfig for CheatingStrategyConfig {
fn initialize(&self, _: &Player, _: &GameStateView) -> Box<Strategy> {
fn initialize(&self, player: Player, _: &GameStateView) -> Box<Strategy> {
Box::new(CheatingStrategy {
player_states_cheat: self.player_states_cheat.clone(),
me: player,
})
}
}
pub struct CheatingStrategy {
player_states_cheat: Rc<RefCell<HashMap<Player, Cards>>>,
me: Player,
}
impl Strategy for CheatingStrategy {
fn decide(&mut self, me: &Player, view: &GameStateView) -> TurnChoice {
let next = view.board.player_to_left(&me);
impl CheatingStrategy {
// help next player cheat!
fn inform_next_player_cards(&self, view: &GameStateView) {
let next = view.board.player_to_left(&self.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,
}
// give a throwaway hint - we only do this when we have nothing to do
fn throwaway_hint(&self, view: &GameStateView) -> TurnChoice {
TurnChoice::Hint(Hint {
player: view.board.player_to_left(&self.me),
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();
})
}
}
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 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 states = self.player_states_cheat.borrow();
let my_cards = states.get(&self.me).unwrap();
let mut playable_cards = my_cards.iter().filter(|card| {
view.board.is_playable(card)
}).peekable();
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);
}
let index = my_cards.iter().position(|card| {
card == play_card
}).unwrap();
TurnChoice::Play(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)
} else {
// play the lowest playable card
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) {

View file

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