improvements to cheating strategy
This commit is contained in:
parent
0b9734c20b
commit
44248c8d3e
3 changed files with 56 additions and 22 deletions
11
src/game.rs
11
src/game.rs
|
@ -30,7 +30,7 @@ pub fn get_count_for_value(value: &Value) -> usize {
|
|||
|
||||
pub type Player = u32;
|
||||
|
||||
#[derive(Debug,Clone,PartialEq)]
|
||||
#[derive(Debug,Clone,PartialEq,Eq,Hash)]
|
||||
pub struct Card {
|
||||
pub color: Color,
|
||||
pub value: Value,
|
||||
|
@ -520,10 +520,13 @@ pub struct GameStateView<'a> {
|
|||
pub board: &'a BoardState,
|
||||
}
|
||||
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!");
|
||||
let state = self.other_player_states.get(player).unwrap();
|
||||
for other_card in &state.hand {
|
||||
&self.other_player_states.get(player).unwrap().hand
|
||||
}
|
||||
|
||||
pub fn has_card(&self, player: &Player, card: &Card) -> bool {
|
||||
for other_card in self.get_hand(player) {
|
||||
if *card == *other_card {
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -59,9 +59,12 @@ pub fn simulate_once<'a>(
|
|||
// TODO: do some stuff
|
||||
debug!("State:\n{}", game);
|
||||
}
|
||||
game.score()
|
||||
let score = game.score();
|
||||
debug!("SCORED: {:?}", score);
|
||||
score
|
||||
}
|
||||
|
||||
// TODO: multithreaded
|
||||
pub fn simulate<'a>(
|
||||
opts: &GameOptions,
|
||||
strat_configs: &Vec<Box<StrategyConfig + 'a>>,
|
||||
|
@ -73,6 +76,7 @@ pub fn simulate<'a>(
|
|||
let mut non_perfect_seeds = Vec::new();
|
||||
|
||||
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();
|
||||
for i in 0..n_trials {
|
||||
if (i > 0) && (i % 1000 == 0) {
|
||||
|
@ -81,7 +85,6 @@ pub fn simulate<'a>(
|
|||
}
|
||||
let seed = first_seed + i;
|
||||
let score = simulate_once(&opts, strat_configs, Some(seed));
|
||||
debug!("Scored: {:?}", score);
|
||||
let count = histogram.get(&score).unwrap_or(&0) + 1;
|
||||
histogram.insert(score, count);
|
||||
if score != 25 {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use std::rc::Rc;
|
||||
use std::cell::{RefCell};
|
||||
use std::collections::HashMap;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
use simulator::*;
|
||||
use game::*;
|
||||
|
@ -101,6 +101,35 @@ impl CheatingStrategy {
|
|||
// maybe value 5s more?
|
||||
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 {
|
||||
fn decide(&mut self, view: &GameStateView) -> TurnChoice {
|
||||
|
@ -120,24 +149,23 @@ impl Strategy for CheatingStrategy {
|
|||
// if view.board.deck_size() > 10 {
|
||||
if view.board.discard.cards.len() < 5 {
|
||||
// if anything is totally useless, discard it
|
||||
for (i, card) in my_cards.iter().enumerate() {
|
||||
if view.board.is_dead(card) {
|
||||
return TurnChoice::Discard(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// it's unintuitive that hinting is better than
|
||||
// discarding dead cards, but turns out true
|
||||
if view.board.hints_remaining > 1 {
|
||||
return self.throwaway_hint(view);
|
||||
}
|
||||
// if anything is totally useless, discard it
|
||||
for (i, card) in my_cards.iter().enumerate() {
|
||||
if view.board.is_dead(card) {
|
||||
if let Some(i) = self.find_useless_card(view, my_cards) {
|
||||
return TurnChoice::Discard(i);
|
||||
}
|
||||
}
|
||||
|
||||
// hinting is better than discarding dead cards
|
||||
// (probably because it stalls the deck-drawing).
|
||||
if view.board.hints_remaining > 1 {
|
||||
if self.someone_else_can_play(view) {
|
||||
return self.throwaway_hint(view);
|
||||
}
|
||||
}
|
||||
// if anything is totally useless, discard it
|
||||
if let Some(i) = self.find_useless_card(view, my_cards) {
|
||||
return TurnChoice::Discard(i);
|
||||
}
|
||||
|
||||
// All cards are plausibly useful.
|
||||
// Play the best discardable card, according to the ordering induced by comparing
|
||||
// (is in another hand, is dispensable, value)
|
||||
|
|
Loading…
Reference in a new issue