diff --git a/src/cards.rs b/src/cards.rs index 17be76d..c27b1fa 100644 --- a/src/cards.rs +++ b/src/cards.rs @@ -131,3 +131,51 @@ impl fmt::Display for Discard { } } +pub type Score = u32; + +#[derive(Debug,Clone)] +pub struct Firework { + pub color: Color, + pub top: Value, +} +impl Firework { + pub fn new(color: Color) -> Firework { + Firework { + color: color, + top: 0, + } + } + + pub fn desired_value(&self) -> Option { + if self.complete() { None } else { Some(self.top + 1) } + } + + pub fn score(&self) -> Score { + self.top + } + + pub fn complete(&self) -> bool { + self.top == FINAL_VALUE + } + + pub fn place(&mut self, card: &Card) { + assert!( + card.color == self.color, + "Attempted to place card on firework of wrong color!" + ); + assert!( + Some(card.value) == self.desired_value(), + "Attempted to place card of wrong value on firework!" + ); + self.top = card.value; + } +} +impl fmt::Display for Firework { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + if self.complete() { + write!(f, "{} firework complete!", self.color) + } else { + write!(f, "{} firework at {}", self.color, self.top) + } + } +} diff --git a/src/game.rs b/src/game.rs index 4600a65..aea465c 100644 --- a/src/game.rs +++ b/src/game.rs @@ -2,12 +2,11 @@ use rand::{self, Rng, SeedableRng}; use std::collections::HashMap; use std::fmt; -use info::*; -use cards::*; +pub use info::*; +pub use cards::*; pub type Player = u32; - #[derive(Debug,Clone)] pub enum Hinted { Color(Color), @@ -292,6 +291,35 @@ impl BoardState { } } + fn probability_of_predicate( + &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(&self, card_info: &T) -> f32 where T: CardInfo { + self.probability_of_predicate(card_info, &Self::is_playable) + } + + pub fn probability_is_dead(&self, card_info: &T) -> f32 where T: CardInfo { + self.probability_of_predicate(card_info, &Self::is_dead) + } + + pub fn probability_is_dispensable(&self, card_info: &T) -> f32 where T: CardInfo { + self.probability_of_predicate(card_info, &Self::is_dispensable) + } + pub fn get_players(&self) -> Vec { (0..self.num_players).collect::>() } @@ -422,8 +450,6 @@ impl fmt::Display for GameState { } } -pub type Score = u32; - impl GameState { pub fn new(opts: &GameOptions, seed: u32) -> GameState { let mut board = BoardState::new(opts, seed); @@ -569,51 +595,3 @@ impl GameState { turn } } - -#[derive(Debug,Clone)] -pub struct Firework { - pub color: Color, - pub top: Value, -} -impl Firework { - pub fn new(color: Color) -> Firework { - Firework { - color: color, - top: 0, - } - } - - fn desired_value(&self) -> Option { - if self.complete() { None } else { Some(self.top + 1) } - } - - fn score(&self) -> Score { - self.top - } - - fn complete(&self) -> bool { - self.top == FINAL_VALUE - } - - fn place(&mut self, card: &Card) { - assert!( - card.color == self.color, - "Attempted to place card on firework of wrong color!" - ); - assert!( - Some(card.value) == self.desired_value(), - "Attempted to place card of wrong value on firework!" - ); - self.top = card.value; - } -} -impl fmt::Display for Firework { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - if self.complete() { - write!(f, "{} firework complete!", self.color) - } else { - write!(f, "{} firework at {}", self.color, self.top) - } - } -} - diff --git a/src/info.rs b/src/info.rs index 00d09ad..da9e53f 100644 --- a/src/info.rs +++ b/src/info.rs @@ -5,6 +5,7 @@ use std::hash::Hash; use cards::*; +// trait representing information about a card pub trait CardInfo { // get all a-priori possibilities fn get_all_possibilities(&self) -> Vec { @@ -16,8 +17,37 @@ pub trait CardInfo { } v } + // whether the card is possible + fn is_possible(&self, card: &Card) -> bool; + + // TODO: have a borrow_possibilities to allow for more efficiency? + // mark all current possibilities for the card - fn get_possibilities(&self) -> Vec; + fn get_possibilities(&self) -> Vec { + let mut v = Vec::new(); + for &color in COLORS.iter() { + for &value in VALUES.iter() { + let card = Card::new(color, value); + if self.is_possible(&card) { + v.push(card); + } + } + } + v + } + // get probability weight for the card + #[allow(unused_variables)] + fn get_weight(&self, card: &Card) -> u32 { + 1 + } + fn get_weighted_possibilities(&self) -> Vec<(Card, u32)> { + let mut v = Vec::new(); + for card in self.get_possibilities() { + let weight = self.get_weight(&card); + v.push((card, weight)); + } + v + } // mark a whole color as false fn mark_color_false(&mut self, color: &Color); @@ -64,47 +94,34 @@ pub trait Info where T: Hash + Eq + Clone { // get map from values to whether it's possible // true means maybe, false means no - fn get_possibility_map(&self) -> &HashMap; - fn get_mut_possibility_map(&mut self) -> &mut HashMap; + fn get_possibility_set(&self) -> &HashSet; + fn get_mut_possibility_set(&mut self) -> &mut HashSet; // get what is now possible fn get_possibilities(&self) -> Vec { - let mut v = Vec::new(); - let map = self.get_possibility_map(); - for (value, is_possible) in map { - if *is_possible { - v.push(value.clone()); - } - } - v + self.get_possibility_set().iter().map(|t| t.clone()).collect::>() } fn is_possible(&self, value: &T) -> bool { - // self.get_possibility_map().contains_key(value) - *self.get_possibility_map().get(value).unwrap() + self.get_possibility_set().contains(value) } - fn initialize() -> HashMap { - let mut possible_map : HashMap = HashMap::new(); + fn initialize() -> HashSet { + let mut possible_map : HashSet = HashSet::new(); for value in Self::get_all_possibilities().iter() { - possible_map.insert(value.clone(), true); + possible_map.insert(value.clone()); } possible_map } fn mark_true(&mut self, value: &T) { - // mark everything else as definitively impossible - for (other_value, possible) in self.get_mut_possibility_map().iter_mut() { - if other_value != value { - *possible = false; - } else { - assert_eq!(*possible, true); - } - } + let possible = self.get_mut_possibility_set(); + possible.clear(); + possible.insert(value.clone()); } fn mark_false(&mut self, value: &T) { - self.get_mut_possibility_map().insert(value.clone(), false); + self.get_mut_possibility_set().remove(value); } fn mark(&mut self, value: &T, info: bool) { @@ -117,25 +134,25 @@ pub trait Info where T: Hash + Eq + Clone { } #[derive(Debug)] -pub struct ColorInfo(HashMap); +pub struct ColorInfo(HashSet); impl ColorInfo { pub fn new() -> ColorInfo { ColorInfo(ColorInfo::initialize()) } } impl Info for ColorInfo { fn get_all_possibilities() -> Vec { COLORS.to_vec() } - fn get_possibility_map(&self) -> &HashMap { &self.0 } - fn get_mut_possibility_map(&mut self) -> &mut HashMap { &mut self.0 } + fn get_possibility_set(&self) -> &HashSet { &self.0 } + fn get_mut_possibility_set(&mut self) -> &mut HashSet { &mut self.0 } } #[derive(Debug)] -pub struct ValueInfo(HashMap); +pub struct ValueInfo(HashSet); impl ValueInfo { pub fn new() -> ValueInfo { ValueInfo(ValueInfo::initialize()) } } impl Info for ValueInfo { fn get_all_possibilities() -> Vec { VALUES.to_vec() } - fn get_possibility_map(&self) -> &HashMap { &self.0 } - fn get_mut_possibility_map(&mut self) -> &mut HashMap { &mut self.0 } + fn get_possibility_set(&self) -> &HashSet { &self.0 } + fn get_mut_possibility_set(&mut self) -> &mut HashSet { &mut self.0 } } // represents information only of the form: @@ -163,6 +180,11 @@ impl CardInfo for SimpleCardInfo { } v } + fn is_possible(&self, card: &Card) -> bool { + self.color_info.is_possible(&card.color) && + self.value_info.is_possible(&card.value) + + } fn mark_color_false(&mut self, color: &Color) { self.color_info.mark_false(color); @@ -193,16 +215,20 @@ impl fmt::Display for SimpleCardInfo { // Can represent information of the form: // this card is/isn't possible +// also, maintains weights for the cards #[derive(Clone)] -struct CardPossibilityTable { - possible: HashSet, +pub struct CardPossibilityTable { + possible: HashMap, } impl CardPossibilityTable { pub fn new() -> CardPossibilityTable { - let mut possible = HashSet::new(); + let mut possible = HashMap::new(); for &color in COLORS.iter() { for &value in VALUES.iter() { - possible.insert(Card::new(color, value)); + possible.insert( + Card::new(color, value), + get_count_for_value(&value) + ); } } CardPossibilityTable { @@ -216,8 +242,11 @@ impl CardPossibilityTable { } } impl CardInfo for CardPossibilityTable { + fn is_possible(&self, card: &Card) -> bool { + self.possible.contains_key(card) + } fn get_possibilities(&self) -> Vec { - let mut cards = self.possible.iter().map(|card| {card.clone() }).collect::>(); + let mut cards = self.possible.keys().map(|card| {card.clone() }).collect::>(); cards.sort(); cards } @@ -232,6 +261,9 @@ impl CardInfo for CardPossibilityTable { self.mark_false(&Card::new(color, value.clone())); } } + fn get_weight(&self, card: &Card) -> u32 { + *self.possible.get(card).unwrap_or(&0) + } } impl fmt::Display for CardPossibilityTable { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { diff --git a/src/strategies/cheating.rs b/src/strategies/cheating.rs index 897257d..8bc6aaf 100644 --- a/src/strategies/cheating.rs +++ b/src/strategies/cheating.rs @@ -4,7 +4,6 @@ use std::collections::{HashMap, HashSet}; use simulator::*; use game::*; -use cards::*; // strategy that explicitly cheats by using Rc/RefCell // serves as a reference point for other strategies