information strategy, working!

This commit is contained in:
Jeff Wu 2016-03-28 22:16:58 -07:00
parent efba24d6e8
commit adf98e0e4a
7 changed files with 604 additions and 181 deletions

View file

@ -92,7 +92,7 @@ impl fmt::Display for CardCounts {
} }
} }
#[derive(Debug)] #[derive(Debug,Clone)]
pub struct Discard { pub struct Discard {
pub cards: Cards, pub cards: Cards,
counts: CardCounts, counts: CardCounts,

View file

@ -66,7 +66,7 @@ pub struct GameOptions {
} }
// The state of a given player: all other players may see this // The state of a given player: all other players may see this
#[derive(Debug)] #[derive(Debug,Clone)]
pub struct PlayerState { pub struct PlayerState {
// the player's actual hand // the player's actual hand
pub hand: Cards, pub hand: Cards,
@ -154,7 +154,7 @@ fn new_deck(seed: u32) -> Cards {
// State of everything except the player's hands // State of everything except the player's hands
// Is all completely common knowledge // Is all completely common knowledge
#[derive(Debug)] #[derive(Debug,Clone)]
pub struct BoardState { pub struct BoardState {
deck: Cards, deck: Cards,
pub total_cards: u32, pub total_cards: u32,
@ -377,9 +377,50 @@ impl fmt::Display for BoardState {
} }
// complete game view of a given player // complete game view of a given player
// state will be borrowed GameState pub trait GameView {
fn me(&self) -> Player;
fn my_info(&self) -> &Vec<SimpleCardInfo>;
fn get_state(&self, player: &Player) -> &PlayerState;
fn get_board(&self) -> &BoardState;
fn get_hand(&self, player: &Player) -> &Cards {
assert!(self.me() != *player, "Cannot query about your own cards!");
&self.get_state(player).hand
}
fn hand_size(&self, player: &Player) -> usize {
if self.me() == *player {
self.my_info().len()
} else {
self.get_hand(player).len()
}
}
fn has_card(&self, player: &Player, card: &Card) -> bool {
for other_card in self.get_hand(player) {
if *card == *other_card {
return true;
}
}
false
}
fn can_see(&self, card: &Card) -> bool {
for other_player in self.get_board().get_players() {
if self.me() == other_player {
continue
}
if self.has_card(&other_player, card) {
return true;
}
}
false
}
}
// version of game view that is borrowed. used in simulator for efficiency,
#[derive(Debug)] #[derive(Debug)]
pub struct GameStateView<'a> { pub struct BorrowedGameView<'a> {
// the player whose view it is // the player whose view it is
pub player: Player, pub player: Player,
// what is known about their own hand (and thus common knowledge) // what is known about their own hand (and thus common knowledge)
@ -389,30 +430,70 @@ pub struct GameStateView<'a> {
// board state // board state
pub board: &'a BoardState, pub board: &'a BoardState,
} }
impl <'a> GameStateView<'a> { impl <'a> GameView for BorrowedGameView<'a> {
pub fn get_hand(&self, player: &Player) -> &Cards { fn me(&self) -> Player {
assert!(self.player != *player, "Cannot query about your own cards!"); self.player
&self.other_player_states.get(player).unwrap().hand
} }
fn my_info(&self) -> &Vec<SimpleCardInfo> {
pub fn has_card(&self, player: &Player, card: &Card) -> bool { self.info
for other_card in self.get_hand(player) {
if *card == *other_card {
return true;
}
}
false
} }
pub fn can_see(&self, card: &Card) -> bool { fn get_state(&self, player: &Player) -> &PlayerState {
for other_player in self.other_player_states.keys() { assert!(self.me() != *player, "Cannot query about your own state!");
if self.has_card(other_player, card) { self.other_player_states.get(player).unwrap()
return true; }
} fn get_board(&self) -> &BoardState {
} self.board
false
} }
} }
// version of game view, may be useful to strategies
#[derive(Debug)]
pub struct OwnedGameView {
// the player whose view it is
pub player: Player,
// what is known about their own hand (and thus common knowledge)
pub info: Vec<SimpleCardInfo>,
// the cards of the other players, as well as the information they have
pub other_player_states: HashMap<Player, PlayerState>,
// board state
pub board: BoardState,
}
impl OwnedGameView {
pub fn clone_from(borrowed_view: &BorrowedGameView) -> OwnedGameView {
let mut info : Vec<SimpleCardInfo> = Vec::new();
for card_info in borrowed_view.info.iter() {
info.push((*card_info).clone());
}
let mut other_player_states : HashMap<Player, PlayerState> = HashMap::new();
for (other_player, player_state) in &borrowed_view.other_player_states {
other_player_states.insert(*other_player, (*player_state).clone());
}
OwnedGameView {
player: borrowed_view.player.clone(),
info: info,
other_player_states: other_player_states,
board: (*borrowed_view.board).clone(),
}
}
}
impl GameView for OwnedGameView {
fn me(&self) -> Player {
self.player
}
fn my_info(&self) -> &Vec<SimpleCardInfo> {
&self.info
}
fn get_state(&self, player: &Player) -> &PlayerState {
assert!(self.me() != *player, "Cannot query about your own state!");
self.other_player_states.get(player).unwrap()
}
fn get_board(&self) -> &BoardState {
&self.board
}
}
// complete game state (known to nobody!) // complete game state (known to nobody!)
#[derive(Debug)] #[derive(Debug)]
pub struct GameState { pub struct GameState {
@ -471,14 +552,14 @@ impl GameState {
} }
// get the game state view of a particular player // get the game state view of a particular player
pub fn get_view(&self, player: Player) -> GameStateView { pub fn get_view(&self, player: Player) -> BorrowedGameView {
let mut other_player_states = HashMap::new(); let mut other_player_states = HashMap::new();
for (other_player, state) in &self.player_states { for (other_player, state) in &self.player_states {
if player != *other_player { if player != *other_player {
other_player_states.insert(*other_player, state); other_player_states.insert(*other_player, state);
} }
} }
GameStateView { BorrowedGameView {
player: player, player: player,
info: &self.player_states.get(&player).unwrap().info, info: &self.player_states.get(&player).unwrap().info,
other_player_states: other_player_states, other_player_states: other_player_states,

View file

@ -151,7 +151,7 @@ pub trait Info<T> where T: Hash + Eq + Clone {
} }
} }
#[derive(Debug)] #[derive(Debug,Clone)]
pub struct ColorInfo(HashSet<Color>); pub struct ColorInfo(HashSet<Color>);
impl ColorInfo { impl ColorInfo {
pub fn new() -> ColorInfo { ColorInfo(ColorInfo::initialize()) } pub fn new() -> ColorInfo { ColorInfo(ColorInfo::initialize()) }
@ -162,7 +162,7 @@ impl Info<Color> for ColorInfo {
fn get_mut_possibility_set(&mut self) -> &mut HashSet<Color> { &mut self.0 } fn get_mut_possibility_set(&mut self) -> &mut HashSet<Color> { &mut self.0 }
} }
#[derive(Debug)] #[derive(Debug,Clone)]
pub struct ValueInfo(HashSet<Value>); pub struct ValueInfo(HashSet<Value>);
impl ValueInfo { impl ValueInfo {
pub fn new() -> ValueInfo { ValueInfo(ValueInfo::initialize()) } pub fn new() -> ValueInfo { ValueInfo(ValueInfo::initialize()) }
@ -175,7 +175,7 @@ impl Info<Value> for ValueInfo {
// represents information only of the form: // represents information only of the form:
// this color is/isn't possible, this value is/isn't possible // this color is/isn't possible, this value is/isn't possible
#[derive(Debug)] #[derive(Debug,Clone)]
pub struct SimpleCardInfo { pub struct SimpleCardInfo {
pub color_info: ColorInfo, pub color_info: ColorInfo,
pub value_info: ValueInfo, pub value_info: ValueInfo,
@ -260,10 +260,25 @@ impl CardPossibilityTable {
} }
pub fn decrement_weight(&mut self, card: &Card) { pub fn decrement_weight(&mut self, card: &Card) {
let weight = let remove = {
self.possible.get_mut(card) let weight =
.expect(&format!("Decrementing weight for impossible card: {}", card)); self.possible.get_mut(card)
*weight -= 1; .expect(&format!("Decrementing weight for impossible card: {}", card));
*weight -= 1;
*weight == 0
};
if remove {
self.possible.remove(card);
}
}
pub fn get_card(&self) -> Option<Card> {
let possibilities = self.get_possibilities();
if possibilities.len() == 1 {
Some(possibilities[0].clone())
} else {
None
}
} }
} }
impl <'a> From<&'a CardCounts> for CardPossibilityTable { impl <'a> From<&'a CardCounts> for CardPossibilityTable {

View file

@ -10,17 +10,17 @@ use game::*;
// Represents the strategy of a given player // Represents the strategy of a given player
pub trait PlayerStrategy { pub trait PlayerStrategy {
// A function to decide what to do on the player's turn. // A function to decide what to do on the player's turn.
// Given a GameStateView, outputs their choice. // Given a BorrowedGameView, outputs their choice.
fn decide(&mut self, &GameStateView) -> TurnChoice; fn decide(&mut self, &BorrowedGameView) -> TurnChoice;
// A function to update internal state after other players' turns. // A function to update internal state after other players' turns.
// Given what happened last turn, and the new state. // Given what happened last turn, and the new state.
fn update(&mut self, &Turn, &GameStateView); fn update(&mut self, &Turn, &BorrowedGameView);
} }
// Represents the overall strategy for a game // Represents the overall strategy for a game
// Shouldn't do much, except store configuration parameters and // Shouldn't do much, except store configuration parameters and
// possibility initialize some shared randomness between players // possibility initialize some shared randomness between players
pub trait GameStrategy { pub trait GameStrategy {
fn initialize(&self, Player, &GameStateView) -> Box<PlayerStrategy>; fn initialize(&self, Player, &BorrowedGameView) -> Box<PlayerStrategy>;
} }
// Represents configuration for a strategy. // Represents configuration for a strategy.

View file

@ -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::*;
@ -43,7 +43,7 @@ impl CheatingStrategy {
} }
} }
impl GameStrategy for CheatingStrategy { impl GameStrategy for CheatingStrategy {
fn initialize(&self, player: Player, view: &GameStateView) -> Box<PlayerStrategy> { fn initialize(&self, player: Player, view: &BorrowedGameView) -> Box<PlayerStrategy> {
for (player, state) in &view.other_player_states { for (player, state) in &view.other_player_states {
self.player_states_cheat.borrow_mut().insert( self.player_states_cheat.borrow_mut().insert(
*player, state.hand.clone() *player, state.hand.clone()
@ -62,7 +62,7 @@ pub struct CheatingPlayerStrategy {
} }
impl CheatingPlayerStrategy { impl CheatingPlayerStrategy {
// last player might've drawn a new card, let him know! // last player might've drawn a new card, let him know!
fn inform_last_player_cards(&self, view: &GameStateView) { fn inform_last_player_cards(&self, view: &BorrowedGameView) {
let next = view.board.player_to_right(&self.me); let next = view.board.player_to_right(&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()
@ -70,7 +70,7 @@ impl CheatingPlayerStrategy {
} }
// give a throwaway hint - we only do this when we have nothing to do // give a throwaway hint - we only do this when we have nothing to do
fn throwaway_hint(&self, view: &GameStateView) -> TurnChoice { fn throwaway_hint(&self, view: &BorrowedGameView) -> TurnChoice {
let hint_player = view.board.player_to_left(&self.me); let hint_player = view.board.player_to_left(&self.me);
let hint_card = &view.get_hand(&hint_player).first().unwrap(); let hint_card = &view.get_hand(&hint_player).first().unwrap();
TurnChoice::Hint(Hint { TurnChoice::Hint(Hint {
@ -80,7 +80,7 @@ impl CheatingPlayerStrategy {
} }
// given a hand of cards, represents how badly it will need to play things // 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, u32>> */) -> u32 { fn hand_play_value(&self, view: &BorrowedGameView, hand: &Cards/*, all_viewable: HashMap<Color, <Value, u32>> */) -> u32 {
// dead = 0 points // dead = 0 points
// indispensible = 5 + (5 - value) points // indispensible = 5 + (5 - value) points
// playable, not in another hand = 2 point // playable, not in another hand = 2 point
@ -102,7 +102,7 @@ impl CheatingPlayerStrategy {
} }
// how badly do we need to play a particular card // how badly do we need to play a particular card
fn get_play_score(&self, view: &GameStateView, card: &Card) -> i32 { fn get_play_score(&self, view: &BorrowedGameView, card: &Card) -> i32 {
let states = self.player_states_cheat.borrow(); let states = self.player_states_cheat.borrow();
let my_hand = states.get(&self.me).unwrap(); let my_hand = states.get(&self.me).unwrap();
@ -124,7 +124,7 @@ impl CheatingPlayerStrategy {
20 - (card.value as i32) 20 - (card.value as i32)
} }
fn find_useless_card(&self, view: &GameStateView, hand: &Cards) -> Option<usize> { fn find_useless_card(&self, view: &BorrowedGameView, hand: &Cards) -> Option<usize> {
let mut set: HashSet<Card> = HashSet::new(); let mut set: HashSet<Card> = HashSet::new();
for (i, card) in hand.iter().enumerate() { for (i, card) in hand.iter().enumerate() {
@ -140,7 +140,7 @@ impl CheatingPlayerStrategy {
return None return None
} }
fn someone_else_can_play(&self, view: &GameStateView) -> bool { fn someone_else_can_play(&self, view: &BorrowedGameView) -> bool {
for player in view.board.get_players() { for player in view.board.get_players() {
if player != self.me { if player != self.me {
for card in view.get_hand(&player) { for card in view.get_hand(&player) {
@ -154,7 +154,7 @@ impl CheatingPlayerStrategy {
} }
} }
impl PlayerStrategy for CheatingPlayerStrategy { impl PlayerStrategy for CheatingPlayerStrategy {
fn decide(&mut self, view: &GameStateView) -> TurnChoice { fn decide(&mut self, view: &BorrowedGameView) -> TurnChoice {
self.inform_last_player_cards(view); self.inform_last_player_cards(view);
let states = self.player_states_cheat.borrow(); let states = self.player_states_cheat.borrow();
@ -180,63 +180,64 @@ impl PlayerStrategy for CheatingPlayerStrategy {
let index = my_cards.iter().position(|card| { let index = my_cards.iter().position(|card| {
card == play_card.unwrap() card == play_card.unwrap()
}).unwrap(); }).unwrap();
TurnChoice::Play(index)
} else {
// discard threshold is how many cards we're willing to discard
// such that if we only played,
// we would not reach the final countdown round
// e.g. 50 total, 25 to play, 20 in hand
let discard_threshold =
view.board.total_cards
- (COLORS.len() * VALUES.len()) as u32
- (view.board.num_players * view.board.hand_size);
if view.board.discard_size() <= discard_threshold {
// if anything is totally useless, discard it
if let Some(i) = self.find_useless_card(view, my_cards) {
return TurnChoice::Discard(i);
}
}
// hinting is better than discarding dead cards return TurnChoice::Play(index)
// (probably because it stalls the deck-drawing). }
if view.board.hints_remaining > 0 {
if self.someone_else_can_play(view) {
return self.throwaway_hint(view);
}
}
// discard threshold is how many cards we're willing to discard
// such that if we only played,
// we would not reach the final countdown round
// e.g. 50 total, 25 to play, 20 in hand
let discard_threshold =
view.board.total_cards
- (COLORS.len() * VALUES.len()) as u32
- (view.board.num_players * view.board.hand_size);
if view.board.discard_size() <= discard_threshold {
// if anything is totally useless, discard it // if anything is totally useless, discard it
if let Some(i) = self.find_useless_card(view, my_cards) { if let Some(i) = self.find_useless_card(view, my_cards) {
return TurnChoice::Discard(i); return TurnChoice::Discard(i);
} }
}
// All cards are plausibly useful. // hinting is better than discarding dead cards
// Play the best discardable card, according to the ordering induced by comparing // (probably because it stalls the deck-drawing).
// (is in another hand, is dispensable, value) if view.board.hints_remaining > 0 {
// The higher, the better to discard if self.someone_else_can_play(view) {
let mut discard_card = None; return self.throwaway_hint(view);
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 {
let index = my_cards.iter().position(|iter_card| {
card == iter_card
}).unwrap();
TurnChoice::Discard(index)
} else {
panic!("This shouldn't happen! No discardable card");
} }
} }
// 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)
// 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 {
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, _: &GameStateView) { fn update(&mut self, _: &Turn, _: &BorrowedGameView) {
} }
} }

View file

@ -24,7 +24,7 @@ pub struct RandomStrategy {
play_probability: f64, play_probability: f64,
} }
impl GameStrategy for RandomStrategy { impl GameStrategy for RandomStrategy {
fn initialize(&self, player: Player, _: &GameStateView) -> Box<PlayerStrategy> { fn initialize(&self, player: Player, _: &BorrowedGameView) -> Box<PlayerStrategy> {
Box::new(RandomStrategyPlayer { Box::new(RandomStrategyPlayer {
hint_probability: self.hint_probability, hint_probability: self.hint_probability,
play_probability: self.play_probability, play_probability: self.play_probability,
@ -40,7 +40,7 @@ pub struct RandomStrategyPlayer {
} }
impl PlayerStrategy for RandomStrategyPlayer { impl PlayerStrategy for RandomStrategyPlayer {
fn decide(&mut self, view: &GameStateView) -> TurnChoice { fn decide(&mut self, view: &BorrowedGameView) -> 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 {
@ -67,6 +67,6 @@ impl PlayerStrategy for RandomStrategyPlayer {
TurnChoice::Discard(0) TurnChoice::Discard(0)
} }
} }
fn update(&mut self, _: &Turn, _: &GameStateView) { fn update(&mut self, _: &Turn, _: &BorrowedGameView) {
} }
} }

View file

@ -1,5 +1,4 @@
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use rand::{self, Rng};
use simulator::*; use simulator::*;
use game::*; use game::*;
@ -13,41 +12,150 @@ use game::*;
// - only 9 + 8 hints total. each player goes through 12.5 cards // - 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. // 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 // 0. a value hint on card i
// 2. a color hint on card i // 1. a color hint on card i
// 3. any hint not involving card i // 2. any hint not involving card i
// //
// for 4 players, can give 6 distinct hints // for 4 players, can give 6 distinct hints
// TODO: currently, you need to be very careful due to
// answers changing from the old view to the new view
#[derive(Debug,Clone)]
struct ModulusInformation { struct ModulusInformation {
modulus: u32, modulus: u32,
value: u32, value: u32,
} }
impl ModulusInformation {
pub fn new(modulus: u32, value: u32) -> Self {
assert!(value < modulus);
ModulusInformation {
modulus: modulus,
value: value,
}
}
enum Question { pub fn none() -> Self {
IsPlayable(usize), Self::new(1, 0)
IsDead(usize), }
}
fn answer_question(question: Question, hand: &Cards, view: &GameStateView) -> ModulusInformation { pub fn combine(&mut self, other: Self) {
match question { self.value = self.value * other.modulus + other.value;
Question::IsPlayable(index) => { self.modulus = self.modulus * other.modulus;
let ref card = hand[index]; }
ModulusInformation {
modulus: 2, pub fn emit(&mut self, modulus: u32) -> Self {
value: if view.board.is_playable(card) { 1 } else { 0 }, assert!(self.modulus >= modulus);
} assert!(self.modulus % modulus == 0);
}, self.modulus = self.modulus / modulus;
Question::IsDead(index) => { let value = self.value / self.modulus;
let ref card = hand[index]; assert!((self.value - value) % modulus == 0);
ModulusInformation { self.value = (self.value - value) / modulus;
modulus: 2,
value: if view.board.is_dead(card) { 1 } else { 0 }, Self::new(modulus, value)
} }
},
pub fn cast_up(&mut self, modulus: u32) {
assert!(self.modulus <= modulus);
self.modulus = modulus;
}
pub fn cast_down(&mut self, modulus: u32) {
assert!(self.modulus >= modulus);
assert!(self.value < modulus);
self.modulus = modulus;
}
pub fn add(&mut self, other: &Self) {
assert!(self.modulus == other.modulus);
self.value = (self.value + other.value) % self.modulus;
}
pub fn subtract(&mut self, other: &Self) {
assert!(self.modulus == other.modulus);
self.value = (self.modulus + self.value - other.value) % self.modulus;
} }
} }
trait Question {
// how much info does this question ask for?
fn info_amount(&self) -> u32;
// get the answer to this question, given cards
fn answer(&self, &Cards, Box<&GameView>) -> u32;
fn answer_info(&self, hand: &Cards, view: Box<&GameView>) -> ModulusInformation {
ModulusInformation::new(
self.info_amount(),
self.answer(hand, view)
)
}
// process the answer to this question, updating card info
fn acknowledge_answer(
&self, value: u32, &mut Vec<CardPossibilityTable>, Box<&GameView>
);
fn acknowledge_answer_info(
&self,
answer: ModulusInformation,
hand_info: &mut Vec<CardPossibilityTable>,
view: Box<&GameView>
) {
assert!(self.info_amount() == answer.modulus);
self.acknowledge_answer(answer.value, hand_info, view);
}
}
struct IsPlayable {
index: usize,
}
impl Question for IsPlayable {
fn info_amount(&self) -> u32 { 2 }
fn answer(&self, hand: &Cards, view: Box<&GameView>) -> u32 {
let ref card = hand[self.index];
if view.get_board().is_playable(card) { 1 } else { 0 }
}
fn acknowledge_answer(
&self,
answer: u32,
hand_info: &mut Vec<CardPossibilityTable>,
view: Box<&GameView>,
) {
let ref mut card_table = hand_info[self.index];
let possible = card_table.get_possibilities();
for card in &possible {
if view.get_board().is_playable(card) {
if answer == 0 { card_table.mark_false(card); }
} else {
if answer == 1 { card_table.mark_false(card); }
}
}
}
}
// struct IsDead {
// index: usize,
// }
// impl Question for IsDead {
// fn info_amount(&self) -> u32 { 2 }
// fn answer(&self, hand: &Cards, view: &Box<GameView>) -> u32 {
// let ref card = hand[self.index];
// if view.get_board().is_dead(card) { 1 } else { 0 }
// }
// fn acknowledge_answer(
// &self,
// answer: u32,
// hand_info: &mut Vec<CardPossibilityTable>,
// view: &Box<GameView>,
// ) {
// let ref mut card_table = hand_info[self.index];
// let possible = card_table.get_possibilities();
// for card in &possible {
// if view.get_board().is_dead(card) {
// if answer == 0 { card_table.mark_false(card); }
// } else {
// if answer == 1 { card_table.mark_false(card); }
// }
// }
// }
// }
#[allow(dead_code)] #[allow(dead_code)]
pub struct InformationStrategyConfig; pub struct InformationStrategyConfig;
@ -73,7 +181,7 @@ impl InformationStrategy {
} }
} }
impl GameStrategy for InformationStrategy { impl GameStrategy for InformationStrategy {
fn initialize(&self, player: Player, view: &GameStateView) -> Box<PlayerStrategy> { fn initialize(&self, player: Player, view: &BorrowedGameView) -> Box<PlayerStrategy> {
let mut public_info = HashMap::new(); let mut public_info = HashMap::new();
for player in view.board.get_players() { for player in view.board.get_players() {
let hand_info = (0..view.board.hand_size).map(|_| { CardPossibilityTable::new() }).collect::<Vec<_>>(); let hand_info = (0..view.board.hand_size).map(|_| { CardPossibilityTable::new() }).collect::<Vec<_>>();
@ -83,6 +191,7 @@ impl GameStrategy for InformationStrategy {
me: player, me: player,
public_info: public_info, public_info: public_info,
public_counts: CardCounts::new(), public_counts: CardCounts::new(),
last_view: OwnedGameView::clone_from(view),
}) })
} }
} }
@ -91,10 +200,147 @@ pub struct InformationPlayerStrategy {
me: Player, me: Player,
public_info: HashMap<Player, Vec<CardPossibilityTable>>, public_info: HashMap<Player, Vec<CardPossibilityTable>>,
public_counts: CardCounts, // what any newly drawn card should be public_counts: CardCounts, // what any newly drawn card should be
last_view: OwnedGameView, // the view on the previous turn
} }
impl InformationPlayerStrategy { impl InformationPlayerStrategy {
fn get_questions<T>(
total_info: u32,
view: &T,
hand_info: &Vec<CardPossibilityTable>,
) -> Vec<Box<Question>>
where T: GameView
{
let mut questions = Vec::new();
let mut info_remaining = total_info;
while info_remaining > 1 {
let mut question = None;
for (i, card_table) in hand_info.iter().enumerate() {
let p = view.get_board().probability_is_playable(card_table);
if (p != 0.0) && (p != 1.0) {
question = Some(Box::new(IsPlayable {index: i}) as Box<Question>);
break;
}
}
if let Some(q) = question {
info_remaining = info_remaining / q.info_amount();
questions.push(q);
} else {
break;
}
}
questions
}
fn answer_questions<T>(
questions: &Vec<Box<Question>>, hand: &Cards, view: &T
) -> ModulusInformation
where T: GameView
{
let mut info = ModulusInformation::none();
for question in questions {
let answer_info = question.answer_info(hand, Box::new(view as &GameView));
info.combine(answer_info);
}
info
}
fn get_hint_info_for_player<T>(
&self, player: &Player, total_info: u32, view: &T
) -> ModulusInformation where T: GameView
{
assert!(player != &self.me);
let hand_info = self.get_player_public_info(player);
let questions = Self::get_questions(total_info, view, hand_info);
trace!("Getting hint for player {}, questions {:?}", player, questions.len());
let mut answer = Self::answer_questions(&questions, view.get_hand(player), view);
answer.cast_up(total_info);
trace!("Resulting answer {:?}", answer);
answer
}
fn get_hint_sum<T>(&self, total_info: u32, view: &T) -> ModulusInformation
where T: GameView
{
let mut sum = ModulusInformation::new(total_info, 0);
for player in view.get_board().get_players() {
if player != self.me {
let answer = self.get_hint_info_for_player(&player, total_info, view);
sum.add(&answer);
}
}
trace!("Summed answer {:?}\n", sum);
sum
}
fn infer_own_from_hint_sum(&mut self, hint: ModulusInformation) {
let mut hint = hint;
let questions = {
let view = &self.last_view;
let hand_info = self.get_my_public_info();
Self::get_questions(hint.modulus, view, hand_info)
};
trace!("{}: Questions {:?}", self.me, questions.len());
let product = questions.iter().fold(1, |a, ref b| a * b.info_amount());
trace!("{}: Product {}, hint: {:?}", self.me, product, hint);
hint.cast_down(product);
trace!("{}: Inferred for myself {:?}", self.me, hint);
let me = self.me.clone();
let mut hand_info = self.take_public_info(&me);
{
let view = &self.last_view;
for question in questions {
let answer_info = hint.emit(question.info_amount());
question.acknowledge_answer_info(answer_info, &mut hand_info, Box::new(view as &GameView));
}
}
self.return_public_info(&me, hand_info);
}
fn update_from_hint_sum(&mut self, hint: ModulusInformation) {
let hinter = self.last_view.board.player;
let players = {
let view = &self.last_view;
view.board.get_players()
};
trace!("{}: inferring for myself, starting with {:?}", self.me, hint);
let mut hint = hint;
for player in players {
if (player != hinter) && (player != self.me) {
{
let view = &self.last_view;
let hint_info = self.get_hint_info_for_player(&player, hint.modulus, view);
hint.subtract(&hint_info);
trace!("{}: subtracted for {}, now {:?}", self.me, player, hint);
}
// *take* instead of borrowing mutably, because of borrow rules...
let mut hand_info = self.take_public_info(&player);
{
let view = &self.last_view;
let hand = view.get_hand(&player);
let questions = Self::get_questions(hint.modulus, view, &mut hand_info);
for question in questions {
let answer = question.answer(hand, Box::new(view as &GameView));
question.acknowledge_answer(answer, &mut hand_info, Box::new(view as &GameView));
}
}
self.return_public_info(&player, hand_info);
}
}
if self.me == hinter {
assert!(hint.value == 0);
} else {
self.infer_own_from_hint_sum(hint);
}
}
// given a hand of cards, represents how badly it will need to play things // 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 { fn hand_play_value(&self, view: &BorrowedGameView, hand: &Cards/*, all_viewable: HashMap<Color, <Value, usize>> */) -> u32 {
// dead = 0 points // dead = 0 points
// indispensible = 5 + (5 - value) points // indispensible = 5 + (5 - value) points
// playable = 1 point // playable = 1 point
@ -112,19 +358,20 @@ impl InformationPlayerStrategy {
value value
} }
fn estimate_hand_play_value(&self, view: &GameStateView) -> u32 { fn estimate_hand_play_value(&self, view: &BorrowedGameView) -> u32 {
// TODO: fix this
0 0
} }
// how badly do we need to play a particular card // how badly do we need to play a particular card
fn get_average_play_score(&self, view: &GameStateView, card_table: &CardPossibilityTable) -> f32 { fn get_average_play_score(&self, view: &BorrowedGameView, card_table: &CardPossibilityTable) -> f32 {
let f = |card: &Card| { let f = |card: &Card| {
self.get_play_score(view, card) as f32 self.get_play_score(view, card) as f32
}; };
card_table.weighted_score(&f) card_table.weighted_score(&f)
} }
fn get_play_score(&self, view: &GameStateView, card: &Card) -> i32 { fn get_play_score(&self, view: &BorrowedGameView, card: &Card) -> i32 {
let my_hand_value = self.estimate_hand_play_value(view); let my_hand_value = self.estimate_hand_play_value(view);
for player in view.board.get_players() { for player in view.board.get_players() {
@ -143,23 +390,25 @@ impl InformationPlayerStrategy {
5 + (5 - (card.value as i32)) 5 + (5 - (card.value as i32))
} }
fn find_useless_card(&self, view: &GameStateView, hand: &Cards) -> Option<usize> { fn find_useless_card(&self, view: &BorrowedGameView, hand: &Vec<CardPossibilityTable>) -> Option<usize> {
let mut set: HashSet<Card> = HashSet::new(); let mut set: HashSet<Card> = HashSet::new();
for (i, card) in hand.iter().enumerate() { for (i, card_table) in hand.iter().enumerate() {
if view.board.is_dead(card) { if view.board.probability_is_dead(card_table) == 1.0 {
return Some(i); return Some(i);
} }
if set.contains(card) { if let Some(card) = card_table.get_card() {
// found a duplicate card if set.contains(&card) {
return Some(i); // found a duplicate card
return Some(i);
}
set.insert(card);
} }
set.insert(card.clone());
} }
return None return None
} }
fn someone_else_can_play(&self, view: &GameStateView) -> bool { fn someone_else_can_play(&self, view: &BorrowedGameView) -> bool {
for player in view.board.get_players() { for player in view.board.get_players() {
if player != self.me { if player != self.me {
for card in view.get_hand(&player) { for card in view.get_hand(&player) {
@ -172,6 +421,18 @@ impl InformationPlayerStrategy {
false false
} }
fn take_public_info(&mut self, player: &Player) -> Vec<CardPossibilityTable> {
self.public_info.remove(player).unwrap()
}
fn return_public_info(&mut self, player: &Player, card_info: Vec<CardPossibilityTable>) {
self.public_info.insert(*player, card_info);
}
fn get_my_public_info(&self) -> &Vec<CardPossibilityTable> {
self.get_player_public_info(&self.me)
}
fn get_player_public_info(&self, player: &Player) -> &Vec<CardPossibilityTable> { fn get_player_public_info(&self, player: &Player) -> &Vec<CardPossibilityTable> {
self.public_info.get(player).unwrap() self.public_info.get(player).unwrap()
} }
@ -200,7 +461,7 @@ impl InformationPlayerStrategy {
fn update_public_info_for_discard_or_play( fn update_public_info_for_discard_or_play(
&mut self, &mut self,
view: &GameStateView, view: &BorrowedGameView,
player: &Player, player: &Player,
index: usize, index: usize,
card: &Card card: &Card
@ -212,7 +473,7 @@ impl InformationPlayerStrategy {
info.remove(index); info.remove(index);
// push *before* incrementing public counts // push *before* incrementing public counts
if info.len() < view.info.len() { if info.len() < view.hand_size(&player) {
info.push(new_card_table); info.push(new_card_table);
} }
} }
@ -220,7 +481,7 @@ impl InformationPlayerStrategy {
// note: other_player could be player, as well // note: other_player could be player, as well
// in particular, we will decrement the newly drawn card // in particular, we will decrement the newly drawn card
for other_player in view.board.get_players() { for other_player in view.board.get_players() {
let mut info = self.get_player_public_info_mut(&other_player); let info = self.get_player_public_info_mut(&other_player);
for card_table in info { for card_table in info {
card_table.decrement_weight_if_possible(card); card_table.decrement_weight_if_possible(card);
} }
@ -229,10 +490,10 @@ impl InformationPlayerStrategy {
self.public_counts.increment(card); self.public_counts.increment(card);
} }
fn get_private_info(&self, view: &GameStateView) -> Vec<CardPossibilityTable> { fn get_private_info(&self, view: &BorrowedGameView) -> Vec<CardPossibilityTable> {
let mut info = self.get_player_public_info(&self.me).clone(); let mut info = self.get_my_public_info().clone();
for card_table in info.iter_mut() { for card_table in info.iter_mut() {
for (other_player, state) in &view.other_player_states { for (_, state) in &view.other_player_states {
for card in &state.hand { for card in &state.hand {
card_table.decrement_weight_if_possible(card); card_table.decrement_weight_if_possible(card);
} }
@ -241,9 +502,81 @@ impl InformationPlayerStrategy {
info info
} }
fn get_hint(&self, view: &BorrowedGameView) -> TurnChoice {
let total_info = 3 * (view.board.num_players - 1);
let hint_info = self.get_hint_sum(total_info, view);
let hint_type = hint_info.value % 3;
let player_amt = (hint_info.value - hint_type) / 3;
let hint_player = (self.me + 1 + player_amt) % view.board.num_players;
let card_index = 0;
let hand = view.get_hand(&hint_player);
let hint_card = &hand[card_index];
let hinted = match hint_type {
0 => {
Hinted::Value(hint_card.value)
}
1 => {
Hinted::Color(hint_card.color)
}
2 => {
let mut hinted_opt = None;
for card in hand {
if card.color != hint_card.color {
hinted_opt = Some(Hinted::Color(card.color));
break;
}
if card.value != hint_card.value {
hinted_opt = Some(Hinted::Value(card.value));
break;
}
}
if let Some(hinted) = hinted_opt {
hinted
} else {
panic!("Found nothing to hint!")
}
}
_ => {
panic!("Invalid hint type")
}
};
TurnChoice::Hint(Hint {
player: hint_player,
hinted: hinted,
})
}
fn infer_from_hint(&mut self, view: &BorrowedGameView, hint: &Hint, result: &Vec<bool>) {
let total_info = 3 * (view.board.num_players - 1);
let hinter = self.last_view.board.player;
let player_amt = (view.board.num_players + hint.player - hinter - 1) % view.board.num_players;
let hint_type = if result[0] {
match hint.hinted {
Hinted::Value(_) => 0,
Hinted::Color(_) => 1,
}
} else {
2
};
let hint_value = player_amt * 3 + hint_type;
let mod_info = ModulusInformation::new(total_info, hint_value);
self.update_from_hint_sum(mod_info);
}
} }
impl PlayerStrategy for InformationPlayerStrategy { impl PlayerStrategy for InformationPlayerStrategy {
fn decide(&mut self, view: &GameStateView) -> TurnChoice { fn decide(&mut self, view: &BorrowedGameView) -> TurnChoice {
let private_info = self.get_private_info(view); let private_info = self.get_private_info(view);
// debug!("My info:"); // debug!("My info:");
// for (i, card_table) in private_info.iter().enumerate() { // for (i, card_table) in private_info.iter().enumerate() {
@ -268,48 +601,33 @@ impl PlayerStrategy for InformationPlayerStrategy {
} }
} }
TurnChoice::Play(play_index) return TurnChoice::Play(play_index)
} else { }
if view.board.hints_remaining > 0 {
let hint_player = view.board.player_to_left(&self.me); let discard_threshold =
let hint_card = rand::thread_rng().choose(&view.get_hand(&hint_player)).unwrap(); view.board.total_cards
let hinted = { - (COLORS.len() * VALUES.len()) as u32
if rand::random() { - (view.board.num_players * view.board.hand_size);
// hint a color
Hinted::Color(hint_card.color) if view.board.discard_size() <= discard_threshold {
} else { // if anything is totally useless, discard it
Hinted::Value(hint_card.value) if let Some(i) = self.find_useless_card(view, &private_info) {
} return TurnChoice::Discard(i);
};
TurnChoice::Hint(Hint {
player: hint_player,
hinted: hinted,
})
} else {
TurnChoice::Discard(0)
} }
} }
// // 50 total, 25 to play, 20 in hand // hinting is better than discarding dead cards
// if view.board.discard.cards.len() < 6 { // (probably because it stalls the deck-drawing).
// // if anything is totally useless, discard it if view.board.hints_remaining > 1 {
// if let Some(i) = self.find_useless_card(view) { if self.someone_else_can_play(view) {
// return TurnChoice::Discard(i); return self.get_hint(view);
// } }
// } }
// // hinting is better than discarding dead cards // if anything is totally useless, discard it
// // (probably because it stalls the deck-drawing). if let Some(i) = self.find_useless_card(view, &private_info) {
// if view.board.hints_remaining > 1 { return TurnChoice::Discard(i);
// 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. // // 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
@ -343,12 +661,19 @@ impl PlayerStrategy for InformationPlayerStrategy {
// panic!("This shouldn't happen! No discardable card"); // panic!("This shouldn't happen! No discardable card");
// } // }
// } // }
if view.board.hints_remaining > 0 {
self.get_hint(view)
} else {
TurnChoice::Discard(0)
}
} }
fn update(&mut self, turn: &Turn, view: &GameStateView) { fn update(&mut self, turn: &Turn, view: &BorrowedGameView) {
match turn.choice { match turn.choice {
TurnChoice::Hint(ref hint) => { TurnChoice::Hint(ref hint) => {
if let &TurnResult::Hint(ref matches) = &turn.result { if let &TurnResult::Hint(ref matches) = &turn.result {
self.infer_from_hint(view, hint, matches);
self.update_public_info_for_hint(hint, matches); self.update_public_info_for_hint(hint, matches);
} else { } else {
panic!("Got turn choice {:?}, but turn result {:?}", panic!("Got turn choice {:?}, but turn result {:?}",
@ -364,7 +689,7 @@ impl PlayerStrategy for InformationPlayerStrategy {
} }
} }
TurnChoice::Play(index) => { TurnChoice::Play(index) => {
if let &TurnResult::Play(ref card, played) = &turn.result { if let &TurnResult::Play(ref card, _) = &turn.result {
self.update_public_info_for_discard_or_play(view, &turn.player, index, card); self.update_public_info_for_discard_or_play(view, &turn.player, index, card);
} else { } else {
panic!("Got turn choice {:?}, but turn result {:?}", panic!("Got turn choice {:?}, but turn result {:?}",
@ -372,5 +697,6 @@ impl PlayerStrategy for InformationPlayerStrategy {
} }
} }
} }
self.last_view = OwnedGameView::clone_from(view);
} }
} }