finish up cheating strategy
This commit is contained in:
parent
2f6dc571c2
commit
02ffb781b3
57
src/game.rs
57
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
|
// 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> {
|
||||||
|
@ -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);
|
||||||
|
@ -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) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
Loading…
Reference in New Issue
Block a user