hanabi.rs/src/game.rs

400 lines
11 KiB
Rust
Raw Normal View History

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 {
write!(f, "[");
for item in &self.0 {
write!(f, "{}, ", item);
}
write!(f, "] ")
}
}
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-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-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,
}
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-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,
pub fireworks: HashMap<Color, Cards>,
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,
// only relevant when deck runs out
2016-03-06 07:49:40 +01:00
deckless_turns_remaining: u32,
}
// 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-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-06 03:49:14 +01:00
let mut deck = GameState::make_deck();
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
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 11:13:08 +01:00
let mut fireworks : HashMap<Color, Cards> = HashMap::new();
2016-03-06 03:49:14 +01:00
for color in COLORS.iter() {
2016-03-06 11:13:08 +01:00
let mut firework = Cards::new();
2016-03-06 03:49:14 +01:00
let card = Card { value: 0, color: color };
2016-03-06 11:13:08 +01:00
firework.place(card);
fireworks.insert(color, firework);
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-06 07:49:40 +01:00
board: BoardState {
2016-03-06 03:49:14 +01:00
deck: deck,
fireworks: fireworks,
2016-03-06 11:13:08 +01:00
discard: Cards::new(),
2016-03-06 07:49:40 +01:00
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,
2016-03-06 03:49:14 +01:00
}
2016-03-06 01:54:46 +01:00
}
}
2016-03-06 11:13:08 +01:00
fn make_deck() -> Cards {
let mut deck: Cards = Cards::from(Vec::new());
2016-03-06 03:49:14 +01:00
2016-03-06 01:54:46 +01:00
for color in COLORS.iter() {
2016-03-06 03:49:14 +01:00
for &(value, count) in VALUE_COUNTS.iter() {
2016-03-06 07:49:40 +01:00
for _ in 0..count {
deck.place(Card {color: color, value: value});
2016-03-06 01:54:46 +01:00
}
}
};
2016-03-06 03:49:14 +01:00
deck.shuffle();
2016-03-07 01:14:47 +01:00
info!("Created deck: {}", deck);
2016-03-06 01:54:46 +01:00
deck
}
2016-03-06 07:49:40 +01:00
pub fn get_players(&self) -> Vec<Player> {
(0..self.board.num_players).collect::<Vec<_>>()
}
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 {
let mut score = 0;
for (_, firework) in &self.board.fireworks {
2016-03-06 10:35:19 +01:00
// subtract one to account for the 0 we pushed
score += firework.size() - 1;
2016-03-06 07:49:40 +01:00
}
score as u32
}
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 01:14:47 +01:00
state.place(new_card);
2016-03-06 07:49:40 +01:00
}
card
}
fn try_add_hint(&mut self) {
if self.board.hints_remaining < self.board.hints_total {
self.board.hints_remaining += 1;
}
}
2016-03-06 01:54:46 +01:00
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-06 10:35:19 +01:00
match *choice {
2016-03-07 01:14:47 +01:00
TurnChoice::Hint(ref hint) => {
2016-03-06 07:49:40 +01:00
assert!(self.board.hints_remaining > 0);
self.board.hints_remaining -= 1;
2016-03-07 01:14:47 +01:00
info!("Hint to player {}, about {}", hint.player, hint.hinted);
let ref mut state = self.player_states.get_mut(&hint.player).unwrap();
state.reveal(&hint.hinted);
2016-03-06 07:49:40 +01:00
}
TurnChoice::Discard(index) => {
let card = self.take_from_hand(index);
2016-03-07 01:14:47 +01:00
info!("Discard {}, which is {}", index, card);
2016-03-06 07:49:40 +01:00
self.board.discard.place(card);
self.try_add_hint();
}
TurnChoice::Play(index) => {
let card = self.take_from_hand(index);
2016-03-06 10:35:19 +01:00
2016-03-07 01:14:47 +01:00
info!(
"Playing card at {}, 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;
{
let ref mut firework = self.board.fireworks.get_mut(&card.color).unwrap();
let playable = {
let under_card = firework.top().unwrap();
card.value == under_card.value + 1
};
if playable {
firework_made = card.value == FINAL_VALUE;
firework.place(card);
} else {
self.board.discard.place(card);
self.board.lives_remaining -= 1;
2016-03-07 01:14:47 +01:00
info!(
2016-03-06 10:35:19 +01:00
"Removing a life! Lives remaining: {}",
self.board.lives_remaining
);
2016-03-06 07:49:40 +01:00
}
}
if firework_made {
self.try_add_hint();
}
}
}
if self.board.deck.size() == 0 {
self.board.deckless_turns_remaining -= 1;
}
self.board.turn += 1;
self.board.player = (self.board.player + 1) % self.board.num_players;
assert_eq!((self.board.turn - 1) % self.board.num_players, self.board.player);
}
2016-03-06 01:54:46 +01:00
}