moving stuff around
- add hand info - don't manage info in game views - prevent deck size cheat - rearrange stuff
This commit is contained in:
parent
d86136889d
commit
f2de390e0e
7 changed files with 340 additions and 382 deletions
175
src/cards.rs
175
src/cards.rs
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
377
src/game.rs
377
src/game.rs
|
@ -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;
|
||||||
|
|
|
@ -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]
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 {
|
||||||
|
|
|
@ -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),
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue