finish up cheating strategy
This commit is contained in:
parent
2f6dc571c2
commit
02ffb781b3
4 changed files with 124 additions and 62 deletions
59
src/game.rs
59
src/game.rs
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in a new issue