allow for probabilities in cardinfo

- helper functions for using probabilistic card info
- move firework to cards, re-export everything in game.rs
This commit is contained in:
Jeff Wu 2016-03-23 10:37:35 -07:00
parent 9c580ecb88
commit 4fa72e030f
4 changed files with 147 additions and 90 deletions

View file

@ -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<Value> {
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)
}
}
}

View file

@ -2,12 +2,11 @@ use rand::{self, Rng, SeedableRng};
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt; use std::fmt;
use info::*; pub use info::*;
use cards::*; pub use cards::*;
pub type Player = u32; pub type Player = u32;
#[derive(Debug,Clone)] #[derive(Debug,Clone)]
pub enum Hinted { pub enum Hinted {
Color(Color), Color(Color),
@ -292,6 +291,35 @@ 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 {
self.probability_of_predicate(card_info, &Self::is_playable)
}
pub fn probability_is_dead<T>(&self, card_info: &T) -> f32 where T: CardInfo {
self.probability_of_predicate(card_info, &Self::is_dead)
}
pub fn probability_is_dispensable<T>(&self, card_info: &T) -> f32 where T: CardInfo {
self.probability_of_predicate(card_info, &Self::is_dispensable)
}
pub fn get_players(&self) -> Vec<Player> { pub fn get_players(&self) -> Vec<Player> {
(0..self.num_players).collect::<Vec<_>>() (0..self.num_players).collect::<Vec<_>>()
} }
@ -422,8 +450,6 @@ impl fmt::Display for GameState {
} }
} }
pub type Score = u32;
impl GameState { impl GameState {
pub fn new(opts: &GameOptions, seed: u32) -> GameState { pub fn new(opts: &GameOptions, seed: u32) -> GameState {
let mut board = BoardState::new(opts, seed); let mut board = BoardState::new(opts, seed);
@ -569,51 +595,3 @@ impl GameState {
turn 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<Value> {
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)
}
}
}

View file

@ -5,6 +5,7 @@ use std::hash::Hash;
use cards::*; use cards::*;
// trait representing information about a card
pub trait CardInfo { pub trait CardInfo {
// get all a-priori possibilities // get all a-priori possibilities
fn get_all_possibilities(&self) -> Vec<Card> { fn get_all_possibilities(&self) -> Vec<Card> {
@ -16,8 +17,37 @@ pub trait CardInfo {
} }
v 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 // mark all current possibilities for the card
fn get_possibilities(&self) -> Vec<Card>; fn get_possibilities(&self) -> Vec<Card> {
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 // mark a whole color as false
fn mark_color_false(&mut self, color: &Color); fn mark_color_false(&mut self, color: &Color);
@ -64,47 +94,34 @@ pub trait Info<T> where T: Hash + Eq + Clone {
// get map from values to whether it's possible // get map from values to whether it's possible
// true means maybe, false means no // true means maybe, false means no
fn get_possibility_map(&self) -> &HashMap<T, bool>; fn get_possibility_set(&self) -> &HashSet<T>;
fn get_mut_possibility_map(&mut self) -> &mut HashMap<T, bool>; fn get_mut_possibility_set(&mut self) -> &mut HashSet<T>;
// get what is now possible // get what is now possible
fn get_possibilities(&self) -> Vec<T> { fn get_possibilities(&self) -> Vec<T> {
let mut v = Vec::new(); self.get_possibility_set().iter().map(|t| t.clone()).collect::<Vec<T>>()
let map = self.get_possibility_map();
for (value, is_possible) in map {
if *is_possible {
v.push(value.clone());
}
}
v
} }
fn is_possible(&self, value: &T) -> bool { fn is_possible(&self, value: &T) -> bool {
// self.get_possibility_map().contains_key(value) self.get_possibility_set().contains(value)
*self.get_possibility_map().get(value).unwrap()
} }
fn initialize() -> HashMap<T, bool> { fn initialize() -> HashSet<T> {
let mut possible_map : HashMap<T, bool> = HashMap::new(); let mut possible_map : HashSet<T> = HashSet::new();
for value in Self::get_all_possibilities().iter() { for value in Self::get_all_possibilities().iter() {
possible_map.insert(value.clone(), true); possible_map.insert(value.clone());
} }
possible_map possible_map
} }
fn mark_true(&mut self, value: &T) { fn mark_true(&mut self, value: &T) {
// mark everything else as definitively impossible let possible = self.get_mut_possibility_set();
for (other_value, possible) in self.get_mut_possibility_map().iter_mut() { possible.clear();
if other_value != value { possible.insert(value.clone());
*possible = false;
} else {
assert_eq!(*possible, true);
}
}
} }
fn mark_false(&mut self, value: &T) { 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) { fn mark(&mut self, value: &T, info: bool) {
@ -117,25 +134,25 @@ pub trait Info<T> where T: Hash + Eq + Clone {
} }
#[derive(Debug)] #[derive(Debug)]
pub struct ColorInfo(HashMap<Color, bool>); pub struct ColorInfo(HashSet<Color>);
impl ColorInfo { impl ColorInfo {
pub fn new() -> ColorInfo { ColorInfo(ColorInfo::initialize()) } pub fn new() -> ColorInfo { ColorInfo(ColorInfo::initialize()) }
} }
impl Info<Color> for ColorInfo { impl Info<Color> for ColorInfo {
fn get_all_possibilities() -> Vec<Color> { COLORS.to_vec() } fn get_all_possibilities() -> Vec<Color> { COLORS.to_vec() }
fn get_possibility_map(&self) -> &HashMap<Color, bool> { &self.0 } fn get_possibility_set(&self) -> &HashSet<Color> { &self.0 }
fn get_mut_possibility_map(&mut self) -> &mut HashMap<Color, bool> { &mut self.0 } fn get_mut_possibility_set(&mut self) -> &mut HashSet<Color> { &mut self.0 }
} }
#[derive(Debug)] #[derive(Debug)]
pub struct ValueInfo(HashMap<Value, bool>); pub struct ValueInfo(HashSet<Value>);
impl ValueInfo { impl ValueInfo {
pub fn new() -> ValueInfo { ValueInfo(ValueInfo::initialize()) } pub fn new() -> ValueInfo { ValueInfo(ValueInfo::initialize()) }
} }
impl Info<Value> for ValueInfo { impl Info<Value> for ValueInfo {
fn get_all_possibilities() -> Vec<Value> { VALUES.to_vec() } fn get_all_possibilities() -> Vec<Value> { VALUES.to_vec() }
fn get_possibility_map(&self) -> &HashMap<Value, bool> { &self.0 } fn get_possibility_set(&self) -> &HashSet<Value> { &self.0 }
fn get_mut_possibility_map(&mut self) -> &mut HashMap<Value, bool> { &mut self.0 } fn get_mut_possibility_set(&mut self) -> &mut HashSet<Value> { &mut self.0 }
} }
// represents information only of the form: // represents information only of the form:
@ -163,6 +180,11 @@ impl CardInfo for SimpleCardInfo {
} }
v 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) { fn mark_color_false(&mut self, color: &Color) {
self.color_info.mark_false(color); self.color_info.mark_false(color);
@ -193,16 +215,20 @@ 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
#[derive(Clone)] #[derive(Clone)]
struct CardPossibilityTable { pub struct CardPossibilityTable {
possible: HashSet<Card>, possible: HashMap<Card, u32>,
} }
impl CardPossibilityTable { impl CardPossibilityTable {
pub fn new() -> CardPossibilityTable { pub fn new() -> CardPossibilityTable {
let mut possible = HashSet::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(Card::new(color, value)); possible.insert(
Card::new(color, value),
get_count_for_value(&value)
);
} }
} }
CardPossibilityTable { CardPossibilityTable {
@ -216,8 +242,11 @@ impl CardPossibilityTable {
} }
} }
impl CardInfo for CardPossibilityTable { impl CardInfo for CardPossibilityTable {
fn is_possible(&self, card: &Card) -> bool {
self.possible.contains_key(card)
}
fn get_possibilities(&self) -> Vec<Card> { fn get_possibilities(&self) -> Vec<Card> {
let mut cards = self.possible.iter().map(|card| {card.clone() }).collect::<Vec<_>>(); let mut cards = self.possible.keys().map(|card| {card.clone() }).collect::<Vec<_>>();
cards.sort(); cards.sort();
cards cards
} }
@ -232,6 +261,9 @@ 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 {
*self.possible.get(card).unwrap_or(&0)
}
} }
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 {

View file

@ -4,7 +4,6 @@ use std::collections::{HashMap, HashSet};
use simulator::*; use simulator::*;
use game::*; use game::*;
use cards::*;
// strategy that explicitly cheats by using Rc/RefCell // strategy that explicitly cheats by using Rc/RefCell
// serves as a reference point for other strategies // serves as a reference point for other strategies