beginnings of information strategy
This commit is contained in:
parent
f09dd58cda
commit
efba24d6e8
6 changed files with 451 additions and 44 deletions
|
@ -65,7 +65,7 @@ impl CardCounts {
|
||||||
get_count_for_value(&card.value) - count
|
get_count_for_value(&card.value) - count
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add(&mut self, card: &Card) {
|
pub fn increment(&mut self, card: &Card) {
|
||||||
let count = self.counts.get_mut(card).unwrap();
|
let count = self.counts.get_mut(card).unwrap();
|
||||||
*count += 1;
|
*count += 1;
|
||||||
}
|
}
|
||||||
|
@ -118,7 +118,7 @@ impl Discard {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn place(&mut self, card: Card) {
|
pub fn place(&mut self, card: Card) {
|
||||||
self.counts.add(&card);
|
self.counts.increment(&card);
|
||||||
self.cards.push(card);
|
self.cards.push(card);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
30
src/game.rs
30
src/game.rs
|
@ -33,8 +33,8 @@ pub struct Hint {
|
||||||
#[derive(Debug,Clone)]
|
#[derive(Debug,Clone)]
|
||||||
pub enum TurnChoice {
|
pub enum TurnChoice {
|
||||||
Hint(Hint),
|
Hint(Hint),
|
||||||
Discard(usize),
|
Discard(usize), // index of card to discard
|
||||||
Play(usize),
|
Play(usize), // index of card to play
|
||||||
}
|
}
|
||||||
|
|
||||||
// represents what happened in a turn
|
// represents what happened in a turn
|
||||||
|
@ -292,33 +292,19 @@ impl BoardState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn probability_of_predicate<T>(
|
|
||||||
&self,
|
|
||||||
card_info: &T,
|
|
||||||
predicate: &Fn(&Self, &Card) -> bool
|
|
||||||
) -> f32 where T: CardInfo {
|
|
||||||
let mut total_weight = 0;
|
|
||||||
let mut playable_weight = 0;
|
|
||||||
for card in card_info.get_possibilities() {
|
|
||||||
let weight = card_info.get_weight(&card);
|
|
||||||
if predicate(&self, &card) {
|
|
||||||
playable_weight += weight;
|
|
||||||
}
|
|
||||||
total_weight += weight;
|
|
||||||
}
|
|
||||||
(playable_weight as f32) / (total_weight as f32)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn probability_is_playable<T>(&self, card_info: &T) -> f32 where T: CardInfo {
|
pub fn probability_is_playable<T>(&self, card_info: &T) -> f32 where T: CardInfo {
|
||||||
self.probability_of_predicate(card_info, &Self::is_playable)
|
let f = |card: &Card| { self.is_playable(card) };
|
||||||
|
card_info.probability_of_predicate(&f)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn probability_is_dead<T>(&self, card_info: &T) -> f32 where T: CardInfo {
|
pub fn probability_is_dead<T>(&self, card_info: &T) -> f32 where T: CardInfo {
|
||||||
self.probability_of_predicate(card_info, &Self::is_dead)
|
let f = |card: &Card| { self.is_dead(card) };
|
||||||
|
card_info.probability_of_predicate(&f)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn probability_is_dispensable<T>(&self, card_info: &T) -> f32 where T: CardInfo {
|
pub fn probability_is_dispensable<T>(&self, card_info: &T) -> f32 where T: CardInfo {
|
||||||
self.probability_of_predicate(card_info, &Self::is_dispensable)
|
let f = |card: &Card| { self.is_dispensable(card) };
|
||||||
|
card_info.probability_of_predicate(&f)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_players(&self) -> Vec<Player> {
|
pub fn get_players(&self) -> Vec<Player> {
|
||||||
|
|
78
src/info.rs
78
src/info.rs
|
@ -2,6 +2,7 @@ use std::cmp::Eq;
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
|
use std::convert::From;
|
||||||
|
|
||||||
use cards::*;
|
use cards::*;
|
||||||
|
|
||||||
|
@ -20,8 +21,6 @@ pub trait CardInfo {
|
||||||
// whether the card is possible
|
// whether the card is possible
|
||||||
fn is_possible(&self, card: &Card) -> bool;
|
fn is_possible(&self, card: &Card) -> bool;
|
||||||
|
|
||||||
// TODO: have a borrow_possibilities to allow for more efficiency?
|
|
||||||
|
|
||||||
// mark all current possibilities for the card
|
// mark all current possibilities for the card
|
||||||
fn get_possibilities(&self) -> Vec<Card> {
|
fn get_possibilities(&self) -> Vec<Card> {
|
||||||
let mut v = Vec::new();
|
let mut v = Vec::new();
|
||||||
|
@ -37,10 +36,10 @@ pub trait CardInfo {
|
||||||
}
|
}
|
||||||
// get probability weight for the card
|
// get probability weight for the card
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
fn get_weight(&self, card: &Card) -> u32 {
|
fn get_weight(&self, card: &Card) -> f32 {
|
||||||
1
|
1 as f32
|
||||||
}
|
}
|
||||||
fn get_weighted_possibilities(&self) -> Vec<(Card, u32)> {
|
fn get_weighted_possibilities(&self) -> Vec<(Card, f32)> {
|
||||||
let mut v = Vec::new();
|
let mut v = Vec::new();
|
||||||
for card in self.get_possibilities() {
|
for card in self.get_possibilities() {
|
||||||
let weight = self.get_weight(&card);
|
let weight = self.get_weight(&card);
|
||||||
|
@ -48,6 +47,25 @@ pub trait CardInfo {
|
||||||
}
|
}
|
||||||
v
|
v
|
||||||
}
|
}
|
||||||
|
fn weighted_score<T>(&self, score_fn: &Fn(&Card) -> T) -> f32
|
||||||
|
where f32: From<T>
|
||||||
|
{
|
||||||
|
let mut total_score = 0.;
|
||||||
|
let mut total_weight = 0.;
|
||||||
|
for card in self.get_possibilities() {
|
||||||
|
let weight = self.get_weight(&card);
|
||||||
|
let score = f32::from(score_fn(&card));
|
||||||
|
total_weight += weight;
|
||||||
|
total_score += weight * score;
|
||||||
|
}
|
||||||
|
total_score / total_weight
|
||||||
|
}
|
||||||
|
fn probability_of_predicate(&self, predicate: &Fn(&Card) -> bool) -> f32 {
|
||||||
|
let f = |card: &Card| {
|
||||||
|
if predicate(card) { 1.0 } else { 0.0 }
|
||||||
|
};
|
||||||
|
self.weighted_score(&f)
|
||||||
|
}
|
||||||
|
|
||||||
// mark a whole color as false
|
// mark a whole color as false
|
||||||
fn mark_color_false(&mut self, color: &Color);
|
fn mark_color_false(&mut self, color: &Color);
|
||||||
|
@ -215,31 +233,53 @@ impl fmt::Display for SimpleCardInfo {
|
||||||
|
|
||||||
// Can represent information of the form:
|
// Can represent information of the form:
|
||||||
// this card is/isn't possible
|
// this card is/isn't possible
|
||||||
// also, maintains weights for the cards
|
// also, maintains integer weights for the cards
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct CardPossibilityTable {
|
pub struct CardPossibilityTable {
|
||||||
possible: HashMap<Card, u32>,
|
possible: HashMap<Card, u32>,
|
||||||
}
|
}
|
||||||
impl CardPossibilityTable {
|
impl CardPossibilityTable {
|
||||||
pub fn new() -> CardPossibilityTable {
|
pub fn new() -> CardPossibilityTable {
|
||||||
|
Self::from(&CardCounts::new())
|
||||||
|
}
|
||||||
|
|
||||||
|
// mark a possible card as false
|
||||||
|
pub fn mark_false(&mut self, card: &Card) {
|
||||||
|
self.possible.remove(card);
|
||||||
|
}
|
||||||
|
|
||||||
|
// a bit more efficient
|
||||||
|
pub fn borrow_possibilities<'a>(&'a self) -> Vec<&'a Card> {
|
||||||
|
self.possible.keys().collect::<Vec<_>>()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decrement_weight_if_possible(&mut self, card: &Card) {
|
||||||
|
if self.is_possible(card) {
|
||||||
|
self.decrement_weight(card);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decrement_weight(&mut self, card: &Card) {
|
||||||
|
let weight =
|
||||||
|
self.possible.get_mut(card)
|
||||||
|
.expect(&format!("Decrementing weight for impossible card: {}", card));
|
||||||
|
*weight -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl <'a> From<&'a CardCounts> for CardPossibilityTable {
|
||||||
|
fn from(counts: &'a CardCounts) -> CardPossibilityTable {
|
||||||
let mut possible = HashMap::new();
|
let mut possible = HashMap::new();
|
||||||
for &color in COLORS.iter() {
|
for &color in COLORS.iter() {
|
||||||
for &value in VALUES.iter() {
|
for &value in VALUES.iter() {
|
||||||
possible.insert(
|
let card = Card::new(color, value);
|
||||||
Card::new(color, value),
|
let count = counts.remaining(&card);
|
||||||
get_count_for_value(&value)
|
possible.insert(card, count);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
CardPossibilityTable {
|
CardPossibilityTable {
|
||||||
possible: possible,
|
possible: possible,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// mark a possible card as false
|
|
||||||
fn mark_false(&mut self, card: &Card) {
|
|
||||||
self.possible.remove(card);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
impl CardInfo for CardPossibilityTable {
|
impl CardInfo for CardPossibilityTable {
|
||||||
fn is_possible(&self, card: &Card) -> bool {
|
fn is_possible(&self, card: &Card) -> bool {
|
||||||
|
@ -261,14 +301,14 @@ impl CardInfo for CardPossibilityTable {
|
||||||
self.mark_false(&Card::new(color, value.clone()));
|
self.mark_false(&Card::new(color, value.clone()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn get_weight(&self, card: &Card) -> u32 {
|
fn get_weight(&self, card: &Card) -> f32 {
|
||||||
*self.possible.get(card).unwrap_or(&0)
|
*self.possible.get(card).unwrap_or(&0) as f32
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl fmt::Display for CardPossibilityTable {
|
impl fmt::Display for CardPossibilityTable {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
for card in self.get_possibilities() {
|
for (card, weight) in &self.possible {
|
||||||
try!(f.write_str(&format!("{}, ", card)));
|
try!(f.write_str(&format!("{} {}, ", weight, card)));
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ mod simulator;
|
||||||
mod strategies {
|
mod strategies {
|
||||||
pub mod examples;
|
pub mod examples;
|
||||||
pub mod cheating;
|
pub mod cheating;
|
||||||
|
pub mod information;
|
||||||
}
|
}
|
||||||
|
|
||||||
use getopts::Options;
|
use getopts::Options;
|
||||||
|
@ -128,6 +129,10 @@ fn main() {
|
||||||
Box::new(strategies::cheating::CheatingStrategyConfig::new())
|
Box::new(strategies::cheating::CheatingStrategyConfig::new())
|
||||||
as Box<simulator::GameStrategyConfig + Sync>
|
as Box<simulator::GameStrategyConfig + Sync>
|
||||||
},
|
},
|
||||||
|
"info" => {
|
||||||
|
Box::new(strategies::information::InformationStrategyConfig::new())
|
||||||
|
as Box<simulator::GameStrategyConfig + Sync>
|
||||||
|
},
|
||||||
_ => {
|
_ => {
|
||||||
print_usage(&program, opts);
|
print_usage(&program, opts);
|
||||||
panic!("Unexpected strategy argument {}", strategy_str);
|
panic!("Unexpected strategy argument {}", strategy_str);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::rc::Rc;
|
|
||||||
use std::cell::{RefCell};
|
use std::cell::{RefCell};
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
use simulator::*;
|
use simulator::*;
|
||||||
use game::*;
|
use game::*;
|
||||||
|
|
376
src/strategies/information.rs
Normal file
376
src/strategies/information.rs
Normal file
|
@ -0,0 +1,376 @@
|
||||||
|
use std::collections::{HashMap, HashSet};
|
||||||
|
use rand::{self, Rng};
|
||||||
|
|
||||||
|
use simulator::*;
|
||||||
|
use game::*;
|
||||||
|
|
||||||
|
// strategy that recommends other players an action.
|
||||||
|
//
|
||||||
|
// 50 cards, 25 plays, 25 left
|
||||||
|
// with 5 players:
|
||||||
|
// - only 5 + 8 hints total. each player goes through 10 cards
|
||||||
|
// with 4 players:
|
||||||
|
// - only 9 + 8 hints total. each player goes through 12.5 cards
|
||||||
|
//
|
||||||
|
// For any given player with at least 4 cards, and index i, there are at least 3 hints that can be given.
|
||||||
|
// 1. a value hint on card i
|
||||||
|
// 2. a color hint on card i
|
||||||
|
// 3. any hint not involving card i
|
||||||
|
//
|
||||||
|
// for 4 players, can give 6 distinct hints
|
||||||
|
|
||||||
|
struct ModulusInformation {
|
||||||
|
modulus: u32,
|
||||||
|
value: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Question {
|
||||||
|
IsPlayable(usize),
|
||||||
|
IsDead(usize),
|
||||||
|
}
|
||||||
|
|
||||||
|
fn answer_question(question: Question, hand: &Cards, view: &GameStateView) -> ModulusInformation {
|
||||||
|
match question {
|
||||||
|
Question::IsPlayable(index) => {
|
||||||
|
let ref card = hand[index];
|
||||||
|
ModulusInformation {
|
||||||
|
modulus: 2,
|
||||||
|
value: if view.board.is_playable(card) { 1 } else { 0 },
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Question::IsDead(index) => {
|
||||||
|
let ref card = hand[index];
|
||||||
|
ModulusInformation {
|
||||||
|
modulus: 2,
|
||||||
|
value: if view.board.is_dead(card) { 1 } else { 0 },
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub struct InformationStrategyConfig;
|
||||||
|
|
||||||
|
impl InformationStrategyConfig {
|
||||||
|
pub fn new() -> InformationStrategyConfig {
|
||||||
|
InformationStrategyConfig
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl GameStrategyConfig for InformationStrategyConfig {
|
||||||
|
fn initialize(&self, opts: &GameOptions) -> Box<GameStrategy> {
|
||||||
|
if opts.num_players < 4 {
|
||||||
|
panic!("Information strategy doesn't work with less than 4 players");
|
||||||
|
}
|
||||||
|
Box::new(InformationStrategy::new())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct InformationStrategy;
|
||||||
|
|
||||||
|
impl InformationStrategy {
|
||||||
|
pub fn new() -> InformationStrategy {
|
||||||
|
InformationStrategy
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl GameStrategy for InformationStrategy {
|
||||||
|
fn initialize(&self, player: Player, view: &GameStateView) -> Box<PlayerStrategy> {
|
||||||
|
let mut public_info = HashMap::new();
|
||||||
|
for player in view.board.get_players() {
|
||||||
|
let hand_info = (0..view.board.hand_size).map(|_| { CardPossibilityTable::new() }).collect::<Vec<_>>();
|
||||||
|
public_info.insert(player, hand_info);
|
||||||
|
}
|
||||||
|
Box::new(InformationPlayerStrategy {
|
||||||
|
me: player,
|
||||||
|
public_info: public_info,
|
||||||
|
public_counts: CardCounts::new(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct InformationPlayerStrategy {
|
||||||
|
me: Player,
|
||||||
|
public_info: HashMap<Player, Vec<CardPossibilityTable>>,
|
||||||
|
public_counts: CardCounts, // what any newly drawn card should be
|
||||||
|
}
|
||||||
|
impl InformationPlayerStrategy {
|
||||||
|
// given a hand of cards, represents how badly it will need to play things
|
||||||
|
fn hand_play_value(&self, view: &GameStateView, hand: &Cards/*, all_viewable: HashMap<Color, <Value, usize>> */) -> u32 {
|
||||||
|
// dead = 0 points
|
||||||
|
// indispensible = 5 + (5 - value) points
|
||||||
|
// playable = 1 point
|
||||||
|
let mut value = 0;
|
||||||
|
for card in hand {
|
||||||
|
if view.board.is_dead(card) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !view.board.is_dispensable(card) {
|
||||||
|
value += 10 - card.value;
|
||||||
|
} else {
|
||||||
|
value += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
value
|
||||||
|
}
|
||||||
|
|
||||||
|
fn estimate_hand_play_value(&self, view: &GameStateView) -> u32 {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
|
// how badly do we need to play a particular card
|
||||||
|
fn get_average_play_score(&self, view: &GameStateView, card_table: &CardPossibilityTable) -> f32 {
|
||||||
|
let f = |card: &Card| {
|
||||||
|
self.get_play_score(view, card) as f32
|
||||||
|
};
|
||||||
|
card_table.weighted_score(&f)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_play_score(&self, view: &GameStateView, card: &Card) -> i32 {
|
||||||
|
let my_hand_value = self.estimate_hand_play_value(view);
|
||||||
|
|
||||||
|
for player in view.board.get_players() {
|
||||||
|
if player != self.me {
|
||||||
|
if view.has_card(&player, card) {
|
||||||
|
let their_hand_value = self.hand_play_value(view, view.get_hand(&player));
|
||||||
|
// they can play this card, and have less urgent plays than i do
|
||||||
|
if their_hand_value <= my_hand_value {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// there are no hints
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_player_public_info(&self, player: &Player) -> &Vec<CardPossibilityTable> {
|
||||||
|
self.public_info.get(player).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_player_public_info_mut(&mut self, player: &Player) -> &mut Vec<CardPossibilityTable> {
|
||||||
|
self.public_info.get_mut(player).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_public_info_for_hint(&mut self, hint: &Hint, matches: &Vec<bool>) {
|
||||||
|
let mut info = self.get_player_public_info_mut(&hint.player);
|
||||||
|
let zip_iter = info.iter_mut().zip(matches);
|
||||||
|
match hint.hinted {
|
||||||
|
Hinted::Color(ref color) => {
|
||||||
|
for (card_info, matched) in zip_iter {
|
||||||
|
card_info.mark_color(color, *matched);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Hinted::Value(ref value) => {
|
||||||
|
for (card_info, matched) in zip_iter {
|
||||||
|
card_info.mark_value(value, *matched);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_public_info_for_discard_or_play(
|
||||||
|
&mut self,
|
||||||
|
view: &GameStateView,
|
||||||
|
player: &Player,
|
||||||
|
index: usize,
|
||||||
|
card: &Card
|
||||||
|
) {
|
||||||
|
let new_card_table = CardPossibilityTable::from(&self.public_counts);
|
||||||
|
{
|
||||||
|
let mut info = self.get_player_public_info_mut(&player);
|
||||||
|
assert!(info[index].is_possible(card));
|
||||||
|
info.remove(index);
|
||||||
|
|
||||||
|
// push *before* incrementing public counts
|
||||||
|
if info.len() < view.info.len() {
|
||||||
|
info.push(new_card_table);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// note: other_player could be player, as well
|
||||||
|
// in particular, we will decrement the newly drawn card
|
||||||
|
for other_player in view.board.get_players() {
|
||||||
|
let mut info = self.get_player_public_info_mut(&other_player);
|
||||||
|
for card_table in info {
|
||||||
|
card_table.decrement_weight_if_possible(card);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.public_counts.increment(card);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_private_info(&self, view: &GameStateView) -> Vec<CardPossibilityTable> {
|
||||||
|
let mut info = self.get_player_public_info(&self.me).clone();
|
||||||
|
for card_table in info.iter_mut() {
|
||||||
|
for (other_player, state) in &view.other_player_states {
|
||||||
|
for card in &state.hand {
|
||||||
|
card_table.decrement_weight_if_possible(card);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
info
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
impl PlayerStrategy for InformationPlayerStrategy {
|
||||||
|
fn decide(&mut self, view: &GameStateView) -> TurnChoice {
|
||||||
|
let private_info = self.get_private_info(view);
|
||||||
|
// debug!("My info:");
|
||||||
|
// for (i, card_table) in private_info.iter().enumerate() {
|
||||||
|
// debug!("{}: {}", i, card_table);
|
||||||
|
// }
|
||||||
|
|
||||||
|
let playable_cards = private_info.iter().enumerate().filter(|&(_, card_table)| {
|
||||||
|
view.board.probability_is_playable(card_table) == 1.0
|
||||||
|
}).collect::<Vec<_>>();
|
||||||
|
|
||||||
|
if playable_cards.len() > 0 {
|
||||||
|
// play the best playable card
|
||||||
|
// the higher the play_score, the better to play
|
||||||
|
let mut play_score = -1.0;
|
||||||
|
let mut play_index = 0;
|
||||||
|
|
||||||
|
for (index, card_table) in playable_cards {
|
||||||
|
let score = self.get_average_play_score(view, card_table);
|
||||||
|
if score > play_score {
|
||||||
|
play_score = score;
|
||||||
|
play_index = index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TurnChoice::Play(play_index)
|
||||||
|
} else {
|
||||||
|
if view.board.hints_remaining > 0 {
|
||||||
|
let hint_player = view.board.player_to_left(&self.me);
|
||||||
|
let hint_card = rand::thread_rng().choose(&view.get_hand(&hint_player)).unwrap();
|
||||||
|
let hinted = {
|
||||||
|
if rand::random() {
|
||||||
|
// hint a color
|
||||||
|
Hinted::Color(hint_card.color)
|
||||||
|
} else {
|
||||||
|
Hinted::Value(hint_card.value)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
TurnChoice::Hint(Hint {
|
||||||
|
player: hint_player,
|
||||||
|
hinted: hinted,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
TurnChoice::Discard(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// // 50 total, 25 to play, 20 in hand
|
||||||
|
// if view.board.discard.cards.len() < 6 {
|
||||||
|
// // if anything is totally useless, discard it
|
||||||
|
// if let Some(i) = self.find_useless_card(view) {
|
||||||
|
// 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) {
|
||||||
|
// 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)
|
||||||
|
// // The higher, the better to discard
|
||||||
|
// let mut discard_card = None;
|
||||||
|
// let mut compval = (false, false, 0);
|
||||||
|
// for card in my_cards {
|
||||||
|
// let my_compval = (
|
||||||
|
// view.can_see(card),
|
||||||
|
// view.board.is_dispensable(card),
|
||||||
|
// card.value,
|
||||||
|
// );
|
||||||
|
// if my_compval > compval {
|
||||||
|
// discard_card = Some(card);
|
||||||
|
// compval = my_compval;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// if let Some(card) = discard_card {
|
||||||
|
// if view.board.hints_remaining > 0 {
|
||||||
|
// if !view.can_see(card) {
|
||||||
|
// return self.throwaway_hint(view);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// let index = my_cards.iter().position(|iter_card| {
|
||||||
|
// card == iter_card
|
||||||
|
// }).unwrap();
|
||||||
|
// TurnChoice::Discard(index)
|
||||||
|
// } else {
|
||||||
|
// panic!("This shouldn't happen! No discardable card");
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update(&mut self, turn: &Turn, view: &GameStateView) {
|
||||||
|
match turn.choice {
|
||||||
|
TurnChoice::Hint(ref hint) => {
|
||||||
|
if let &TurnResult::Hint(ref matches) = &turn.result {
|
||||||
|
self.update_public_info_for_hint(hint, matches);
|
||||||
|
} else {
|
||||||
|
panic!("Got turn choice {:?}, but turn result {:?}",
|
||||||
|
turn.choice, turn.result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TurnChoice::Discard(index) => {
|
||||||
|
if let &TurnResult::Discard(ref card) = &turn.result {
|
||||||
|
self.update_public_info_for_discard_or_play(view, &turn.player, index, card);
|
||||||
|
} else {
|
||||||
|
panic!("Got turn choice {:?}, but turn result {:?}",
|
||||||
|
turn.choice, turn.result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TurnChoice::Play(index) => {
|
||||||
|
if let &TurnResult::Play(ref card, played) = &turn.result {
|
||||||
|
self.update_public_info_for_discard_or_play(view, &turn.player, index, card);
|
||||||
|
} else {
|
||||||
|
panic!("Got turn choice {:?}, but turn result {:?}",
|
||||||
|
turn.choice, turn.result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue