move stuff to boardstate
This commit is contained in:
parent
49627b91b6
commit
b4d949ff2f
3 changed files with 120 additions and 87 deletions
194
src/game.rs
194
src/game.rs
|
@ -64,9 +64,10 @@ impl <T> From<Vec<T>> for Pile<T> {
|
||||||
}
|
}
|
||||||
impl <T> fmt::Display for Pile<T> where T: fmt::Display {
|
impl <T> fmt::Display for Pile<T> where T: fmt::Display {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
write!(f, "[");
|
// surely doing unwraps is improper..
|
||||||
|
write!(f, "[").unwrap();
|
||||||
for item in &self.0 {
|
for item in &self.0 {
|
||||||
write!(f, "{}, ", item);
|
write!(f, "{}, ", item).unwrap();
|
||||||
}
|
}
|
||||||
write!(f, "] ")
|
write!(f, "] ")
|
||||||
}
|
}
|
||||||
|
@ -179,6 +180,21 @@ impl PlayerState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
// 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)]
|
#[derive(Debug)]
|
||||||
|
@ -201,6 +217,65 @@ pub struct BoardState {
|
||||||
// only relevant when deck runs out
|
// only relevant when deck runs out
|
||||||
deckless_turns_remaining: u32,
|
deckless_turns_remaining: u32,
|
||||||
}
|
}
|
||||||
|
impl BoardState {
|
||||||
|
pub fn new(opts: &GameOptions) -> BoardState {
|
||||||
|
let mut fireworks : HashMap<Color, Cards> = HashMap::new();
|
||||||
|
for color in COLORS.iter() {
|
||||||
|
let mut firework = Cards::new();
|
||||||
|
let card = Card { value: 0, color: color };
|
||||||
|
firework.place(card);
|
||||||
|
fireworks.insert(color, firework);
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
let under_card = firework.top().unwrap();
|
||||||
|
card.value == under_card.value + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
// subtract one to account for the 0 we pushed
|
||||||
|
score += firework.size() - 1;
|
||||||
|
}
|
||||||
|
score as u32
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// complete game view of a given player
|
// complete game view of a given player
|
||||||
// state will be borrowed GameState
|
// state will be borrowed GameState
|
||||||
|
@ -227,63 +302,27 @@ pub type Score = u32;
|
||||||
|
|
||||||
impl GameState {
|
impl GameState {
|
||||||
pub fn new(opts: &GameOptions) -> GameState {
|
pub fn new(opts: &GameOptions) -> GameState {
|
||||||
let mut deck = GameState::make_deck();
|
let mut board = BoardState::new(opts);
|
||||||
|
|
||||||
let mut player_states : HashMap<Player, PlayerState> = HashMap::new();
|
let mut player_states : HashMap<Player, PlayerState> = HashMap::new();
|
||||||
for i in 0..opts.num_players {
|
for i in 0..opts.num_players {
|
||||||
let raw_hand = (0..opts.hand_size).map(|_| {
|
let raw_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
|
||||||
deck.draw().unwrap()
|
board.deck.draw().unwrap()
|
||||||
}).collect::<Vec<_>>();
|
}).collect::<Vec<_>>();
|
||||||
player_states.insert(
|
player_states.insert(
|
||||||
i, PlayerState::new(Cards::from(raw_hand)),
|
i, PlayerState::new(Cards::from(raw_hand)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut fireworks : HashMap<Color, Cards> = HashMap::new();
|
|
||||||
for color in COLORS.iter() {
|
|
||||||
let mut firework = Cards::new();
|
|
||||||
let card = Card { value: 0, color: color };
|
|
||||||
firework.place(card);
|
|
||||||
fireworks.insert(color, firework);
|
|
||||||
}
|
|
||||||
|
|
||||||
GameState {
|
GameState {
|
||||||
player_states: player_states,
|
player_states: player_states,
|
||||||
board: BoardState {
|
board: board,
|
||||||
deck: 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 make_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
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_players(&self) -> Vec<Player> {
|
pub fn get_players(&self) -> Vec<Player> {
|
||||||
(0..self.board.num_players).collect::<Vec<_>>()
|
self.board.get_players()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_over(&self) -> bool {
|
pub fn is_over(&self) -> bool {
|
||||||
|
@ -293,12 +332,7 @@ impl GameState {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn score(&self) -> Score {
|
pub fn score(&self) -> Score {
|
||||||
let mut score = 0;
|
self.board.score()
|
||||||
for (_, firework) in &self.board.fireworks {
|
|
||||||
// subtract one to account for the 0 we pushed
|
|
||||||
score += firework.size() - 1;
|
|
||||||
}
|
|
||||||
score as u32
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the game state view of a particular player
|
// get the game state view of a particular player
|
||||||
|
@ -322,68 +356,63 @@ impl GameState {
|
||||||
let ref mut state = self.player_states.get_mut(&self.board.player).unwrap();
|
let ref mut state = self.player_states.get_mut(&self.board.player).unwrap();
|
||||||
let (card, _) = state.take(index);
|
let (card, _) = state.take(index);
|
||||||
if let Some(new_card) = self.board.deck.draw() {
|
if let Some(new_card) = self.board.deck.draw() {
|
||||||
|
info!("Drew new card, {}", new_card);
|
||||||
state.place(new_card);
|
state.place(new_card);
|
||||||
}
|
}
|
||||||
card
|
card
|
||||||
}
|
}
|
||||||
|
|
||||||
fn try_add_hint(&mut self) {
|
|
||||||
if self.board.hints_remaining < self.board.hints_total {
|
|
||||||
self.board.hints_remaining += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn process_choice(&mut self, choice: &TurnChoice) {
|
pub fn process_choice(&mut self, choice: &TurnChoice) {
|
||||||
info!("Player {}'s move", self.board.player);
|
info!("Player {}'s move", self.board.player);
|
||||||
match *choice {
|
match choice {
|
||||||
TurnChoice::Hint(ref hint) => {
|
&TurnChoice::Hint(ref hint) => {
|
||||||
assert!(self.board.hints_remaining > 0);
|
assert!(self.board.hints_remaining > 0,
|
||||||
|
"Tried to hint with no hints remaining");
|
||||||
self.board.hints_remaining -= 1;
|
self.board.hints_remaining -= 1;
|
||||||
info!("Hint to player {}, about {}", hint.player, hint.hinted);
|
info!("Hint to player {}, about {}", hint.player, hint.hinted);
|
||||||
|
|
||||||
|
assert!(self.board.player != hint.player,
|
||||||
|
format!("Player {} gave a hint to himself", hint.player));
|
||||||
|
|
||||||
let ref mut state = self.player_states.get_mut(&hint.player).unwrap();
|
let ref mut state = self.player_states.get_mut(&hint.player).unwrap();
|
||||||
state.reveal(&hint.hinted);
|
state.reveal(&hint.hinted);
|
||||||
}
|
}
|
||||||
TurnChoice::Discard(index) => {
|
&TurnChoice::Discard(index) => {
|
||||||
let card = self.take_from_hand(index);
|
let card = self.take_from_hand(index);
|
||||||
info!("Discard {}, which is {}", index, card);
|
info!("Discard card in position {}, which is {}", index, card);
|
||||||
self.board.discard.place(card);
|
self.board.discard.place(card);
|
||||||
|
|
||||||
self.try_add_hint();
|
self.board.try_add_hint();
|
||||||
}
|
}
|
||||||
TurnChoice::Play(index) => {
|
&TurnChoice::Play(index) => {
|
||||||
let card = self.take_from_hand(index);
|
let card = self.take_from_hand(index);
|
||||||
|
|
||||||
info!(
|
info!(
|
||||||
"Playing card at {}, which is {}",
|
"Playing card at position {}, which is {}",
|
||||||
index, card
|
index, card
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut firework_made = false;
|
let mut firework_made = false;
|
||||||
|
|
||||||
{
|
if self.board.is_playable(&card) {
|
||||||
let ref mut firework = self.board.fireworks.get_mut(&card.color).unwrap();
|
let ref mut firework = self.board.fireworks.get_mut(&card.color).unwrap();
|
||||||
|
firework_made = card.value == FINAL_VALUE;
|
||||||
let playable = {
|
info!("Successfully played {}!", card);
|
||||||
let under_card = firework.top().unwrap();
|
if firework_made {
|
||||||
card.value == under_card.value + 1
|
info!("Firework complete for {}!", card.color);
|
||||||
};
|
|
||||||
|
|
||||||
if playable {
|
|
||||||
firework_made = card.value == FINAL_VALUE;
|
|
||||||
firework.place(card);
|
|
||||||
} else {
|
|
||||||
self.board.discard.place(card);
|
|
||||||
self.board.lives_remaining -= 1;
|
|
||||||
info!(
|
|
||||||
"Removing a life! Lives remaining: {}",
|
|
||||||
self.board.lives_remaining
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
firework.place(card);
|
||||||
|
} else {
|
||||||
|
self.board.discard.place(card);
|
||||||
|
self.board.lives_remaining -= 1;
|
||||||
|
info!(
|
||||||
|
"Removing a life! Lives remaining: {}",
|
||||||
|
self.board.lives_remaining
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if firework_made {
|
if firework_made {
|
||||||
self.try_add_hint();
|
self.board.try_add_hint();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -392,7 +421,10 @@ impl GameState {
|
||||||
self.board.deckless_turns_remaining -= 1;
|
self.board.deckless_turns_remaining -= 1;
|
||||||
}
|
}
|
||||||
self.board.turn += 1;
|
self.board.turn += 1;
|
||||||
self.board.player = (self.board.player + 1) % self.board.num_players;
|
self.board.player = {
|
||||||
|
let cur = self.board.player;
|
||||||
|
self.board.player_to_left(&cur)
|
||||||
|
};
|
||||||
assert_eq!((self.board.turn - 1) % self.board.num_players, self.board.player);
|
assert_eq!((self.board.turn - 1) % self.board.num_players, self.board.player);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,11 +6,13 @@ mod game;
|
||||||
mod strategies;
|
mod strategies;
|
||||||
mod info;
|
mod info;
|
||||||
|
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
use log::LogLevel::{Trace, Debug, Info, Warn, Error};
|
||||||
|
|
||||||
struct SimpleLogger;
|
struct SimpleLogger;
|
||||||
impl log::Log for SimpleLogger {
|
impl log::Log for SimpleLogger {
|
||||||
fn enabled(&self, metadata: &log::LogMetadata) -> bool {
|
fn enabled(&self, metadata: &log::LogMetadata) -> bool {
|
||||||
true
|
metadata.level() <= Debug
|
||||||
}
|
}
|
||||||
|
|
||||||
fn log(&self, record: &log::LogRecord) {
|
fn log(&self, record: &log::LogRecord) {
|
||||||
|
@ -22,10 +24,9 @@ impl log::Log for SimpleLogger {
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
log::set_logger(|max_log_level| {
|
log::set_logger(|max_log_level| {
|
||||||
// Trace, Debug, Info, Warn, ...
|
max_log_level.set(log::LogLevelFilter::Trace);
|
||||||
max_log_level.set(log::LogLevelFilter::Info);
|
|
||||||
Box::new(SimpleLogger)
|
Box::new(SimpleLogger)
|
||||||
});
|
}).unwrap();
|
||||||
|
|
||||||
let opts = game::GameOptions {
|
let opts = game::GameOptions {
|
||||||
num_players: 4,
|
num_players: 4,
|
||||||
|
|
|
@ -99,7 +99,7 @@ impl Strategy for RandomStrategy {
|
||||||
fn initialize(_: &Player, _: &GameStateView) -> () {
|
fn initialize(_: &Player, _: &GameStateView) -> () {
|
||||||
()
|
()
|
||||||
}
|
}
|
||||||
fn decide(_: &mut (), _: &Player, view: &GameStateView) -> TurnChoice {
|
fn decide(_: &mut (), me: &Player, view: &GameStateView) -> TurnChoice {
|
||||||
let p = rand::random::<f64>();
|
let p = rand::random::<f64>();
|
||||||
if p < 0.4 {
|
if p < 0.4 {
|
||||||
if view.board.hints_remaining > 0 {
|
if view.board.hints_remaining > 0 {
|
||||||
|
@ -112,7 +112,7 @@ impl Strategy for RandomStrategy {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
TurnChoice::Hint(Hint {
|
TurnChoice::Hint(Hint {
|
||||||
player: 0,
|
player: view.board.player_to_left(&me),
|
||||||
hinted: hinted,
|
hinted: hinted,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
|
Loading…
Reference in a new issue