diff --git a/src/game.rs b/src/game.rs index 9d5c47e..a30aade 100644 --- a/src/game.rs +++ b/src/game.rs @@ -1,20 +1,24 @@ use rand::{self, Rng}; +use std::convert::From; use std::collections::HashSet; use std::collections::HashMap; use std::fmt; +use info::*; + /* * Type definitions */ pub type Color = &'static str; -const COLORS: [Color; 5] = ["blue", "red", "yellow", "white", "green"]; +pub const COLORS: [Color; 5] = ["blue", "red", "yellow", "white", "green"]; pub type Value = u32; // list of (value, count) pairs -const VALUE_COUNTS : [(Value, u32); 5] = [(1, 3), (2, 2), (3, 2), (4, 2), (5, 1)]; -const FINAL_VALUE : Value = 5; +pub const VALUES : [Value; 5] = [1, 2, 3, 4, 5]; +pub const VALUE_COUNTS : [(Value, u32); 5] = [(1, 3), (2, 2), (3, 2), (4, 2), (5, 1)]; +pub const FINAL_VALUE : Value = 5; pub struct Card { pub color: Color, @@ -27,22 +31,22 @@ impl fmt::Debug for Card { } #[derive(Debug)] -pub struct Pile(Vec); -// basically a stack of cards -impl Pile { - pub fn new() -> Pile { - Pile(Vec::new()) +// basically a stack of cards, or card info +pub struct Pile(Vec); +impl Pile { + pub fn new() -> Pile { + Pile(Vec::::new()) } - pub fn draw(&mut self) -> Option { + pub fn draw(&mut self) -> Option { self.0.pop() } - pub fn place(&mut self, card: Card) { - self.0.push(card); + pub fn place(&mut self, item: T) { + self.0.push(item); } - pub fn take(&mut self, index: usize) -> Card { + pub fn take(&mut self, index: usize) -> T { self.0.remove(index) } - pub fn top(&self) -> Option<&Card> { + pub fn top(&self) -> Option<&T> { self.0.last() } pub fn shuffle(&mut self) { @@ -52,6 +56,15 @@ impl Pile { self.0.len() } } +impl From> for Pile { + fn from(items: Vec) -> Pile { + Pile(items) + } +} + +pub type Cards = Pile; + +pub type CardsInfo = Pile; pub type Player = u32; @@ -89,18 +102,18 @@ pub struct GameOptions { #[derive(Debug)] pub struct PlayerState { // the player's actual hand - pub hand: Pile, + pub hand: Cards, // represents what is common knowledge about the player's hand - // pub known: , + pub info: CardsInfo, } // State of everything except the player's hands // Is all completely common knowledge #[derive(Debug)] pub struct BoardState { - deck: Pile, - pub discard: Pile, - pub fireworks: HashMap, + deck: Cards, + pub discard: Cards, + pub fireworks: HashMap, pub num_players: u32, @@ -123,9 +136,9 @@ pub struct BoardState { pub struct GameStateView<'a> { // the player whose view it is pub player: Player, - // what is known about their own hand - // pub known: - // the cards of the other players + // what is known about their own hand (and thus common knowledge) + pub info: &'a CardsInfo, + // the cards of the other players, as well as the information they have pub other_player_states: HashMap, // board state pub board: &'a BoardState, @@ -150,18 +163,22 @@ impl GameState { // we can assume the deck is big enough to draw initial hands deck.draw().unwrap() }).collect::>(); + let infos = (0..opts.hand_size).map(|_| { + CardInfo::new() + }).collect::>(); let state = PlayerState { - hand: Pile(raw_hand), + hand: Cards::from(raw_hand), + info: CardsInfo::from(infos), }; player_states.insert(i, state); } - let mut fireworks : HashMap = HashMap::new(); + let mut fireworks : HashMap = HashMap::new(); for color in COLORS.iter() { - let mut pile = Pile::new(); + let mut firework = Cards::new(); let card = Card { value: 0, color: color }; - pile.place(card); - fireworks.insert(color, pile); + firework.place(card); + fireworks.insert(color, firework); } GameState { @@ -169,7 +186,7 @@ impl GameState { board: BoardState { deck: deck, fireworks: fireworks, - discard: Pile::new(), + discard: Cards::new(), num_players: opts.num_players, player: 0, turn: 1, @@ -183,8 +200,8 @@ impl GameState { } } - fn make_deck() -> Pile { - let mut deck: Pile = Pile(Vec::new()); + fn make_deck() -> Cards { + let mut deck: Cards = Cards::from(Vec::new()); for color in COLORS.iter() { for &(value, count) in VALUE_COUNTS.iter() { @@ -227,6 +244,7 @@ impl GameState { } GameStateView { player: player, + info: &self.player_states.get(&player).unwrap().info, other_player_states: other_player_states, board: &self.board, } @@ -234,10 +252,12 @@ impl GameState { // takes a card from the player's hand, and replaces it if possible fn take_from_hand(&mut self, index: usize) -> Card { - let ref mut hand = self.player_states.get_mut(&self.board.player).unwrap().hand; - let card = hand.take(index); + let ref mut state = self.player_states.get_mut(&self.board.player).unwrap(); + let card = state.hand.take(index); + state.info.take(index); if let Some(new_card) = self.board.deck.draw() { - hand.place(new_card); + state.hand.place(new_card); + state.info.place(CardInfo::new()); } card } diff --git a/src/info.rs b/src/info.rs new file mode 100644 index 0000000..77f05cb --- /dev/null +++ b/src/info.rs @@ -0,0 +1,105 @@ +use std::collections::HashMap; +use std::cmp::Eq; +use std::hash::Hash; + +use game::*; + +// Represents a bit of information about T +trait Info where T: Hash + Eq + Clone { + // get all a-priori possibilities + fn get_possibilities() -> Vec; + + // 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 initialize() -> HashMap { + let mut possible_map : HashMap = HashMap::new(); + for value in Self::get_possibilities().iter() { + possible_map.insert(value.clone(), true); + } + possible_map + } + + fn merge(&mut self, other: &Self) { + for (value, possible) in self.get_mut_possibility_map().iter_mut() { + *possible = *possible && *other.get_possibility_map().get(value).unwrap(); + } + } + + 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); + } + } + } + + fn mark_false(&mut self, value: &T) { + self.get_mut_possibility_map().insert(value.clone(), false); + } +} + +#[derive(Debug)] +pub struct ColorInfo(HashMap); +impl ColorInfo { + pub fn new() -> ColorInfo { + ColorInfo(ColorInfo::initialize()) + } +} +impl Info for ColorInfo { + fn get_possibilities() -> Vec { + let mut possible : Vec = Vec::new(); + for color in COLORS.iter() { + possible.push(*color); + } + possible + } + fn get_possibility_map(&self) -> &HashMap { + &self.0 + } + fn get_mut_possibility_map(&mut self) -> &mut HashMap { + &mut self.0 + } +} + +#[derive(Debug)] +pub struct ValueInfo(HashMap); +impl ValueInfo { + pub fn new() -> ValueInfo { + ValueInfo(ValueInfo::initialize()) + } +} +impl Info for ValueInfo { + fn get_possibilities() -> Vec { + let mut possible : Vec = Vec::new(); + for value in VALUES.iter() { + possible.push(*value); + } + possible + } + fn get_possibility_map(&self) -> &HashMap { + &self.0 + } + fn get_mut_possibility_map(&mut self) -> &mut HashMap { + &mut self.0 + } +} + +#[derive(Debug)] +pub struct CardInfo { + pub color_info: ColorInfo, + pub value_info: ValueInfo, +} +impl CardInfo { + pub fn new() -> CardInfo { + CardInfo { + color_info: ColorInfo::new(), + value_info: ValueInfo::new(), + } + } +} diff --git a/src/main.rs b/src/main.rs index 055bd07..b3aad5c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,12 +4,15 @@ extern crate log; mod game; mod strategies; +mod info; struct SimpleLogger; impl log::Log for SimpleLogger { fn enabled(&self, metadata: &log::LogMetadata) -> bool { - metadata.level() <= log::LogLevel::Debug + // metadata.level() <= log::LogLevel::Warn + metadata.level() <= log::LogLevel::Info + // metadata.level() <= log::LogLevel::Debug } fn log(&self, record: &log::LogRecord) { diff --git a/src/strategies.rs b/src/strategies.rs index 455d76a..8945960 100644 --- a/src/strategies.rs +++ b/src/strategies.rs @@ -60,6 +60,7 @@ pub fn simulate(opts: &GameOptions, strategy: &S, n_trials: u32) -> average } +// dummy, terrible strategy pub struct AlwaysPlay; impl Strategy for AlwaysPlay { type InternalState = ();