2016-03-06 10:35:19 +01:00
|
|
|
|
2016-03-06 03:49:14 +01:00
|
|
|
use rand::{self, Rng};
|
2016-03-06 11:13:08 +01:00
|
|
|
use std::convert::From;
|
2016-03-06 01:54:46 +01:00
|
|
|
use std::collections::HashMap;
|
|
|
|
use std::fmt;
|
|
|
|
|
2016-03-06 11:13:08 +01:00
|
|
|
use info::*;
|
|
|
|
|
2016-03-06 03:49:14 +01:00
|
|
|
/*
|
|
|
|
* Type definitions
|
|
|
|
*/
|
2016-03-06 01:54:46 +01:00
|
|
|
|
|
|
|
pub type Color = &'static str;
|
2016-03-06 11:13:08 +01:00
|
|
|
pub const COLORS: [Color; 5] = ["blue", "red", "yellow", "white", "green"];
|
2016-03-06 03:49:14 +01:00
|
|
|
|
|
|
|
pub type Value = u32;
|
|
|
|
// list of (value, count) pairs
|
2016-03-06 11:13:08 +01:00
|
|
|
pub const VALUES : [Value; 5] = [1, 2, 3, 4, 5];
|
|
|
|
pub const VALUE_COUNTS : [(Value, u32); 5] = [(1, 3), (2, 2), (3, 2), (4, 2), (5, 1)];
|
|
|
|
pub const FINAL_VALUE : Value = 5;
|
2016-03-06 01:54:46 +01:00
|
|
|
|
2016-03-07 01:14:47 +01:00
|
|
|
#[derive(Debug)]
|
2016-03-06 01:54:46 +01:00
|
|
|
pub struct Card {
|
|
|
|
pub color: Color,
|
|
|
|
pub value: Value,
|
|
|
|
}
|
2016-03-07 01:14:47 +01:00
|
|
|
impl fmt::Display for Card {
|
2016-03-06 01:54:46 +01:00
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
2016-03-07 01:14:47 +01:00
|
|
|
let colorchar = self.color.chars().next().unwrap();
|
|
|
|
write!(f, "{}{}", colorchar, self.value)
|
2016-03-06 01:54:46 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-06 03:49:14 +01:00
|
|
|
#[derive(Debug)]
|
2016-03-06 11:13:08 +01:00
|
|
|
// basically a stack of cards, or card info
|
|
|
|
pub struct Pile<T>(Vec<T>);
|
|
|
|
impl <T> Pile<T> {
|
|
|
|
pub fn new() -> Pile<T> {
|
|
|
|
Pile(Vec::<T>::new())
|
2016-03-06 03:49:14 +01:00
|
|
|
}
|
2016-03-06 11:13:08 +01:00
|
|
|
pub fn draw(&mut self) -> Option<T> {
|
2016-03-06 03:49:14 +01:00
|
|
|
self.0.pop()
|
|
|
|
}
|
2016-03-06 11:13:08 +01:00
|
|
|
pub fn place(&mut self, item: T) {
|
|
|
|
self.0.push(item);
|
2016-03-06 03:49:14 +01:00
|
|
|
}
|
2016-03-06 11:13:08 +01:00
|
|
|
pub fn take(&mut self, index: usize) -> T {
|
2016-03-06 03:49:14 +01:00
|
|
|
self.0.remove(index)
|
|
|
|
}
|
2016-03-06 11:13:08 +01:00
|
|
|
pub fn top(&self) -> Option<&T> {
|
2016-03-06 07:49:40 +01:00
|
|
|
self.0.last()
|
|
|
|
}
|
2016-03-06 03:49:14 +01:00
|
|
|
pub fn shuffle(&mut self) {
|
|
|
|
rand::thread_rng().shuffle(&mut self.0[..]);
|
|
|
|
}
|
2016-03-06 07:49:40 +01:00
|
|
|
pub fn size(&self) -> usize {
|
|
|
|
self.0.len()
|
|
|
|
}
|
2016-03-06 03:49:14 +01:00
|
|
|
}
|
2016-03-06 11:13:08 +01:00
|
|
|
impl <T> From<Vec<T>> for Pile<T> {
|
|
|
|
fn from(items: Vec<T>) -> Pile<T> {
|
|
|
|
Pile(items)
|
|
|
|
}
|
|
|
|
}
|
2016-03-07 01:14:47 +01:00
|
|
|
impl <T> fmt::Display for Pile<T> where T: fmt::Display {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
2016-03-09 19:27:40 +01:00
|
|
|
try!(f.write_str("["));
|
2016-03-07 01:14:47 +01:00
|
|
|
for item in &self.0 {
|
2016-03-09 19:27:40 +01:00
|
|
|
try!(f.write_str(&format!("{}, ", item)));
|
2016-03-07 01:14:47 +01:00
|
|
|
}
|
2016-03-09 19:27:40 +01:00
|
|
|
try!(f.write_str(""));
|
|
|
|
Ok(())
|
2016-03-07 01:14:47 +01:00
|
|
|
}
|
|
|
|
}
|
2016-03-06 11:13:08 +01:00
|
|
|
|
|
|
|
pub type Cards = Pile<Card>;
|
|
|
|
|
|
|
|
pub type CardsInfo = Pile<CardInfo>;
|
2016-03-06 03:49:14 +01:00
|
|
|
|
|
|
|
pub type Player = u32;
|
2016-03-06 01:54:46 +01:00
|
|
|
|
2016-03-07 07:00:23 +01:00
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct Firework {
|
|
|
|
pub color: Color,
|
|
|
|
pub cards: Cards,
|
|
|
|
}
|
|
|
|
impl Firework {
|
|
|
|
fn new(color: Color) -> Firework {
|
|
|
|
let mut cards = Cards::new();
|
|
|
|
// have a 0, so it's easier to implement
|
|
|
|
let card = Card { value: 0, color: color };
|
|
|
|
cards.place(card);
|
|
|
|
Firework {
|
|
|
|
color: color,
|
|
|
|
cards: cards,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-09 19:27:40 +01:00
|
|
|
fn top_value(&self) -> Value {
|
|
|
|
self.cards.top().unwrap().value
|
|
|
|
}
|
|
|
|
|
2016-03-07 07:00:23 +01:00
|
|
|
fn desired_value(&self) -> Option<Value> {
|
2016-03-09 19:27:40 +01:00
|
|
|
if self.complete() {
|
2016-03-07 07:00:23 +01:00
|
|
|
None
|
|
|
|
} else {
|
2016-03-09 19:27:40 +01:00
|
|
|
Some(self.top_value() + 1)
|
2016-03-07 07:00:23 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn score(&self) -> usize {
|
|
|
|
// subtract one to account for the 0 we pushed
|
|
|
|
self.cards.size() - 1
|
|
|
|
}
|
|
|
|
|
|
|
|
fn complete(&self) -> bool {
|
2016-03-09 19:27:40 +01:00
|
|
|
self.top_value() == FINAL_VALUE
|
2016-03-07 07:00:23 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
fn place(&mut self, card: Card) {
|
|
|
|
assert!(
|
|
|
|
card.color == self.color,
|
|
|
|
"Attempted to place card on firework of wrong color!"
|
|
|
|
);
|
|
|
|
assert!(
|
|
|
|
Some(card.value) == self.desired_value(),
|
|
|
|
"Attempted to place card of wrong value on firework!"
|
|
|
|
);
|
|
|
|
|
|
|
|
self.cards.place(card);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
impl fmt::Display for Firework {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
2016-03-09 19:27:40 +01:00
|
|
|
if self.complete() {
|
2016-03-07 07:00:23 +01:00
|
|
|
write!(f, "{} firework complete!", self.color)
|
2016-03-09 19:27:40 +01:00
|
|
|
} else {
|
|
|
|
write!(f, "{} firework at {}", self.color, self.top_value())
|
2016-03-07 07:00:23 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-06 07:49:40 +01:00
|
|
|
#[derive(Debug)]
|
2016-03-07 01:14:47 +01:00
|
|
|
pub enum Hinted {
|
|
|
|
Color(Color),
|
|
|
|
Value(Value),
|
|
|
|
}
|
|
|
|
impl fmt::Display for Hinted {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
match self {
|
|
|
|
&Hinted::Color(color) => { write!(f, "{}", color) }
|
|
|
|
&Hinted::Value(value) => { write!(f, "{}", value) }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct Hint {
|
|
|
|
pub player: Player,
|
|
|
|
pub hinted: Hinted,
|
2016-03-06 07:49:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// represents the choice a player made in a given turn
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub enum TurnChoice {
|
2016-03-07 01:14:47 +01:00
|
|
|
Hint(Hint),
|
2016-03-06 07:49:40 +01:00
|
|
|
Discard(usize),
|
|
|
|
Play(usize),
|
|
|
|
}
|
|
|
|
|
|
|
|
// represents a turn taken in the game
|
|
|
|
pub struct Turn<'a> {
|
|
|
|
pub player: &'a Player,
|
|
|
|
pub choice: &'a TurnChoice,
|
|
|
|
}
|
|
|
|
|
|
|
|
// represents possible settings for the game
|
2016-03-06 01:54:46 +01:00
|
|
|
pub struct GameOptions {
|
2016-03-06 03:49:14 +01:00
|
|
|
pub num_players: u32,
|
|
|
|
pub hand_size: u32,
|
|
|
|
// when hits 0, you cannot hint
|
2016-03-06 07:49:40 +01:00
|
|
|
pub num_hints: u32,
|
2016-03-06 03:49:14 +01:00
|
|
|
// when hits 0, you lose
|
2016-03-06 07:49:40 +01:00
|
|
|
pub num_lives: u32,
|
2016-03-11 07:49:20 +01:00
|
|
|
// TODO:
|
|
|
|
// pub allow_empty_hints: bool,
|
2016-03-06 01:54:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// The state of a given player: all other players may see this
|
2016-03-06 07:49:40 +01:00
|
|
|
#[derive(Debug)]
|
2016-03-06 03:49:14 +01:00
|
|
|
pub struct PlayerState {
|
2016-03-06 07:49:40 +01:00
|
|
|
// the player's actual hand
|
2016-03-07 01:14:47 +01:00
|
|
|
hand: Cards,
|
2016-03-06 07:49:40 +01:00
|
|
|
// represents what is common knowledge about the player's hand
|
2016-03-07 01:14:47 +01:00
|
|
|
info: CardsInfo,
|
|
|
|
}
|
2016-03-09 19:27:40 +01:00
|
|
|
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.0 {
|
|
|
|
let info : &CardInfo = &self.info.0[i];
|
|
|
|
try!(f.write_str(&format!("{} =? {: <15} ", card, info)));
|
|
|
|
i += 1;
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
2016-03-07 01:14:47 +01:00
|
|
|
impl PlayerState {
|
|
|
|
pub fn new(hand: Cards) -> PlayerState {
|
|
|
|
let infos = (0..hand.size()).map(|_| {
|
|
|
|
CardInfo::new()
|
|
|
|
}).collect::<Vec<_>>();
|
|
|
|
PlayerState {
|
|
|
|
hand: hand,
|
|
|
|
info: CardsInfo::from(infos),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn take(&mut self, index: usize) -> (Card, CardInfo) {
|
|
|
|
let card = self.hand.take(index);
|
|
|
|
let info = self.info.take(index);
|
|
|
|
(card, info)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn place(&mut self, card: Card) {
|
|
|
|
self.hand.place(card);
|
|
|
|
self.info.place(CardInfo::new());
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn reveal(&mut self, hinted: &Hinted) {
|
|
|
|
match hinted {
|
|
|
|
&Hinted::Color(ref color) => {
|
|
|
|
let mut i = 0;
|
|
|
|
for card in &self.hand.0 {
|
|
|
|
self.info.0[i].color_info.mark(
|
|
|
|
color,
|
|
|
|
card.color == *color
|
|
|
|
);
|
|
|
|
i += 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
&Hinted::Value(ref value) => {
|
|
|
|
let mut i = 0;
|
|
|
|
for card in &self.hand.0 {
|
|
|
|
self.info.0[i].value_info.mark(
|
|
|
|
value,
|
|
|
|
card.value == *value
|
|
|
|
);
|
|
|
|
i += 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
2016-03-06 01:54:46 +01:00
|
|
|
}
|
|
|
|
|
2016-03-07 06:44:17 +01:00
|
|
|
fn new_deck() -> Cards {
|
|
|
|
let mut deck: Cards = Cards::from(Vec::new());
|
|
|
|
|
|
|
|
for color in COLORS.iter() {
|
|
|
|
for &(value, count) in VALUE_COUNTS.iter() {
|
|
|
|
for _ in 0..count {
|
|
|
|
deck.place(Card {color: color, value: value});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
deck.shuffle();
|
|
|
|
info!("Created deck: {}", deck);
|
|
|
|
deck
|
|
|
|
}
|
|
|
|
|
2016-03-06 03:49:14 +01:00
|
|
|
// State of everything except the player's hands
|
2016-03-06 07:49:40 +01:00
|
|
|
// Is all completely common knowledge
|
|
|
|
#[derive(Debug)]
|
2016-03-06 03:49:14 +01:00
|
|
|
pub struct BoardState {
|
2016-03-06 11:13:08 +01:00
|
|
|
deck: Cards,
|
|
|
|
pub discard: Cards,
|
2016-03-07 07:00:23 +01:00
|
|
|
pub fireworks: HashMap<Color, Firework>,
|
2016-03-06 03:49:14 +01:00
|
|
|
|
2016-03-06 07:49:40 +01:00
|
|
|
pub num_players: u32,
|
|
|
|
|
|
|
|
// which turn is it?
|
|
|
|
pub turn: u32,
|
2016-03-06 01:54:46 +01:00
|
|
|
// // whose turn is it?
|
2016-03-06 07:49:40 +01:00
|
|
|
pub player: Player,
|
2016-03-06 03:49:14 +01:00
|
|
|
|
2016-03-06 07:49:40 +01:00
|
|
|
pub hints_total: u32,
|
2016-03-06 03:49:14 +01:00
|
|
|
pub hints_remaining: u32,
|
2016-03-06 07:49:40 +01:00
|
|
|
pub lives_total: u32,
|
2016-03-06 03:49:14 +01:00
|
|
|
pub lives_remaining: u32,
|
2016-03-07 07:00:23 +01:00
|
|
|
// TODO:
|
|
|
|
// pub turn_history: Vec<TurnChoice>,
|
2016-03-06 03:49:14 +01:00
|
|
|
// only relevant when deck runs out
|
2016-03-09 19:27:40 +01:00
|
|
|
pub deckless_turns_remaining: u32,
|
2016-03-06 07:49:40 +01:00
|
|
|
}
|
2016-03-07 06:44:17 +01:00
|
|
|
impl BoardState {
|
|
|
|
pub fn new(opts: &GameOptions) -> BoardState {
|
2016-03-07 07:00:23 +01:00
|
|
|
let mut fireworks : HashMap<Color, Firework> = HashMap::new();
|
2016-03-07 06:44:17 +01:00
|
|
|
for color in COLORS.iter() {
|
2016-03-07 07:00:23 +01:00
|
|
|
fireworks.insert(color, Firework::new(color));
|
2016-03-07 06:44:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
BoardState {
|
|
|
|
deck: new_deck(),
|
|
|
|
fireworks: fireworks,
|
|
|
|
discard: Cards::new(),
|
|
|
|
num_players: opts.num_players,
|
|
|
|
player: 0,
|
|
|
|
turn: 1,
|
|
|
|
hints_total: opts.num_hints,
|
|
|
|
hints_remaining: opts.num_hints,
|
|
|
|
lives_total: opts.num_lives,
|
|
|
|
lives_remaining: opts.num_lives,
|
|
|
|
// number of turns to play with deck length ran out
|
|
|
|
deckless_turns_remaining: opts.num_players + 1,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn try_add_hint(&mut self) {
|
|
|
|
if self.hints_remaining < self.hints_total {
|
|
|
|
self.hints_remaining += 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// returns whether a card would place on a firework
|
|
|
|
pub fn is_playable(&self, card: &Card) -> bool {
|
|
|
|
let firework = self.fireworks.get(card.color).unwrap();
|
2016-03-07 07:00:23 +01:00
|
|
|
Some(card.value) == firework.desired_value()
|
2016-03-07 06:44:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get_players(&self) -> Vec<Player> {
|
|
|
|
(0..self.num_players).collect::<Vec<_>>()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn score(&self) -> Score {
|
|
|
|
let mut score = 0;
|
|
|
|
for (_, firework) in &self.fireworks {
|
2016-03-07 07:00:23 +01:00
|
|
|
score += firework.score();
|
2016-03-07 06:44:17 +01:00
|
|
|
}
|
|
|
|
score as u32
|
|
|
|
}
|
|
|
|
|
2016-03-09 19:27:40 +01:00
|
|
|
pub fn deck_size(&self) -> usize {
|
|
|
|
self.deck.size()
|
|
|
|
}
|
|
|
|
|
2016-03-07 06:44:17 +01:00
|
|
|
pub fn player_to_left(&self, player: &Player) -> Player {
|
|
|
|
(player + 1) % self.num_players
|
|
|
|
}
|
|
|
|
pub fn player_to_right(&self, player: &Player) -> Player {
|
|
|
|
(player - 1) % self.num_players
|
|
|
|
}
|
|
|
|
}
|
2016-03-09 19:27:40 +01:00
|
|
|
impl fmt::Display for BoardState {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
try!(f.write_str(&format!(
|
|
|
|
"Turn {} (Player {}'s turn):\n", self.turn, self.player
|
|
|
|
)));
|
|
|
|
let deck_size = self.deck_size();
|
|
|
|
try!(f.write_str(&format!(
|
|
|
|
"{} cards remaining in deck\n", deck_size
|
|
|
|
)));
|
|
|
|
if deck_size == 0 {
|
|
|
|
try!(f.write_str(&format!(
|
|
|
|
"Deck is empty. {} turns remaining in game\n", self.deckless_turns_remaining
|
|
|
|
)));
|
|
|
|
}
|
|
|
|
try!(f.write_str(&format!(
|
|
|
|
"{}/{} hints remaining\n", self.hints_remaining, self.hints_total
|
|
|
|
)));
|
|
|
|
try!(f.write_str(&format!(
|
|
|
|
"{}/{} lives remaining\n", self.lives_remaining, self.lives_total
|
|
|
|
)));
|
|
|
|
try!(f.write_str("Fireworks:\n"));
|
|
|
|
for (_, firework) in &self.fireworks {
|
|
|
|
try!(f.write_str(&format!(" {}\n", firework)));
|
|
|
|
}
|
|
|
|
// discard: Cards::new(),
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
2016-03-06 07:49:40 +01:00
|
|
|
|
|
|
|
// complete game view of a given player
|
|
|
|
// state will be borrowed GameState
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct GameStateView<'a> {
|
|
|
|
// the player whose view it is
|
|
|
|
pub player: Player,
|
2016-03-06 11:13:08 +01:00
|
|
|
// what is known about their own hand (and thus common knowledge)
|
|
|
|
pub info: &'a CardsInfo,
|
|
|
|
// the cards of the other players, as well as the information they have
|
2016-03-06 07:49:40 +01:00
|
|
|
pub other_player_states: HashMap<Player, &'a PlayerState>,
|
|
|
|
// board state
|
|
|
|
pub board: &'a BoardState,
|
2016-03-06 03:49:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// complete game state (known to nobody!)
|
2016-03-06 07:49:40 +01:00
|
|
|
#[derive(Debug)]
|
2016-03-06 03:49:14 +01:00
|
|
|
pub struct GameState {
|
|
|
|
pub player_states: HashMap<Player, PlayerState>,
|
2016-03-06 07:49:40 +01:00
|
|
|
pub board: BoardState,
|
2016-03-06 03:49:14 +01:00
|
|
|
}
|
2016-03-09 19:27:40 +01:00
|
|
|
impl fmt::Display for GameState {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
try!(f.write_str("==========================\n"));
|
|
|
|
try!(f.write_str("Hands:\n"));
|
|
|
|
try!(f.write_str("==========================\n"));
|
|
|
|
for player in 0..self.board.num_players {
|
|
|
|
let state = &self.player_states.get(&player).unwrap();
|
|
|
|
try!(f.write_str(&format!("player {} {}\n", player, state)));
|
|
|
|
}
|
|
|
|
try!(f.write_str("==========================\n"));
|
|
|
|
try!(f.write_str("Board:\n"));
|
|
|
|
try!(f.write_str("==========================\n"));
|
|
|
|
try!(f.write_str(&format!("{}", self.board)));
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
2016-03-06 03:49:14 +01:00
|
|
|
|
2016-03-06 07:49:40 +01:00
|
|
|
pub type Score = u32;
|
2016-03-06 01:54:46 +01:00
|
|
|
|
|
|
|
impl GameState {
|
2016-03-06 10:35:19 +01:00
|
|
|
pub fn new(opts: &GameOptions) -> GameState {
|
2016-03-07 06:44:17 +01:00
|
|
|
let mut board = BoardState::new(opts);
|
2016-03-06 03:49:14 +01:00
|
|
|
|
|
|
|
let mut player_states : HashMap<Player, PlayerState> = HashMap::new();
|
|
|
|
for i in 0..opts.num_players {
|
2016-03-06 07:49:40 +01:00
|
|
|
let raw_hand = (0..opts.hand_size).map(|_| {
|
2016-03-06 03:49:14 +01:00
|
|
|
// we can assume the deck is big enough to draw initial hands
|
2016-03-07 06:44:17 +01:00
|
|
|
board.deck.draw().unwrap()
|
2016-03-06 07:49:40 +01:00
|
|
|
}).collect::<Vec<_>>();
|
2016-03-07 01:14:47 +01:00
|
|
|
player_states.insert(
|
|
|
|
i, PlayerState::new(Cards::from(raw_hand)),
|
|
|
|
);
|
2016-03-06 03:49:14 +01:00
|
|
|
}
|
|
|
|
|
2016-03-06 01:54:46 +01:00
|
|
|
GameState {
|
2016-03-06 03:49:14 +01:00
|
|
|
player_states: player_states,
|
2016-03-07 06:44:17 +01:00
|
|
|
board: board,
|
2016-03-06 01:54:46 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-06 07:49:40 +01:00
|
|
|
pub fn get_players(&self) -> Vec<Player> {
|
2016-03-07 06:44:17 +01:00
|
|
|
self.board.get_players()
|
2016-03-06 07:49:40 +01:00
|
|
|
}
|
2016-03-06 03:49:14 +01:00
|
|
|
|
2016-03-06 07:49:40 +01:00
|
|
|
pub fn is_over(&self) -> bool {
|
|
|
|
// TODO: add condition that fireworks cannot be further completed?
|
|
|
|
(self.board.lives_remaining == 0) ||
|
|
|
|
(self.board.deckless_turns_remaining == 0)
|
|
|
|
}
|
2016-03-06 01:54:46 +01:00
|
|
|
|
2016-03-06 07:49:40 +01:00
|
|
|
pub fn score(&self) -> Score {
|
2016-03-07 06:44:17 +01:00
|
|
|
self.board.score()
|
2016-03-06 07:49:40 +01:00
|
|
|
}
|
2016-03-06 01:54:46 +01:00
|
|
|
|
2016-03-06 07:49:40 +01:00
|
|
|
// get the game state view of a particular player
|
|
|
|
pub fn get_view(&self, player: Player) -> GameStateView {
|
|
|
|
let mut other_player_states = HashMap::new();
|
|
|
|
for (other_player, state) in &self.player_states {
|
|
|
|
if player != *other_player {
|
|
|
|
other_player_states.insert(player, state);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
GameStateView {
|
|
|
|
player: player,
|
2016-03-06 11:13:08 +01:00
|
|
|
info: &self.player_states.get(&player).unwrap().info,
|
2016-03-06 07:49:40 +01:00
|
|
|
other_player_states: other_player_states,
|
|
|
|
board: &self.board,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// takes a card from the player's hand, and replaces it if possible
|
|
|
|
fn take_from_hand(&mut self, index: usize) -> Card {
|
2016-03-06 11:13:08 +01:00
|
|
|
let ref mut state = self.player_states.get_mut(&self.board.player).unwrap();
|
2016-03-07 01:14:47 +01:00
|
|
|
let (card, _) = state.take(index);
|
2016-03-06 07:49:40 +01:00
|
|
|
if let Some(new_card) = self.board.deck.draw() {
|
2016-03-07 06:44:17 +01:00
|
|
|
info!("Drew new card, {}", new_card);
|
2016-03-07 01:14:47 +01:00
|
|
|
state.place(new_card);
|
2016-03-06 07:49:40 +01:00
|
|
|
}
|
|
|
|
card
|
|
|
|
}
|
|
|
|
|
2016-03-06 10:35:19 +01:00
|
|
|
pub fn process_choice(&mut self, choice: &TurnChoice) {
|
2016-03-07 01:14:47 +01:00
|
|
|
info!("Player {}'s move", self.board.player);
|
2016-03-07 06:44:17 +01:00
|
|
|
match choice {
|
|
|
|
&TurnChoice::Hint(ref hint) => {
|
|
|
|
assert!(self.board.hints_remaining > 0,
|
|
|
|
"Tried to hint with no hints remaining");
|
2016-03-06 07:49:40 +01:00
|
|
|
self.board.hints_remaining -= 1;
|
2016-03-07 01:14:47 +01:00
|
|
|
info!("Hint to player {}, about {}", hint.player, hint.hinted);
|
|
|
|
|
2016-03-07 06:44:17 +01:00
|
|
|
assert!(self.board.player != hint.player,
|
|
|
|
format!("Player {} gave a hint to himself", hint.player));
|
|
|
|
|
2016-03-07 01:14:47 +01:00
|
|
|
let ref mut state = self.player_states.get_mut(&hint.player).unwrap();
|
|
|
|
state.reveal(&hint.hinted);
|
2016-03-06 07:49:40 +01:00
|
|
|
}
|
2016-03-07 06:44:17 +01:00
|
|
|
&TurnChoice::Discard(index) => {
|
2016-03-06 07:49:40 +01:00
|
|
|
let card = self.take_from_hand(index);
|
2016-03-07 06:44:17 +01:00
|
|
|
info!("Discard card in position {}, which is {}", index, card);
|
2016-03-06 07:49:40 +01:00
|
|
|
self.board.discard.place(card);
|
|
|
|
|
2016-03-07 06:44:17 +01:00
|
|
|
self.board.try_add_hint();
|
2016-03-06 07:49:40 +01:00
|
|
|
}
|
2016-03-07 06:44:17 +01:00
|
|
|
&TurnChoice::Play(index) => {
|
2016-03-06 07:49:40 +01:00
|
|
|
let card = self.take_from_hand(index);
|
2016-03-06 10:35:19 +01:00
|
|
|
|
2016-03-07 01:14:47 +01:00
|
|
|
info!(
|
2016-03-07 06:44:17 +01:00
|
|
|
"Playing card at position {}, which is {}",
|
2016-03-06 10:35:19 +01:00
|
|
|
index, card
|
|
|
|
);
|
|
|
|
|
2016-03-06 07:49:40 +01:00
|
|
|
let mut firework_made = false;
|
|
|
|
|
2016-03-07 06:44:17 +01:00
|
|
|
if self.board.is_playable(&card) {
|
2016-03-06 07:49:40 +01:00
|
|
|
let ref mut firework = self.board.fireworks.get_mut(&card.color).unwrap();
|
2016-03-07 06:44:17 +01:00
|
|
|
firework_made = card.value == FINAL_VALUE;
|
|
|
|
info!("Successfully played {}!", card);
|
|
|
|
if firework_made {
|
|
|
|
info!("Firework complete for {}!", card.color);
|
2016-03-06 07:49:40 +01:00
|
|
|
}
|
2016-03-07 06:44:17 +01:00
|
|
|
firework.place(card);
|
|
|
|
} else {
|
|
|
|
self.board.discard.place(card);
|
|
|
|
self.board.lives_remaining -= 1;
|
|
|
|
info!(
|
|
|
|
"Removing a life! Lives remaining: {}",
|
|
|
|
self.board.lives_remaining
|
|
|
|
);
|
2016-03-06 07:49:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if firework_made {
|
2016-03-07 06:44:17 +01:00
|
|
|
self.board.try_add_hint();
|
2016-03-06 07:49:40 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if self.board.deck.size() == 0 {
|
|
|
|
self.board.deckless_turns_remaining -= 1;
|
|
|
|
}
|
|
|
|
self.board.turn += 1;
|
2016-03-07 06:44:17 +01:00
|
|
|
self.board.player = {
|
|
|
|
let cur = self.board.player;
|
|
|
|
self.board.player_to_left(&cur)
|
|
|
|
};
|
2016-03-06 07:49:40 +01:00
|
|
|
assert_eq!((self.board.turn - 1) % self.board.num_players, self.board.player);
|
|
|
|
|
|
|
|
}
|
2016-03-06 01:54:46 +01:00
|
|
|
}
|