moving stuff around

- add hand info
- don't manage info in game views
- prevent deck size cheat
- rearrange stuff
This commit is contained in:
Jeff Wu 2016-04-03 22:29:57 -07:00
parent d86136889d
commit f2de390e0e
7 changed files with 340 additions and 382 deletions

View file

@ -1,175 +0,0 @@
use std::collections::HashMap;
use std::fmt;
pub type Color = char;
pub const COLORS: [Color; 5] = ['r', 'y', 'g', 'b', 'w'];
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, "{}{}", self.color, self.value)
}
}
pub type Cards = Vec<Card>;
#[derive(Debug,Clone)]
pub struct CardCounts {
counts: HashMap<Card, u32>,
}
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 increment(&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!(
"{}: ", 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 struct Discard {
pub cards: Cards,
counts: CardCounts,
}
impl Discard {
pub fn new() -> Discard {
Discard {
cards: Cards::new(),
counts: CardCounts::new(),
}
}
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.increment(&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)
}
}
pub type Score = u32;
pub const PERFECT_SCORE: Score = 25;
#[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 needed_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.needed_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

@ -1,14 +1,184 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt; use std::fmt;
use std::iter;
use std::slice::IterMut;
use std::ops::Range; use std::ops::Range;
pub use info::*;
pub use cards::*;
pub type Player = u32; pub type Player = u32;
pub type Color = char;
pub const NUM_COLORS: usize = 5;
pub const COLORS: [Color; NUM_COLORS] = ['r', 'y', 'g', 'b', 'w'];
pub type Value = u32;
// list of values, assumed to be small to large
pub const NUM_VALUES: usize = 5;
pub const VALUES : [Value; NUM_VALUES] = [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, "{}{}", self.color, self.value)
}
}
#[derive(Debug,Clone)]
pub struct CardCounts {
counts: HashMap<Card, u32>,
}
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 increment(&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!(
"{}: ", 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(())
}
}
pub type Cards = Vec<Card>;
#[derive(Debug,Clone)]
pub struct Discard {
pub cards: Cards,
counts: CardCounts,
}
impl Discard {
pub fn new() -> Discard {
Discard {
cards: Cards::new(),
counts: CardCounts::new(),
}
}
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.increment(&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)
}
}
pub type Score = u32;
pub const PERFECT_SCORE: Score = (NUM_COLORS * NUM_VALUES) as 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 needed_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.needed_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,Clone,Hash,PartialEq,Eq)] #[derive(Debug,Clone,Hash,PartialEq,Eq)]
pub enum Hinted { pub enum Hinted {
Color(Color), Color(Color),
@ -65,80 +235,11 @@ pub struct GameOptions {
pub allow_empty_hints: bool, pub allow_empty_hints: bool,
} }
// The state of a given player: all other players may see this
#[derive(Debug,Clone)]
pub struct PlayerState {
// the player's actual hand
pub hand: Cards,
// represents what is common knowledge about the player's hand
pub info: Vec<SimpleCardInfo>,
}
impl fmt::Display for PlayerState {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
try!(f.write_str("hand: "));
let mut i = 0;
for card in &self.hand {
let info : &SimpleCardInfo = &self.info[i];
try!(f.write_str(&format!("{} =? {: <15} ", card, info)));
i += 1;
}
Ok(())
}
}
impl PlayerState {
pub fn new(hand: Cards) -> PlayerState {
let infos = (0..hand.len()).map(|_| {
SimpleCardInfo::new()
}).collect::<Vec<_>>();
PlayerState {
hand: hand,
info: infos,
}
}
pub fn take(&mut self, index: usize) -> (Card, SimpleCardInfo) {
let card = self.hand.remove(index);
let info = self.info.remove(index);
(card, info)
}
pub fn place(&mut self, card: Card) {
self.hand.push(card);
self.info.push(SimpleCardInfo::new());
}
fn hand_info_iter_mut<'a>(&'a mut self) ->
iter::Zip<IterMut<'a, Card>, IterMut<'a, SimpleCardInfo>>
{
self.hand.iter_mut().zip(self.info.iter_mut())
}
pub fn reveal(&mut self, hinted: &Hinted) -> Vec<bool> {
match hinted {
&Hinted::Color(color) => {
self.hand_info_iter_mut().map(|(card, info)| {
let matches = card.color == color;
info.mark_color(color, matches);
matches
}).collect::<Vec<_>>()
}
&Hinted::Value(value) => {
self.hand_info_iter_mut().map(|(card, info)| {
let matches = card.value == value;
info.mark_value(value, matches);
matches
}).collect::<Vec<_>>()
}
}
}
}
// 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,Clone)] #[derive(Debug,Clone)]
pub struct BoardState { pub struct BoardState {
deck: Cards, pub deck_size: u32,
pub total_cards: u32, pub total_cards: u32,
pub discard: Discard, pub discard: Discard,
pub fireworks: HashMap<Color, Firework>, pub fireworks: HashMap<Color, Firework>,
@ -161,16 +262,14 @@ pub struct BoardState {
pub deckless_turns_remaining: u32, pub deckless_turns_remaining: u32,
} }
impl BoardState { impl BoardState {
pub fn new(opts: &GameOptions, deck: Cards) -> BoardState { pub fn new(opts: &GameOptions, deck_size: u32) -> BoardState {
let fireworks = COLORS.iter().map(|&color| { let fireworks = COLORS.iter().map(|&color| {
(color, Firework::new(color)) (color, Firework::new(color))
}).collect::<HashMap<_, _>>(); }).collect::<HashMap<_, _>>();
let total_cards = deck.len() as u32;
BoardState { BoardState {
deck: deck, deck_size: deck_size,
total_cards: total_cards, total_cards: deck_size,
fireworks: fireworks, fireworks: fireworks,
discard: Discard::new(), discard: Discard::new(),
num_players: opts.num_players, num_players: opts.num_players,
@ -272,10 +371,6 @@ impl BoardState {
self.fireworks.iter().map(|(_, firework)| firework.score()).fold(0, |a, b| a + b) self.fireworks.iter().map(|(_, firework)| firework.score()).fold(0, |a, b| a + b)
} }
pub fn deck_size(&self) -> u32 {
self.deck.len() as u32
}
pub fn discard_size(&self) -> u32 { pub fn discard_size(&self) -> u32 {
self.discard.cards.len() as u32 self.discard.cards.len() as u32
} }
@ -303,11 +398,10 @@ impl fmt::Display for BoardState {
))); )));
} }
let deck_size = self.deck_size();
try!(f.write_str(&format!( try!(f.write_str(&format!(
"{} cards remaining in deck\n", deck_size "{} cards remaining in deck\n", self.deck_size
))); )));
if deck_size == 0 { if self.deck_size == 0 {
try!(f.write_str(&format!( try!(f.write_str(&format!(
"Deck is empty. {} turns remaining in game\n", self.deckless_turns_remaining "Deck is empty. {} turns remaining in game\n", self.deckless_turns_remaining
))); )));
@ -332,18 +426,14 @@ impl fmt::Display for BoardState {
// complete game view of a given player // complete game view of a given player
pub trait GameView { pub trait GameView {
fn me(&self) -> Player; fn me(&self) -> Player;
fn my_info(&self) -> &Vec<SimpleCardInfo>; fn get_hand(&self, &Player) -> &Cards;
fn get_state(&self, player: &Player) -> &PlayerState;
fn get_board(&self) -> &BoardState; fn get_board(&self) -> &BoardState;
fn get_hand(&self, player: &Player) -> &Cards { fn my_hand_size(&self) -> usize;
assert!(self.me() != *player, "Cannot query about your own cards!");
&self.get_state(player).hand
}
fn hand_size(&self, player: &Player) -> usize { fn hand_size(&self, player: &Player) -> usize {
if self.me() == *player { if self.me() == *player {
self.my_info().len() self.my_hand_size()
} else { } else {
self.get_hand(player).len() self.get_hand(player).len()
} }
@ -379,10 +469,9 @@ pub trait GameView {
pub struct BorrowedGameView<'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) pub hand_size: usize,
pub info: &'a Vec<SimpleCardInfo>,
// the cards of the other players, as well as the information they have // the cards of the other players, as well as the information they have
pub other_player_states: HashMap<Player, &'a PlayerState>, pub other_hands: HashMap<Player, &'a Cards>,
// board state // board state
pub board: &'a BoardState, pub board: &'a BoardState,
} }
@ -390,12 +479,12 @@ impl <'a> GameView for BorrowedGameView<'a> {
fn me(&self) -> Player { fn me(&self) -> Player {
self.player self.player
} }
fn my_info(&self) -> &Vec<SimpleCardInfo> { fn my_hand_size(&self) -> usize {
self.info self.hand_size
} }
fn get_state(&self, player: &Player) -> &PlayerState { fn get_hand(&self, player: &Player) -> &Cards {
assert!(self.me() != *player, "Cannot query about your own state!"); assert!(self.me() != *player, "Cannot query about your own state!");
self.other_player_states.get(player).unwrap() self.other_hands.get(player).unwrap()
} }
fn get_board(&self) -> &BoardState { fn get_board(&self) -> &BoardState {
self.board self.board
@ -407,27 +496,23 @@ impl <'a> GameView for BorrowedGameView<'a> {
pub struct OwnedGameView { pub struct OwnedGameView {
// 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) pub hand_size: usize,
pub info: Vec<SimpleCardInfo>,
// the cards of the other players, as well as the information they have // the cards of the other players, as well as the information they have
pub other_player_states: HashMap<Player, PlayerState>, pub other_hands: HashMap<Player, Cards>,
// board state // board state
pub board: BoardState, pub board: BoardState,
} }
impl OwnedGameView { impl OwnedGameView {
pub fn clone_from(borrowed_view: &BorrowedGameView) -> OwnedGameView { pub fn clone_from(borrowed_view: &BorrowedGameView) -> OwnedGameView {
let info = borrowed_view.info.iter() let other_hands = borrowed_view.other_hands.iter()
.map(|card_info| card_info.clone()).collect::<Vec<_>>();
let other_player_states = borrowed_view.other_player_states.iter()
.map(|(&other_player, &player_state)| { .map(|(&other_player, &player_state)| {
(other_player, player_state.clone()) (other_player, player_state.clone())
}).collect::<HashMap<_, _>>(); }).collect::<HashMap<_, _>>();
OwnedGameView { OwnedGameView {
player: borrowed_view.player.clone(), player: borrowed_view.player.clone(),
info: info, hand_size: borrowed_view.hand_size,
other_player_states: other_player_states, other_hands: other_hands,
board: (*borrowed_view.board).clone(), board: (*borrowed_view.board).clone(),
} }
} }
@ -436,24 +521,24 @@ impl GameView for OwnedGameView {
fn me(&self) -> Player { fn me(&self) -> Player {
self.player self.player
} }
fn my_info(&self) -> &Vec<SimpleCardInfo> { fn my_hand_size(&self) -> usize {
&self.info self.hand_size
} }
fn get_state(&self, player: &Player) -> &PlayerState { fn get_hand(&self, player: &Player) -> &Cards {
assert!(self.me() != *player, "Cannot query about your own state!"); assert!(self.me() != *player, "Cannot query about your own state!");
self.other_player_states.get(player).unwrap() self.other_hands.get(player).unwrap()
} }
fn get_board(&self) -> &BoardState { fn get_board(&self) -> &BoardState {
&self.board &self.board
} }
} }
// complete game state (known to nobody!) // complete game state (known to nobody!)
#[derive(Debug)] #[derive(Debug)]
pub struct GameState { pub struct GameState {
pub player_states: HashMap<Player, PlayerState>, pub hands: HashMap<Player, Cards>,
pub board: BoardState, pub board: BoardState,
pub deck: Cards,
} }
impl fmt::Display for GameState { impl fmt::Display for GameState {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
@ -462,8 +547,12 @@ impl fmt::Display for GameState {
try!(f.write_str("Hands:\n")); try!(f.write_str("Hands:\n"));
try!(f.write_str("======\n")); try!(f.write_str("======\n"));
for player in self.board.get_players() { for player in self.board.get_players() {
let state = &self.player_states.get(&player).unwrap(); let hand = &self.hands.get(&player).unwrap();
try!(f.write_str(&format!("player {} {}\n", player, state))); try!(f.write_str(&format!("player {}:", player)));
for card in hand.iter() {
try!(f.write_str(&format!(" {}", card)));
}
try!(f.write_str(&"\n"));
} }
try!(f.write_str("======\n")); try!(f.write_str("======\n"));
try!(f.write_str("Board:\n")); try!(f.write_str("Board:\n"));
@ -474,21 +563,23 @@ impl fmt::Display for GameState {
} }
impl GameState { impl GameState {
pub fn new(opts: &GameOptions, deck: Cards) -> GameState { pub fn new(opts: &GameOptions, mut deck: Cards) -> GameState {
let mut board = BoardState::new(opts, deck); let mut board = BoardState::new(opts, deck.len() as u32);
let player_states = let hands =
(0..opts.num_players).map(|player| { (0..opts.num_players).map(|player| {
let hand = (0..opts.hand_size).map(|_| { let hand = (0..opts.hand_size).map(|_| {
// we can assume the deck is big enough to draw initial hands // we can assume the deck is big enough to draw initial hands
board.deck.pop().unwrap() board.deck_size -= 1;
deck.pop().unwrap()
}).collect::<Vec<_>>(); }).collect::<Vec<_>>();
(player, PlayerState::new(hand)) (player, hand)
}).collect::<HashMap<_, _>>(); }).collect::<HashMap<_, _>>();
GameState { GameState {
player_states: player_states, hands: hands,
board: board, board: board,
deck: deck,
} }
} }
@ -506,33 +597,33 @@ 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) -> BorrowedGameView { pub fn get_view(&self, player: Player) -> BorrowedGameView {
let mut other_player_states = HashMap::new(); let mut other_hands = HashMap::new();
for (&other_player, state) in &self.player_states { for (&other_player, hand) in &self.hands {
if player != other_player { if player != other_player {
other_player_states.insert(other_player, state); other_hands.insert(other_player, hand);
} }
} }
BorrowedGameView { BorrowedGameView {
player: player, player: player,
info: &self.player_states.get(&player).unwrap().info, hand_size: self.hands.get(&player).unwrap().len(),
other_player_states: other_player_states, other_hands: other_hands,
board: &self.board, board: &self.board,
} }
} }
// takes a card from the player's hand, and replaces it if possible // takes a card from the player's hand, and replaces it if possible
fn take_from_hand(&mut self, index: usize) -> Card { fn take_from_hand(&mut self, index: usize) -> Card {
let ref mut state = self.player_states.get_mut(&self.board.player).unwrap(); let ref mut hand = self.hands.get_mut(&self.board.player).unwrap();
let (card, _) = state.take(index); hand.remove(index)
card
} }
fn replenish_hand(&mut self) { fn replenish_hand(&mut self) {
let ref mut state = self.player_states.get_mut(&self.board.player).unwrap(); let ref mut hand = self.hands.get_mut(&self.board.player).unwrap();
if (state.hand.len() as u32) < self.board.hand_size { if (hand.len() as u32) < self.board.hand_size {
if let Some(new_card) = self.board.deck.pop() { if let Some(new_card) = self.deck.pop() {
self.board.deck_size -= 1;
debug!("Drew new card, {}", new_card); debug!("Drew new card, {}", new_card);
state.place(new_card); hand.push(new_card);
} }
} }
} }
@ -549,11 +640,19 @@ impl GameState {
assert!(self.board.player != hint.player, assert!(self.board.player != hint.player,
format!("Player {} gave a hint to himself", hint.player)); format!("Player {} gave a hint to himself", hint.player));
let ref mut state = self.player_states.get_mut(&hint.player).unwrap(); let hand = self.hands.get(&hint.player).unwrap();
let results = state.reveal(&hint.hinted); let results = match hint.hinted {
Hinted::Color(color) => {
hand.iter().map(|card| { card.color == color }).collect::<Vec<_>>()
}
Hinted::Value(value) => {
hand.iter().map(|card| { card.value == value }).collect::<Vec<_>>()
}
};
if (!self.board.allow_empty_hints) && (results.iter().all(|matched| !matched)) { if (!self.board.allow_empty_hints) && (results.iter().all(|matched| !matched)) {
panic!("Tried hinting an empty hint"); panic!("Tried hinting an empty hint");
} }
TurnResult::Hint(results) TurnResult::Hint(results)
} }
TurnChoice::Discard(index) => { TurnChoice::Discard(index) => {
@ -603,7 +702,7 @@ impl GameState {
self.replenish_hand(); self.replenish_hand();
if self.board.deck.len() == 0 { if self.board.deck_size == 0 {
self.board.deckless_turns_remaining -= 1; self.board.deckless_turns_remaining -= 1;
} }
self.board.turn += 1; self.board.turn += 1;

View file

@ -1,14 +1,17 @@
use std::cmp::Eq; use std::cmp::Eq;
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use std::fmt; use std::fmt;
use std::ops::{Index,IndexMut};
use std::hash::Hash; use std::hash::Hash;
use std::convert::From; use std::convert::From;
use std::slice;
use cards::*; use game::*;
use game::BoardState;
// trait representing information about a card // trait representing information about a card
pub trait CardInfo { pub trait CardInfo {
fn new() -> Self;
// whether the card is possible // whether the card is possible
fn is_possible(&self, card: &Card) -> bool; fn is_possible(&self, card: &Card) -> bool;
@ -190,15 +193,14 @@ pub struct SimpleCardInfo {
pub color_info: ColorInfo, pub color_info: ColorInfo,
pub value_info: ValueInfo, pub value_info: ValueInfo,
} }
impl SimpleCardInfo { impl CardInfo for SimpleCardInfo {
pub fn new() -> SimpleCardInfo { fn new() -> SimpleCardInfo {
SimpleCardInfo { SimpleCardInfo {
color_info: ColorInfo::new(), color_info: ColorInfo::new(),
value_info: ValueInfo::new(), value_info: ValueInfo::new(),
} }
} }
}
impl CardInfo for SimpleCardInfo {
fn get_possibilities(&self) -> Vec<Card> { fn get_possibilities(&self) -> Vec<Card> {
let mut v = Vec::new(); let mut v = Vec::new();
for &color in self.color_info.get_possibilities().iter() { for &color in self.color_info.get_possibilities().iter() {
@ -249,10 +251,6 @@ pub struct CardPossibilityTable {
possible: HashMap<Card, u32>, possible: HashMap<Card, u32>,
} }
impl CardPossibilityTable { impl CardPossibilityTable {
pub fn new() -> CardPossibilityTable {
Self::from(&CardCounts::new())
}
// mark a possible card as false // mark a possible card as false
pub fn mark_false(&mut self, card: &Card) { pub fn mark_false(&mut self, card: &Card) {
self.possible.remove(card); self.possible.remove(card);
@ -327,6 +325,10 @@ impl <'a> From<&'a CardCounts> for CardPossibilityTable {
} }
} }
impl CardInfo for CardPossibilityTable { impl CardInfo for CardPossibilityTable {
fn new() -> CardPossibilityTable {
Self::from(&CardCounts::new())
}
fn is_possible(&self, card: &Card) -> bool { fn is_possible(&self, card: &Card) -> bool {
self.possible.contains_key(card) self.possible.contains_key(card)
} }
@ -358,3 +360,49 @@ impl fmt::Display for CardPossibilityTable {
Ok(()) Ok(())
} }
} }
#[derive(Clone)]
pub struct HandInfo<T> where T: CardInfo {
pub hand_info: Vec<T>
}
impl <T> HandInfo<T> where T: CardInfo {
pub fn new(hand_size: u32) -> Self {
let hand_info = (0..hand_size).map(|_| T::new()).collect::<Vec<_>>();
HandInfo {
hand_info: hand_info,
}
}
// update for hint to me
pub fn update_for_hint(&mut self, hinted: &Hinted, matches: &Vec<bool>) {
match hinted {
&Hinted::Color(color) => {
for (card_info, &matched) in self.hand_info.iter_mut().zip(matches.iter()) {
card_info.mark_color(color, matched);
}
}
&Hinted::Value(value) => {
for (card_info, &matched) in self.hand_info.iter_mut().zip(matches.iter()) {
card_info.mark_value(value, matched);
}
}
}
}
pub fn remove(&mut self, index: usize) -> T { self.hand_info.remove(index) }
pub fn push(&mut self, card_info: T) { self.hand_info.push(card_info) }
pub fn iter_mut(&mut self) -> slice::IterMut<T> { self.hand_info.iter_mut() }
pub fn iter(&self) -> slice::Iter<T> { self.hand_info.iter() }
pub fn len(&self) -> usize { self.hand_info.len() }
}
impl <T> Index<usize> for HandInfo<T> where T: CardInfo {
type Output = T;
fn index(&self, index: usize) -> &T {
&self.hand_info[index]
}
}
impl <T> IndexMut<usize> for HandInfo<T> where T: CardInfo {
fn index_mut(&mut self, index: usize) -> &mut T {
&mut self.hand_info[index]
}
}

View file

@ -4,8 +4,7 @@ extern crate log;
extern crate rand; extern crate rand;
extern crate crossbeam; extern crate crossbeam;
mod cards; mod helpers;
mod info;
mod game; mod game;
mod simulator; mod simulator;
mod strategies { mod strategies {

View file

@ -17,7 +17,6 @@ use game::*;
// - if a hint exists, hint // - if a hint exists, hint
// - discard the first card // - discard the first card
#[allow(dead_code)]
pub struct CheatingStrategyConfig; pub struct CheatingStrategyConfig;
impl CheatingStrategyConfig { impl CheatingStrategyConfig {
@ -32,40 +31,41 @@ impl GameStrategyConfig for CheatingStrategyConfig {
} }
pub struct CheatingStrategy { pub struct CheatingStrategy {
player_states_cheat: Rc<RefCell<HashMap<Player, Cards>>>, player_hands_cheat: Rc<RefCell<HashMap<Player, Cards>>>,
} }
impl CheatingStrategy { impl CheatingStrategy {
pub fn new() -> CheatingStrategy { pub fn new() -> CheatingStrategy {
CheatingStrategy { CheatingStrategy {
player_states_cheat: Rc::new(RefCell::new(HashMap::new())), player_hands_cheat: Rc::new(RefCell::new(HashMap::new())),
} }
} }
} }
impl GameStrategy for CheatingStrategy { impl GameStrategy for CheatingStrategy {
fn initialize(&self, player: Player, view: &BorrowedGameView) -> Box<PlayerStrategy> { fn initialize(&self, player: Player, view: &BorrowedGameView) -> Box<PlayerStrategy> {
for (&player, state) in &view.other_player_states { for (&player, &hand) in &view.other_hands {
self.player_states_cheat.borrow_mut().insert( self.player_hands_cheat.borrow_mut().insert(
player, state.hand.clone() player, hand.clone()
); );
} }
Box::new(CheatingPlayerStrategy { Box::new(CheatingPlayerStrategy {
player_states_cheat: self.player_states_cheat.clone(), player_hands_cheat: self.player_hands_cheat.clone(),
me: player, me: player,
}) })
} }
} }
pub struct CheatingPlayerStrategy { pub struct CheatingPlayerStrategy {
player_states_cheat: Rc<RefCell<HashMap<Player, Cards>>>, player_hands_cheat: Rc<RefCell<HashMap<Player, Cards>>>,
me: Player, me: Player,
} }
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: &BorrowedGameView) { 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( let their_hand = *view.other_hands.get(&next).unwrap();
next, view.other_player_states.get(&next).unwrap().hand.clone() self.player_hands_cheat.borrow_mut().insert(
next, their_hand.clone()
); );
} }
@ -98,15 +98,15 @@ 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: &BorrowedGameView, card: &Card) -> i32 { fn get_play_score(&self, view: &BorrowedGameView, card: &Card) -> i32 {
let states = self.player_states_cheat.borrow(); let hands = self.player_hands_cheat.borrow();
let my_hand = states.get(&self.me).unwrap(); let my_hand = hands.get(&self.me).unwrap();
let my_hand_value = self.hand_play_value(view, my_hand); let my_hand_value = self.hand_play_value(view, my_hand);
for player in view.board.get_players() { for player in view.board.get_players() {
if player != self.me { if player != self.me {
if view.has_card(&player, card) { if view.has_card(&player, card) {
let their_hand_value = self.hand_play_value(view, states.get(&player).unwrap()); let their_hand_value = self.hand_play_value(view, hands.get(&player).unwrap());
// they can play this card, and have less urgent plays than i do // they can play this card, and have less urgent plays than i do
if their_hand_value < my_hand_value { if their_hand_value < my_hand_value {
return 10 - (card.value as i32) return 10 - (card.value as i32)
@ -139,9 +139,9 @@ impl PlayerStrategy for CheatingPlayerStrategy {
fn decide(&mut self, view: &BorrowedGameView) -> 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 hands = self.player_hands_cheat.borrow();
let my_cards = states.get(&self.me).unwrap(); let my_hand = hands.get(&self.me).unwrap();
let playable_cards = my_cards.iter().enumerate().filter(|&(_, card)| { let playable_cards = my_hand.iter().enumerate().filter(|&(_, card)| {
view.board.is_playable(card) view.board.is_playable(card)
}).collect::<Vec<_>>(); }).collect::<Vec<_>>();
@ -171,7 +171,7 @@ impl PlayerStrategy for CheatingPlayerStrategy {
- (view.board.num_players * view.board.hand_size); - (view.board.num_players * view.board.hand_size);
if view.board.discard_size() <= discard_threshold { 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_hand) {
return TurnChoice::Discard(i); return TurnChoice::Discard(i);
} }
} }
@ -185,7 +185,7 @@ impl PlayerStrategy for CheatingPlayerStrategy {
} }
// 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_hand) {
return TurnChoice::Discard(i); return TurnChoice::Discard(i);
} }
@ -195,7 +195,7 @@ impl PlayerStrategy for CheatingPlayerStrategy {
// The higher, the better to discard // The higher, the better to discard
let mut index = 0; let mut index = 0;
let mut compval = (false, false, 0); let mut compval = (false, false, 0);
for (i, card) in my_cards.iter().enumerate() { for (i, card) in my_hand.iter().enumerate() {
let my_compval = ( let my_compval = (
view.can_see(card), view.can_see(card),
view.board.is_dispensable(card), view.board.is_dispensable(card),

View file

@ -3,7 +3,6 @@ use game::*;
use rand::{self, Rng}; use rand::{self, Rng};
// dummy, terrible strategy, as an example // dummy, terrible strategy, as an example
#[allow(dead_code)]
#[derive(Clone)] #[derive(Clone)]
pub struct RandomStrategyConfig { pub struct RandomStrategyConfig {
pub hint_probability: f64, pub hint_probability: f64,

View file

@ -3,6 +3,7 @@ use std::cmp::Ordering;
use simulator::*; use simulator::*;
use game::*; use game::*;
use helpers::*;
// TODO: use random extra information - i.e. when casting up and down, // TODO: use random extra information - i.e. when casting up and down,
// we sometimes have 2 choices of value to choose // we sometimes have 2 choices of value to choose
@ -73,7 +74,7 @@ trait Question {
fn answer(&self, &Cards, &OwnedGameView) -> u32; fn answer(&self, &Cards, &OwnedGameView) -> u32;
// process the answer to this question, updating card info // process the answer to this question, updating card info
fn acknowledge_answer( fn acknowledge_answer(
&self, value: u32, &mut Vec<CardPossibilityTable>, &OwnedGameView &self, value: u32, &mut HandInfo<CardPossibilityTable>, &OwnedGameView
); );
fn answer_info(&self, hand: &Cards, view: &OwnedGameView) -> ModulusInformation { fn answer_info(&self, hand: &Cards, view: &OwnedGameView) -> ModulusInformation {
@ -86,7 +87,7 @@ trait Question {
fn acknowledge_answer_info( fn acknowledge_answer_info(
&self, &self,
answer: ModulusInformation, answer: ModulusInformation,
hand_info: &mut Vec<CardPossibilityTable>, hand_info: &mut HandInfo<CardPossibilityTable>,
view: &OwnedGameView view: &OwnedGameView
) { ) {
assert!(self.info_amount() == answer.modulus); assert!(self.info_amount() == answer.modulus);
@ -105,7 +106,7 @@ impl Question for IsPlayable {
fn acknowledge_answer( fn acknowledge_answer(
&self, &self,
answer: u32, answer: u32,
hand_info: &mut Vec<CardPossibilityTable>, hand_info: &mut HandInfo<CardPossibilityTable>,
view: &OwnedGameView, view: &OwnedGameView,
) { ) {
let ref mut card_table = hand_info[self.index]; let ref mut card_table = hand_info[self.index];
@ -187,7 +188,7 @@ impl Question for CardPossibilityPartition {
fn acknowledge_answer( fn acknowledge_answer(
&self, &self,
answer: u32, answer: u32,
hand_info: &mut Vec<CardPossibilityTable>, hand_info: &mut HandInfo<CardPossibilityTable>,
_: &OwnedGameView, _: &OwnedGameView,
) { ) {
let ref mut card_table = hand_info[self.index]; let ref mut card_table = hand_info[self.index];
@ -200,7 +201,6 @@ impl Question for CardPossibilityPartition {
} }
} }
#[allow(dead_code)]
pub struct InformationStrategyConfig; pub struct InformationStrategyConfig;
impl InformationStrategyConfig { impl InformationStrategyConfig {
@ -225,7 +225,7 @@ impl GameStrategy for InformationStrategy {
fn initialize(&self, player: Player, view: &BorrowedGameView) -> Box<PlayerStrategy> { fn initialize(&self, player: Player, view: &BorrowedGameView) -> Box<PlayerStrategy> {
let public_info = let public_info =
view.board.get_players().map(|player| { view.board.get_players().map(|player| {
let hand_info = (0..view.board.hand_size).map(|_| { CardPossibilityTable::new() }).collect::<Vec<_>>(); let hand_info = HandInfo::new(view.board.hand_size);
(player, hand_info) (player, hand_info)
}).collect::<HashMap<_,_>>(); }).collect::<HashMap<_,_>>();
@ -240,16 +240,17 @@ impl GameStrategy for InformationStrategy {
pub struct InformationPlayerStrategy { pub struct InformationPlayerStrategy {
me: Player, me: Player,
public_info: HashMap<Player, Vec<CardPossibilityTable>>, public_info: HashMap<Player, HandInfo<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 last_view: OwnedGameView, // the view on the previous turn
} }
impl InformationPlayerStrategy { impl InformationPlayerStrategy {
fn get_questions( fn get_questions(
total_info: u32, total_info: u32,
view: &OwnedGameView, view: &OwnedGameView,
hand_info: &Vec<CardPossibilityTable>, hand_info: &HandInfo<CardPossibilityTable>,
) -> Vec<Box<Question>> { ) -> Vec<Box<Question>> {
let mut questions = Vec::new(); let mut questions = Vec::new();
let mut info_remaining = total_info; let mut info_remaining = total_info;
@ -443,7 +444,7 @@ impl InformationPlayerStrategy {
fn get_play_score(&self, view: &OwnedGameView, card: &Card) -> f32 { fn get_play_score(&self, view: &OwnedGameView, card: &Card) -> f32 {
let mut num_with = 1; let mut num_with = 1;
if view.board.deck_size() > 0 { if view.board.deck_size > 0 {
for player in view.board.get_players() { for player in view.board.get_players() {
if player != self.me { if player != self.me {
if view.has_card(&player, card) { if view.has_card(&player, card) {
@ -455,7 +456,7 @@ impl InformationPlayerStrategy {
(10.0 - card.value as f32) / (num_with as f32) (10.0 - card.value as f32) / (num_with as f32)
} }
fn find_useless_cards(&self, view: &OwnedGameView, hand: &Vec<CardPossibilityTable>) -> Vec<usize> { fn find_useless_cards(&self, view: &OwnedGameView, hand: &HandInfo<CardPossibilityTable>) -> Vec<usize> {
let mut useless: HashSet<usize> = HashSet::new(); let mut useless: HashSet<usize> = HashSet::new();
let mut seen: HashMap<Card, usize> = HashMap::new(); let mut seen: HashMap<Card, usize> = HashMap::new();
@ -479,47 +480,34 @@ impl InformationPlayerStrategy {
return useless_vec; return useless_vec;
} }
fn take_public_info(&mut self, player: &Player) -> Vec<CardPossibilityTable> { fn take_public_info(&mut self, player: &Player) -> HandInfo<CardPossibilityTable> {
self.public_info.remove(player).unwrap() self.public_info.remove(player).unwrap()
} }
fn return_public_info(&mut self, player: &Player, card_info: Vec<CardPossibilityTable>) { fn return_public_info(&mut self, player: &Player, card_info: HandInfo<CardPossibilityTable>) {
self.public_info.insert(*player, card_info); self.public_info.insert(*player, card_info);
} }
fn get_my_public_info(&self) -> &Vec<CardPossibilityTable> { fn get_my_public_info(&self) -> &HandInfo<CardPossibilityTable> {
self.get_player_public_info(&self.me) self.get_player_public_info(&self.me)
} }
// fn get_my_public_info_mut(&mut self) -> &mut Vec<CardPossibilityTable> { // fn get_my_public_info_mut(&mut self) -> &mut HandInfo<CardPossibilityTable> {
// let me = self.me.clone(); // let me = self.me.clone();
// self.get_player_public_info_mut(&me) // self.get_player_public_info_mut(&me)
// } // }
fn get_player_public_info(&self, player: &Player) -> &Vec<CardPossibilityTable> { fn get_player_public_info(&self, player: &Player) -> &HandInfo<CardPossibilityTable> {
self.public_info.get(player).unwrap() self.public_info.get(player).unwrap()
} }
fn get_player_public_info_mut(&mut self, player: &Player) -> &mut Vec<CardPossibilityTable> { fn get_player_public_info_mut(&mut self, player: &Player) -> &mut HandInfo<CardPossibilityTable> {
self.public_info.get_mut(player).unwrap() self.public_info.get_mut(player).unwrap()
} }
fn update_public_info_for_hint(&mut self, hint: &Hint, matches: &Vec<bool>) { fn update_public_info_for_hint(&mut self, hint: &Hint, matches: &Vec<bool>) {
let mut info = self.get_player_public_info_mut(&hint.player); let mut info = self.get_player_public_info_mut(&hint.player);
let zip_iter = info.iter_mut().zip(matches); info.update_for_hint(&hint.hinted, matches);
match hint.hinted {
Hinted::Color(color) => {
for (card_info, matched) in zip_iter {
card_info.mark_color(color, *matched);
}
}
Hinted::Value(value) => {
for (card_info, matched) in zip_iter {
card_info.mark_value(value, *matched);
}
}
}
} }
fn update_public_info_for_discard_or_play( fn update_public_info_for_discard_or_play(
@ -547,7 +535,7 @@ impl InformationPlayerStrategy {
// 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 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.iter_mut() {
card_table.decrement_weight_if_possible(card); card_table.decrement_weight_if_possible(card);
} }
} }
@ -555,11 +543,11 @@ impl InformationPlayerStrategy {
self.public_counts.increment(card); self.public_counts.increment(card);
} }
fn get_private_info(&self, view: &OwnedGameView) -> Vec<CardPossibilityTable> { fn get_private_info(&self, view: &OwnedGameView) -> HandInfo<CardPossibilityTable> {
let mut info = self.get_my_public_info().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 (_, state) in &view.other_player_states { for (_, hand) in &view.other_hands {
for card in &state.hand { for card in hand {
card_table.decrement_weight_if_possible(card); card_table.decrement_weight_if_possible(card);
} }
} }
@ -585,7 +573,7 @@ impl InformationPlayerStrategy {
return score; return score;
} }
fn get_index_for_hint(&self, info: &Vec<CardPossibilityTable>, view: &OwnedGameView) -> usize { fn get_index_for_hint(&self, info: &HandInfo<CardPossibilityTable>, view: &OwnedGameView) -> usize {
let mut scores = info.iter().enumerate().map(|(i, card_table)| { let mut scores = info.iter().enumerate().map(|(i, card_table)| {
let score = self.get_hint_index_score(card_table, view); let score = self.get_hint_index_score(card_table, view);
(-score, i) (-score, i)
@ -739,8 +727,8 @@ impl InformationPlayerStrategy {
self.update_from_hint_sum(mod_info); self.update_from_hint_sum(mod_info);
} }
} }
impl PlayerStrategy for InformationPlayerStrategy { impl PlayerStrategy for InformationPlayerStrategy {
fn decide(&mut self, _: &BorrowedGameView) -> TurnChoice { fn decide(&mut self, _: &BorrowedGameView) -> TurnChoice {
// we already stored the view // we already stored the view