use crate::{
cards::deck::{Deck, DeckConfig},
game::{
action::GameAction,
error::{ActionError, FailedActionError, GameError, InternalError},
rules::GameRules,
score::{RoundScore, VariantPlayerScore},
},
player::Player,
};
use std::collections::HashMap;
use std::fmt::Debug;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct GameState<P: VariantPlayerScore, R: GameRules<VariantScore = P>> {
pub(crate) phase: GamePhase,
pub(crate) players: Vec<Player>,
pub(crate) deck: Deck,
pub(crate) current_player: usize,
pub(crate) current_round: usize,
pub(crate) round_scores: HashMap<usize, RoundScore<P>>,
pub(crate) variant_state: R::VariantState,
}
impl<P: VariantPlayerScore, R: GameRules<VariantScore = P>> GameState<P, R>
where
R::VariantState: VariantState<P, R>,
{
pub fn initialize(
player_ids: Vec<usize>,
deck_config: DeckConfig,
variant_state: R::VariantState,
) -> Self {
let players = player_ids
.into_iter()
.map(|id| Player {
id,
cards: vec![],
melds: vec![],
active: true,
joined_in_round: 0,
})
.collect();
let deck = Deck::new(deck_config);
Self {
phase: GamePhase::RoundEnd,
players,
deck,
current_round: 0,
current_player: 0,
round_scores: HashMap::new(),
variant_state,
}
}
pub fn validate_action(&self, action: &GameAction) -> Result<(), ActionError> {
match (self.phase, action) {
(GamePhase::Draw, GameAction::DrawDeck(_)) => (),
(GamePhase::Draw, GameAction::DrawDiscardPile(_)) => (),
(GamePhase::Play, GameAction::FormMeld(_)) => (),
(GamePhase::Play, GameAction::FormMelds(_)) => (),
(GamePhase::Play, GameAction::LayOff(_)) => (),
(GamePhase::Play, GameAction::Discard(_)) => (),
_ => {
let err = FailedActionError::InvalidGamePhase {
current_phase: self.phase,
};
return Err(ActionError::FailedAction(err));
}
};
R::VariantState::validate_action(self, action)
}
pub fn start_new_round(
&mut self,
cards_to_deal: usize,
starting_player_index: usize,
) -> Result<(), GameError> {
if self.phase != GamePhase::RoundEnd {
return Err(GameError::WrongGamePhase);
}
self.deck.reset();
for player in &mut self.players {
if !player.active && player.joined_in_round == self.current_round {
player.active = true;
}
player.melds = Vec::new();
player.cards = self
.deck
.draw(cards_to_deal)
.map_err(|err| InternalError::NoCardsInDeckOrDiscardPile)?;
}
self.current_player = starting_player_index;
self.phase = GamePhase::Draw;
self.current_round += 1;
Ok(())
}
pub fn get_current_player_mut(&mut self) -> Result<&mut Player, InternalError> {
self.players
.get_mut(self.current_player)
.ok_or(InternalError::InvalidCurrentPlayer {
current: self.current_player,
})
}
pub fn get_current_player(&self) -> Result<&Player, InternalError> {
self.players
.get(self.current_player)
.ok_or(InternalError::InvalidCurrentPlayer {
current: self.current_player,
})
}
pub fn to_next_player(&mut self) {
let mut next_player = (self.current_player + 1) % self.players.len();
while !self.players[next_player].active {
next_player = (self.current_player + 1) % self.players.len();
}
self.current_player = next_player;
}
pub fn phase(&self) -> GamePhase {
self.phase
}
pub fn players(&self) -> &Vec<Player> {
&self.players
}
pub fn deck(&self) -> &Deck {
&self.deck
}
pub fn current_player_index(&self) -> usize {
self.current_player
}
pub fn current_round(&self) -> usize {
self.current_round
}
pub fn round_scores(&self) -> &HashMap<usize, RoundScore<P>> {
&self.round_scores
}
pub fn variant_state(&self) -> &R::VariantState {
&self.variant_state
}
}
pub trait VariantState<P: VariantPlayerScore, R: GameRules<VariantState = Self, VariantScore = P>>:
Sized + Clone + Debug + Eq
{
fn validate_action(state: &GameState<P, R>, action: &GameAction) -> Result<(), ActionError> {
Ok(())
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum GamePhase {
Draw,
Play,
RoundEnd,
GameEnd,
}