diff --git a/src/cards.rs b/src/cards.rs new file mode 100644 index 0000000..17be76d --- /dev/null +++ b/src/cards.rs @@ -0,0 +1,133 @@ +use std::collections::HashMap; +use std::fmt; + +pub type Color = &'static str; +pub const COLORS: [Color; 5] = ["red", "yellow", "green", "blue", "white"]; +pub fn display_color(color: Color) -> char { + color.chars().next().unwrap() +} + +pub type Value = u32; +// list of values, assumed to be small to large +pub const VALUES : [Value; 5] = [1, 2, 3, 4, 5]; +pub const FINAL_VALUE : Value = 5; + +pub fn get_count_for_value(value: &Value) -> u32 { + match *value { + 1 => 3, + 2 | 3 | 4 => 2, + 5 => 1, + _ => { panic!(format!("Unexpected value: {}", value)); } + } +} + +#[derive(Debug,Clone,PartialEq,Eq,Hash,Ord,PartialOrd)] +pub struct Card { + pub color: Color, + pub value: Value, +} +impl Card { + pub fn new(color: Color, value: Value) -> Card { + Card { color: color, value: value } + } +} +impl fmt::Display for Card { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}{}", display_color(self.color), self.value) + } +} + +pub type Cards = Vec; + +#[derive(Debug,Clone)] +pub struct CardCounts { + counts: HashMap, +} +impl CardCounts { + pub fn new() -> CardCounts { + let mut counts = HashMap::new(); + for color in COLORS.iter() { + for value in VALUES.iter() { + counts.insert(Card::new(*color, *value), 0); + } + } + CardCounts { + counts: counts, + } + } + + pub fn get_count(&self, card: &Card) -> u32 { + *self.counts.get(card).unwrap() + } + + pub fn remaining(&self, card: &Card) -> u32 { + let count = self.get_count(card); + get_count_for_value(&card.value) - count + } + + pub fn add(&mut self, card: &Card) { + let count = self.counts.get_mut(card).unwrap(); + *count += 1; + } +} +impl fmt::Display for CardCounts { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + for color in COLORS.iter() { + try!(f.write_str(&format!( + "{}: ", display_color(color), + ))); + for value in VALUES.iter() { + let count = self.get_count(&Card::new(color, *value)); + let total = get_count_for_value(value); + try!(f.write_str(&format!( + "{}/{} {}s", count, total, value + ))); + if *value != FINAL_VALUE { + try!(f.write_str(", ")); + } + } + try!(f.write_str("\n")); + } + Ok(()) + } +} + +#[derive(Debug)] +pub struct Discard { + pub cards: Cards, + counts: CardCounts, +} +impl Discard { + pub fn new() -> Discard { + Discard { + cards: Cards::new(), + counts: CardCounts::new(), + } + } + + pub fn get_count(&self, card: &Card) -> u32 { + self.counts.get_count(card) + } + + pub fn has_all(&self, card: &Card) -> bool { + self.counts.remaining(card) == 0 + } + + pub fn remaining(&self, card: &Card) -> u32 { + self.counts.remaining(card) + } + + pub fn place(&mut self, card: Card) { + self.counts.add(&card); + self.cards.push(card); + } +} +impl fmt::Display for Discard { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + // try!(f.write_str(&format!( + // "{}", self.cards, + // ))); + write!(f, "{}", self.counts) + } +} + diff --git a/src/game.rs b/src/game.rs index a9c44b5..4600a65 100644 --- a/src/game.rs +++ b/src/game.rs @@ -3,165 +3,10 @@ use std::collections::HashMap; use std::fmt; use info::*; - -/* -* Type definitions -*/ - -pub type Color = &'static str; -pub const COLORS: [Color; 5] = ["red", "yellow", "green", "blue", "white"]; -pub fn display_color(color: Color) -> char { - color.chars().next().unwrap() -} - -pub type Value = u32; -// list of values, assumed to be small to large -pub const VALUES : [Value; 5] = [1, 2, 3, 4, 5]; -pub const FINAL_VALUE : Value = 5; - -pub fn get_count_for_value(value: &Value) -> u32 { - match *value { - 1 => 3, - 2 | 3 | 4 => 2, - 5 => 1, - _ => { panic!(format!("Unexpected value: {}", value)); } - } -} +use cards::*; pub type Player = u32; -#[derive(Debug,Clone,PartialEq,Eq,Hash)] -pub struct Card { - pub color: Color, - pub value: Value, -} -impl Card { - fn new(color: Color, value: Value) -> Card { - Card { color: color, value: value } - } -} -impl fmt::Display for Card { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}{}", display_color(self.color), self.value) - } -} - -pub type Cards = Vec; -pub type CardsInfo = Vec; - -#[derive(Debug,Clone)] -pub struct Firework { - pub color: Color, - pub top: Value, -} -impl Firework { - 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) - } - } -} - -#[derive(Debug)] -pub struct Discard { - pub cards: Cards, - counts: HashMap>, -} -impl Discard { - fn new() -> Discard { - let mut counts = HashMap::new(); - for color in COLORS.iter() { - let mut color_count = HashMap::new(); - for value in VALUES.iter() { - color_count.insert(*value, 0); - } - counts.insert(*color, color_count); - } - Discard { - cards: Cards::new(), - counts: counts, - } - } - - fn get_count(&self, card: &Card) -> u32 { - let color_count = self.counts.get(card.color).unwrap(); - color_count.get(&card.value).unwrap().clone() - } - - fn has_all(&self, card: &Card) -> bool { - self.remaining(card) == 0 - } - - fn remaining(&self, card: &Card) -> u32 { - let count = self.get_count(&card); - get_count_for_value(&card.value) - count - } - - fn place(&mut self, card: Card) { - let count = self.get_count(&card); - let ref mut color_count = self.counts.get_mut(card.color).unwrap(); - color_count.insert(card.value, count + 1); - self.cards.push(card); - } -} -impl fmt::Display for Discard { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - // try!(f.write_str(&format!( - // "{}", self.cards, - // ))); - for color in COLORS.iter() { - try!(f.write_str(&format!( - "{}: ", display_color(color), - ))); - for value in VALUES.iter() { - let count = self.get_count(&Card::new(color, *value)); - let total = get_count_for_value(value); - try!(f.write_str(&format!( - "{}/{} {}s", count, total, value - ))); - if *value != FINAL_VALUE { - try!(f.write_str(", ")); - } - } - try!(f.write_str("\n")); - } - Ok(()) - } -} #[derive(Debug,Clone)] pub enum Hinted { @@ -225,7 +70,7 @@ pub struct PlayerState { // the player's actual hand pub hand: Cards, // represents what is common knowledge about the player's hand - pub info: CardsInfo, + pub info: Vec, } impl fmt::Display for PlayerState { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { @@ -233,7 +78,7 @@ impl fmt::Display for PlayerState { let mut i = 0; for card in &self.hand { - let info : &CardInfo = &self.info[i]; + let info : &SimpleCardInfo = &self.info[i]; try!(f.write_str(&format!("{} =? {: <15} ", card, info))); i += 1; } @@ -243,7 +88,7 @@ impl fmt::Display for PlayerState { impl PlayerState { pub fn new(hand: Cards) -> PlayerState { let infos = (0..hand.len()).map(|_| { - CardInfo::new() + SimpleCardInfo::new() }).collect::>(); PlayerState { hand: hand, @@ -251,7 +96,7 @@ impl PlayerState { } } - pub fn take(&mut self, index: usize) -> (Card, CardInfo) { + pub fn take(&mut self, index: usize) -> (Card, SimpleCardInfo) { let card = self.hand.remove(index); let info = self.info.remove(index); (card, info) @@ -259,7 +104,7 @@ impl PlayerState { pub fn place(&mut self, card: Card) { self.hand.push(card); - self.info.push(CardInfo::new()); + self.info.push(SimpleCardInfo::new()); } pub fn reveal(&mut self, hinted: &Hinted) -> Vec { @@ -269,7 +114,7 @@ impl PlayerState { let mut i = 0; for card in &self.hand { let matches = card.color == *color; - self.info[i].color_info.mark(color, matches); + self.info[i].mark_color(color, matches); if matches { indices.push(i); } i += 1; } @@ -278,7 +123,7 @@ impl PlayerState { let mut i = 0; for card in &self.hand { let matches = card.value == *value; - self.info[i].value_info.mark(value, matches); + self.info[i].mark_value(value, matches); if matches { indices.push(i); } i += 1; } @@ -368,7 +213,7 @@ impl BoardState { } } - fn get_firework(&self, color: &Color) -> &Firework { + pub fn get_firework(&self, color: &Color) -> &Firework { self.fireworks.get(color).unwrap() } @@ -523,7 +368,7 @@ pub struct GameStateView<'a> { // the player whose view it is pub player: Player, // what is known about their own hand (and thus common knowledge) - pub info: &'a CardsInfo, + pub info: &'a Vec, // the cards of the other players, as well as the information they have pub other_player_states: HashMap, // board state @@ -724,3 +569,51 @@ 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 b7c3e69..00d09ad 100644 --- a/src/info.rs +++ b/src/info.rs @@ -1,9 +1,61 @@ use std::cmp::Eq; -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use std::fmt; use std::hash::Hash; -use game::*; +use cards::*; + +pub trait CardInfo { + // get all a-priori possibilities + fn get_all_possibilities(&self) -> Vec { + let mut v = Vec::new(); + for &color in COLORS.iter() { + for &value in VALUES.iter() { + v.push(Card::new(color, value)); + } + } + v + } + // mark all current possibilities for the card + fn get_possibilities(&self) -> Vec; + + // mark a whole color as false + fn mark_color_false(&mut self, color: &Color); + // mark a color as correct + fn mark_color_true(&mut self, color: &Color) { + for other_color in COLORS.iter() { + if other_color != color { + self.mark_color_false(other_color); + } + } + } + fn mark_color(&mut self, color: &Color, is_color: bool) { + if is_color { + self.mark_color_true(color); + } else { + self.mark_color_false(color); + } + } + + // mark a whole value as false + fn mark_value_false(&mut self, value: &Value); + // mark a value as correct + fn mark_value_true(&mut self, value: &Value) { + for other_value in VALUES.iter() { + if other_value != value { + self.mark_value_false(other_value); + } + } + } + fn mark_value(&mut self, value: &Value, is_value: bool) { + if is_value { + self.mark_value_true(value); + } else { + self.mark_value_false(value); + } + } +} + // Represents hinted information about possible values of type T pub trait Info where T: Hash + Eq + Clone { @@ -16,12 +68,12 @@ pub trait Info where T: Hash + Eq + Clone { fn get_mut_possibility_map(&mut self) -> &mut HashMap; // get what is now possible - fn get_possibilities(&self) -> Vec<&T> { + 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); + v.push(value.clone()); } } v @@ -86,20 +138,40 @@ impl Info for ValueInfo { fn get_mut_possibility_map(&mut self) -> &mut HashMap { &mut self.0 } } +// represents information only of the form: +// this color is/isn't possible, this value is/isn't possible #[derive(Debug)] -pub struct CardInfo { +pub struct SimpleCardInfo { pub color_info: ColorInfo, pub value_info: ValueInfo, } -impl CardInfo { - pub fn new() -> CardInfo { - CardInfo { +impl SimpleCardInfo { + pub fn new() -> SimpleCardInfo { + SimpleCardInfo { color_info: ColorInfo::new(), value_info: ValueInfo::new(), } } } -impl fmt::Display for CardInfo { +impl CardInfo for SimpleCardInfo { + fn get_possibilities(&self) -> Vec { + let mut v = Vec::new(); + for &color in self.color_info.get_possibilities().iter() { + for &value in self.value_info.get_possibilities().iter() { + v.push(Card::new(color, value)); + } + } + v + } + fn mark_color_false(&mut self, color: &Color) { + self.color_info.mark_false(color); + + } + fn mark_value_false(&mut self, value: &Value) { + self.value_info.mark_false(value); + } +} +impl fmt::Display for SimpleCardInfo { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let mut string = String::new(); for color in &COLORS { @@ -118,3 +190,54 @@ impl fmt::Display for CardInfo { f.pad(&string) } } + +// Can represent information of the form: +// this card is/isn't possible +#[derive(Clone)] +struct CardPossibilityTable { + possible: HashSet, +} +impl CardPossibilityTable { + pub fn new() -> CardPossibilityTable { + let mut possible = HashSet::new(); + for &color in COLORS.iter() { + for &value in VALUES.iter() { + possible.insert(Card::new(color, value)); + } + } + CardPossibilityTable { + possible: possible, + } + } + + // mark a possible card as false + fn mark_false(&mut self, card: &Card) { + self.possible.remove(card); + } +} +impl CardInfo for CardPossibilityTable { + fn get_possibilities(&self) -> Vec { + let mut cards = self.possible.iter().map(|card| {card.clone() }).collect::>(); + cards.sort(); + cards + } + fn mark_color_false(&mut self, color: &Color) { + for &value in VALUES.iter() { + self.mark_false(&Card::new(color, value)); + } + + } + fn mark_value_false(&mut self, value: &Value) { + for &color in COLORS.iter() { + self.mark_false(&Card::new(color, value.clone())); + } + } +} +impl fmt::Display for CardPossibilityTable { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + for card in self.get_possibilities() { + try!(f.write_str(&format!("{}, ", card))); + } + Ok(()) + } +} diff --git a/src/main.rs b/src/main.rs index 311825c..8002e28 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,13 +4,14 @@ extern crate log; extern crate rand; extern crate crossbeam; +mod cards; +mod info; mod game; mod simulator; mod strategies { pub mod examples; pub mod cheating; } -mod info; use getopts::Options; use std::str::FromStr; diff --git a/src/simulator.rs b/src/simulator.rs index 7477eab..ca770da 100644 --- a/src/simulator.rs +++ b/src/simulator.rs @@ -9,14 +9,20 @@ use game::*; // Represents the strategy of a given player pub trait PlayerStrategy { + // A function to decide what to do on the player's turn. + // Given a GameStateView, outputs their choice. fn decide(&mut self, &GameStateView) -> TurnChoice; + // A function to update internal state after other players' turns. + // Given what happened last turn, and the new state. fn update(&mut self, &Turn, &GameStateView); } // Represents the overall strategy for a game -// Shouldn't do much, except possibility e.g. initialize some shared randomness between players +// Shouldn't do much, except store configuration parameters and +// possibility initialize some shared randomness between players pub trait GameStrategy { fn initialize(&self, Player, &GameStateView) -> Box; } + // Represents configuration for a strategy. // Acts as a factory for game strategies, so we can play many rounds pub trait GameStrategyConfig { diff --git a/src/strategies/cheating.rs b/src/strategies/cheating.rs index 8bc6aaf..897257d 100644 --- a/src/strategies/cheating.rs +++ b/src/strategies/cheating.rs @@ -4,6 +4,7 @@ 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