improvements to cheating strategy

This commit is contained in:
Jeff Wu 2016-03-13 23:05:01 -07:00
parent 0b9734c20b
commit 44248c8d3e
3 changed files with 56 additions and 22 deletions

View File

@ -30,7 +30,7 @@ pub fn get_count_for_value(value: &Value) -> usize {
pub type Player = u32; pub type Player = u32;
#[derive(Debug,Clone,PartialEq)] #[derive(Debug,Clone,PartialEq,Eq,Hash)]
pub struct Card { pub struct Card {
pub color: Color, pub color: Color,
pub value: Value, pub value: Value,
@ -520,10 +520,13 @@ pub struct GameStateView<'a> {
pub board: &'a BoardState, pub board: &'a BoardState,
} }
impl <'a> GameStateView<'a> { impl <'a> GameStateView<'a> {
pub fn has_card(&self, player: &Player, card: &Card) -> bool { pub fn get_hand(&self, player: &Player) -> &Cards {
assert!(self.player != *player, "Cannot query about your own cards!"); assert!(self.player != *player, "Cannot query about your own cards!");
let state = self.other_player_states.get(player).unwrap(); &self.other_player_states.get(player).unwrap().hand
for other_card in &state.hand { }
pub fn has_card(&self, player: &Player, card: &Card) -> bool {
for other_card in self.get_hand(player) {
if *card == *other_card { if *card == *other_card {
return true; return true;
} }

View File

@ -59,9 +59,12 @@ pub fn simulate_once<'a>(
// TODO: do some stuff // TODO: do some stuff
debug!("State:\n{}", game); debug!("State:\n{}", game);
} }
game.score() let score = game.score();
debug!("SCORED: {:?}", score);
score
} }
// TODO: multithreaded
pub fn simulate<'a>( pub fn simulate<'a>(
opts: &GameOptions, opts: &GameOptions,
strat_configs: &Vec<Box<StrategyConfig + 'a>>, strat_configs: &Vec<Box<StrategyConfig + 'a>>,
@ -73,6 +76,7 @@ pub fn simulate<'a>(
let mut non_perfect_seeds = Vec::new(); let mut non_perfect_seeds = Vec::new();
let first_seed = first_seed_opt.unwrap_or(rand::thread_rng().next_u32()); let first_seed = first_seed_opt.unwrap_or(rand::thread_rng().next_u32());
info!("Initial seed: {}\n", first_seed);
let mut histogram = HashMap::<Score, usize>::new(); let mut histogram = HashMap::<Score, usize>::new();
for i in 0..n_trials { for i in 0..n_trials {
if (i > 0) && (i % 1000 == 0) { if (i > 0) && (i % 1000 == 0) {
@ -81,7 +85,6 @@ pub fn simulate<'a>(
} }
let seed = first_seed + i; let seed = first_seed + i;
let score = simulate_once(&opts, strat_configs, Some(seed)); let score = simulate_once(&opts, strat_configs, Some(seed));
debug!("Scored: {:?}", score);
let count = histogram.get(&score).unwrap_or(&0) + 1; let count = histogram.get(&score).unwrap_or(&0) + 1;
histogram.insert(score, count); histogram.insert(score, count);
if score != 25 { if score != 25 {

View File

@ -1,6 +1,6 @@
use std::rc::Rc; use std::rc::Rc;
use std::cell::{RefCell}; use std::cell::{RefCell};
use std::collections::HashMap; use std::collections::{HashMap, HashSet};
use simulator::*; use simulator::*;
use game::*; use game::*;
@ -101,6 +101,35 @@ impl CheatingStrategy {
// maybe value 5s more? // maybe value 5s more?
5 + (5 - (card.value as i32)) 5 + (5 - (card.value as i32))
} }
fn find_useless_card(&self, view: &GameStateView, hand: &Cards) -> Option<usize> {
let mut set: HashSet<Card> = HashSet::new();
for (i, card) in hand.iter().enumerate() {
if view.board.is_dead(card) {
return Some(i);
}
if set.contains(card) {
// found a duplicate card
return Some(i);
}
set.insert(card.clone());
}
return None
}
fn someone_else_can_play(&self, view: &GameStateView) -> bool {
for player in view.board.get_players() {
if player != self.me {
for card in view.get_hand(&player) {
if view.board.is_playable(card) {
return true;
}
}
}
}
false
}
} }
impl Strategy for CheatingStrategy { impl Strategy for CheatingStrategy {
fn decide(&mut self, view: &GameStateView) -> TurnChoice { fn decide(&mut self, view: &GameStateView) -> TurnChoice {
@ -120,24 +149,23 @@ impl Strategy for CheatingStrategy {
// if view.board.deck_size() > 10 { // if view.board.deck_size() > 10 {
if view.board.discard.cards.len() < 5 { if view.board.discard.cards.len() < 5 {
// if anything is totally useless, discard it // if anything is totally useless, discard it
for (i, card) in my_cards.iter().enumerate() { if let Some(i) = self.find_useless_card(view, my_cards) {
if view.board.is_dead(card) {
return TurnChoice::Discard(i); return TurnChoice::Discard(i);
} }
} }
}
// it's unintuitive that hinting is better than // hinting is better than discarding dead cards
// discarding dead cards, but turns out true // (probably because it stalls the deck-drawing).
if view.board.hints_remaining > 1 { if view.board.hints_remaining > 1 {
if self.someone_else_can_play(view) {
return self.throwaway_hint(view); return self.throwaway_hint(view);
} }
}
// if anything is totally useless, discard it // if anything is totally useless, discard it
for (i, card) in my_cards.iter().enumerate() { if let Some(i) = self.find_useless_card(view, my_cards) {
if view.board.is_dead(card) {
return TurnChoice::Discard(i); return TurnChoice::Discard(i);
} }
}
// All cards are plausibly useful. // All cards are plausibly useful.
// Play the best discardable card, according to the ordering induced by comparing // Play the best discardable card, according to the ordering induced by comparing
// (is in another hand, is dispensable, value) // (is in another hand, is dispensable, value)