use crate::auth::Auth;
-use crate::game::{Action, GameSettings, GameSummary};
-use crate::games::UserAction;
+use crate::game::{Action, GameSettings, GameSummary, UserAction};
use crate::username::Username;
#[derive(Debug, Clone, Deserialize)]
use futures::stream::{Stream, StreamExt, empty, iter, once};
use crate::api::{ClientMessage, ServerMessage};
-use crate::game::GameList;
-use crate::games::{Game, UserAction};
+use crate::game::{Game, GameList, UserAction};
use crate::server::{ActionStatus, ServerState};
use crate::username::Username;
ClientInterest::GameList => match &mut self.client {
ClientState::LoggedIn{state: LoggedInState::InLobby{ref mut game_list}, ..} => {
let from = game_list.games_len();
- match self.server.update_game_list(game_list).await {
- Ok(()) => iter(game_list.update(from)).map(|game| ServerMessage::NewGame{game}).boxed(),
+ match self.server.game_list(from).await {
+ Ok(games) => {
+ for game in games.clone() {
+ game_list.push(game);
+ }
+ iter(games).map(|game| ServerMessage::NewGame{game}).boxed()
+ }
Err(err) => once(async move { ServerMessage::ProtocolError{reason: err.to_string()} }).boxed(),
}
}
Ok(actions) => {
let actions_view: Vec<_> = actions.iter().map(|action| action.view_for(username)).collect();
for action in actions {
- game.take_action(action);
+ if let Err(err) = game.take_action(action) {
+ error!("Action from database failed to apply: {}", err);
+ }
}
iter(actions_view).map(|action| ServerMessage::NewAction{action}).boxed()
}
}
(&mut ClientState::LoggedIn{username, ..}, ClientMessage::JoinLobby{filter}) => {
let mut game_list = GameList::new(filter);
- match self.server.update_game_list(&mut game_list).await {
- Ok(()) => {
- let games = game_list.update(0);
+ match self.server.game_list(0).await {
+ Ok(games) => {
+ for game in games.clone() {
+ game_list.push(game);
+ }
self.client = ClientState::LoggedIn{username: username.clone(), state: LoggedInState::InLobby{game_list}};
ServerMessage::JoinLobbySuccess{games}
}
use redis::{ErrorKind, RedisError, RedisResult};
use crate::client::ClientInterest;
-use crate::game::Action;
-use crate::games::{Game, ValidatedUserAction};
+use crate::game::{Action, Game, ValidatedUserAction};
use crate::server::{ActionStatus, ServerState};
pub struct Dealer {
+++ /dev/null
-use std::collections::HashSet;
-use std::fmt::Debug;
-
-use crate::card::{Card, Suit};
-use crate::gamestate::GameState;
-
-#[derive(Debug, Clone, Serialize, Deserialize)]
-#[serde(tag = "format")]
-pub enum GameSettings {
- Chatroom {
- title: String,
- max_players: u32,
- },
- KnockOutWhist {
- title: String,
- max_players: u32,
- },
-}
-
-#[derive(Debug, Clone, Serialize, Deserialize)]
-#[serde(tag = "format")]
-pub struct GameList {
- filter: String,
- games: Vec<GameSummary>,
-}
-
-impl GameList {
- pub fn new(filter: String) -> Self {
- Self {
- filter,
- games: Vec::new(),
- }
- }
-
- pub fn update(&self, from: usize) -> Vec<GameSummary> {
- self.games.iter().skip(from).cloned().collect()
- }
-
- pub fn games_len(&self) -> usize {
- self.games.len()
- }
-
- pub fn push(&mut self, game: GameSummary) {
- self.games.push(game);
- }
-}
-
-#[derive(Debug, Clone, Serialize, Deserialize)]
-#[serde(tag = "action")]
-pub enum Action {
- Join { seat: u32, chips: u64 },
- AddOn { chips: u64 },
- NextToDeal,
- CommunityCard { card: Card },
- ReceiveCard { card: Option<Card> },
- EndDeal,
- RevealCard { card: Card },
- PlayCard { card: Card },
- ChooseTrumps { suit: Suit },
- Fold,
- Bet { chips: u64 },
- WinTrick,
- WinHand { chips: u64 },
- WinGame,
- Message { message: String },
- Leave,
- KnockedOut,
-}
-
-impl Action {
- pub fn anonymise(&self) -> Self {
- match self {
- Action::ReceiveCard{..} => Action::ReceiveCard{card: None},
- action => action.clone(),
- }
- }
-}
-
-#[derive(Debug, Clone, Serialize, Deserialize)]
-pub struct UserAction {
- pub username: String,
- pub action: Action,
-}
-
-impl UserAction {
- pub fn view_for(&self, username: &str) -> Self {
- Self {
- username: self.username.clone(),
- action: if username == self.username { self.action.clone() } else { self.action.anonymise() },
- }
- }
-}
-
-#[derive(Debug, Clone, Serialize)]
-pub enum ActionError {
- NotAuthorised,
- AlreadyJoined,
- GameHasStarted,
- SeatNotAvailable,
- NoSeatAvailable,
- OutOfTurn,
- CardNotPlayable,
- InvalidActionForGameType,
-}
-
-use std::fmt::{Display, Formatter};
-
-impl Display for ActionError {
- fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
- match self {
- ActionError::NotAuthorised => f.write_str("NotAuthorised"),
- ActionError::AlreadyJoined => f.write_str("AlreadyJoined"),
- ActionError::GameHasStarted => f.write_str("GameHasStarted"),
- ActionError::SeatNotAvailable => f.write_str("SeatNotAvailable"),
- ActionError::NoSeatAvailable => f.write_str("NoSeatAvailable"),
- ActionError::OutOfTurn => f.write_str("OutOfTurn"),
- ActionError::CardNotPlayable => f.write_str("CardNotPlayable"),
- ActionError::InvalidActionForGameType => f.write_str("InvalidActionForGameType"),
- }
- }
-}
-
-#[derive(Debug, Clone, Serialize, Deserialize)]
-pub struct GameSummary {
- id: u32,
- settings: GameSettings,
-}
-
-impl GameSummary {
- pub fn new(id: u32, settings: GameSettings) -> Self {
- Self{id, settings}
- }
-
- pub fn id(&self) -> u32 {
- self.id
- }
-}
-
-#[derive(Debug, Clone, Serialize, Deserialize)]
-pub struct Game {
- summary: GameSummary,
- state: GameState,
-}
-
-impl Game {
- pub fn new(id: u32, settings: GameSettings) -> Self {
- Self {
- summary: GameSummary{id, settings},
- state: GameState::new(),
- }
- }
-
- pub fn id(&self) -> u32 {
- self.summary.id
- }
-
- pub fn actions_len(&self) -> usize {
- self.state.actions_len()
- }
-
- pub fn players(&self) -> HashSet<&str> {
- self.state.players()
- }
-
- pub fn players_len(&self) -> u32 {
- self.state.players_len()
- }
-
- pub fn deck(&self) -> HashSet<Card> {
- self.state.deck()
- }
-
- pub fn has_started(&self) -> bool {
- self.state.has_started()
- }
-
- pub fn dealing_to(&self) -> Option<String> {
- self.state.dealing_to()
- }
-
- pub fn dealer(&self) -> Option<String> {
- self.state.dealer()
- }
-
- pub fn all_players_have_played_a_card(&self) -> bool {
- self.state.all_players_have_played_a_card()
- }
-
- pub fn trick_winner(&self) -> Option<String> {
- self.state.trick_winner()
- }
-
- pub fn view_for(&self, username: &str) -> Game {
- Game {
- summary: self.summary.clone(),
- state: self.state.view_for(username),
- }
- }
-
- pub fn update_view_for(&self, username: &str, from: usize) -> Vec<UserAction> {
- self.state.update_view_for(username, from)
- }
-
- pub fn verify(&self, UserAction{ref username, ref action}: &UserAction) -> Result<(), ActionError> {
- debug!("Verifying action: UserAction {{ username: {:?}, action: {:?} }}", username, action);
- match self.summary.settings {
- GameSettings::Chatroom{max_players, ..} => match action {
- Action::Join{chips: 0, ..} if self.state.player_has_joined(username) => Err(ActionError::AlreadyJoined),
- Action::Join{chips: 0, ..} if self.state.players_len() + 1 > max_players => Err(ActionError::NoSeatAvailable),
- Action::Join{seat, chips: 0} if !self.state.seat_is_available(*seat) => Err(ActionError::SeatNotAvailable),
- Action::Join{chips: 0, ..} => Ok(()),
- Action::Message{..} if self.state.player_has_joined(username) => Ok(()),
- Action::Message{..} => Err(ActionError::NotAuthorised),
- Action::Leave if self.state.player_has_joined(username) => Ok(()),
- Action::Leave => Err(ActionError::NotAuthorised),
- _ => Err(ActionError::InvalidActionForGameType),
- },
- GameSettings::KnockOutWhist{max_players, ..} => match action {
- Action::Join{seat, chips: 0} if self.state.player_has_joined(username) => Err(ActionError::AlreadyJoined),
- Action::Join{seat, chips: 0} if self.state.has_started() => Err(ActionError::GameHasStarted),
- Action::Join{seat, chips: 0} if *seat >= max_players => Err(ActionError::NoSeatAvailable),
- Action::Join{seat, chips: 0} if !self.state.seat_is_available(*seat) => Err(ActionError::SeatNotAvailable),
- Action::Join{seat, chips: 0} => Ok(()),
- Action::Leave if self.state.player_has_joined(username) && !self.state.has_started() => Ok(()),
- Action::Leave if self.state.player_has_joined(username) => Err(ActionError::GameHasStarted),
- Action::Leave => Err(ActionError::NotAuthorised),
- Action::ReceiveCard{card: Some(card)} if self.state.is_dealing_to(username) => Ok(()),
- Action::ReceiveCard{..} => Err(ActionError::OutOfTurn),
- Action::NextToDeal => Ok(()),
- Action::EndDeal => Ok(()),
- Action::CommunityCard{..} => Ok(()),
- Action::PlayCard{card} if self.state.player_is_active(username) && !self.state.all_players_have_played_a_card() && self.state.player_has_card(username, *card) => Ok(()),
- Action::PlayCard{card} if !self.state.player_is_active(username) => Err(ActionError::OutOfTurn),
- Action::PlayCard{..} => Err(ActionError::CardNotPlayable),
- Action::WinTrick{..} => Ok(()),
- Action::WinHand{..} => Ok(()),
- Action::WinGame => Ok(()),
- Action::KnockedOut => Ok(()),
- _ => Err(ActionError::InvalidActionForGameType),
- }
- }
- }
-
- pub fn take_action(&mut self, user_action: UserAction) -> Result<(), ActionError> {
- self.verify(&user_action)?;
- debug!("Taking action: {:?}", user_action);
- //debug!("State before: {:?}", self.state);
- self.state.take_action(user_action);
- //debug!("State after: {:?}", self.state);
- Ok(())
- }
-}
--- /dev/null
+pub mod chatroom;
+
+use std::collections::HashSet;
+use std::fmt::{Debug, Display, Formatter};
+
+use crate::card::{Card, Suit};
+use crate::username::Username;
+
+#[derive(Debug, Clone, Serialize, Deserialize)]
+#[serde(transparent)]
+pub struct ValidatedUserAction(UserAction);
+
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub struct UserAction {
+ pub username: Username,
+ pub action: Action,
+}
+
+impl ValidatedUserAction {
+ pub fn view_for(&self, username: Username) -> UserAction {
+ UserAction {
+ username: self.0.username.clone(),
+ action: if username == self.0.username { self.0.action.clone() } else { self.0.action.anonymise() },
+ }
+ }
+}
+
+pub trait Game : Debug + CloneBox + Send + Sync {
+ fn id(&self) -> u32;
+ fn players(&self) -> HashSet<Username>;
+ fn actions_len(&self) -> usize;
+ fn validate_action(&self, action: UserAction) -> Result<ValidatedUserAction, ActionError>;
+ fn take_action(&mut self, action: ValidatedUserAction) -> Result<(), ActionError>;
+ fn next_dealer_action(&self) -> Option<ValidatedUserAction>;
+}
+
+pub trait CloneBox {
+ fn clone_box(&self) -> Box<dyn Game>;
+}
+
+impl<T: Clone + Game + 'static> CloneBox for T {
+ fn clone_box(&self) -> Box<dyn Game> {
+ Box::new(self.clone())
+ }
+}
+
+impl Clone for Box<dyn Game> {
+ fn clone(&self) -> Self {
+ self.clone_box()
+ }
+}
+
+impl dyn Game {
+ pub fn new(summary: GameSummary) -> Box<Self> {
+ todo!()
+ }
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize)]
+#[serde(tag = "format")]
+pub enum GameSettings {
+ Chatroom {
+ title: String,
+ max_players: u32,
+ },
+ KnockOutWhist {
+ title: String,
+ max_players: u32,
+ },
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize)]
+#[serde(tag = "format")]
+pub struct GameList {
+ filter: String,
+ games: Vec<GameSummary>,
+}
+
+impl GameList {
+ pub fn new(filter: String) -> Self {
+ Self {
+ filter,
+ games: Vec::new(),
+ }
+ }
+
+ pub fn games_len(&self) -> usize {
+ self.games.len()
+ }
+
+ pub fn push(&mut self, game: GameSummary) {
+ self.games.push(game);
+ }
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize)]
+#[serde(tag = "action")]
+pub enum Action {
+ Join { seat: u32, chips: u64 },
+ AddOn { chips: u64 },
+ NextToDeal,
+ CommunityCard { card: Card },
+ ReceiveCard { card: Option<Card> },
+ EndDeal,
+ RevealCard { card: Card },
+ PlayCard { card: Card },
+ ChooseTrumps { suit: Suit },
+ Fold,
+ Bet { chips: u64 },
+ WinTrick,
+ WinHand { chips: u64 },
+ WinGame,
+ Message { message: String },
+ Leave,
+ KnockedOut,
+}
+
+impl Action {
+ pub fn anonymise(&self) -> Self {
+ match self {
+ Action::ReceiveCard{..} => Action::ReceiveCard{card: None},
+ action => action.clone(),
+ }
+ }
+}
+
+#[derive(Debug, Clone, Serialize)]
+pub enum ActionError {
+ NotAuthorised,
+ AlreadyJoined,
+ GameHasStarted,
+ SeatNotAvailable,
+ NoSeatAvailable,
+ OutOfTurn,
+ CardNotPlayable,
+ InvalidActionForGameType,
+}
+
+impl Display for ActionError {
+ fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
+ match self {
+ ActionError::NotAuthorised => f.write_str("NotAuthorised"),
+ ActionError::AlreadyJoined => f.write_str("AlreadyJoined"),
+ ActionError::GameHasStarted => f.write_str("GameHasStarted"),
+ ActionError::SeatNotAvailable => f.write_str("SeatNotAvailable"),
+ ActionError::NoSeatAvailable => f.write_str("NoSeatAvailable"),
+ ActionError::OutOfTurn => f.write_str("OutOfTurn"),
+ ActionError::CardNotPlayable => f.write_str("CardNotPlayable"),
+ ActionError::InvalidActionForGameType => f.write_str("InvalidActionForGameType"),
+ }
+ }
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub struct GameSummary {
+ id: u32,
+ settings: GameSettings,
+}
+
+impl GameSummary {
+ pub fn new(id: u32, settings: GameSettings) -> Self {
+ Self{id, settings}
+ }
+
+ pub fn id(&self) -> u32 {
+ self.id
+ }
+}
+++ /dev/null
-pub mod chatroom;
-
-use std::collections::HashSet;
-use std::fmt::Debug;
-
-use crate::game::{Action, ActionError, GameSummary};
-use crate::username::Username;
-
-#[derive(Debug, Clone, Serialize, Deserialize)]
-#[serde(transparent)]
-pub struct ValidatedUserAction(UserAction);
-
-#[derive(Debug, Clone, Serialize, Deserialize)]
-pub struct UserAction {
- pub username: Username,
- pub action: Action,
-}
-
-impl ValidatedUserAction {
- pub fn view_for(&self, username: Username) -> UserAction {
- UserAction {
- username: self.0.username.clone(),
- action: if username == self.0.username { self.0.action.clone() } else { self.0.action.anonymise() },
- }
- }
-}
-
-pub trait Game : Debug + CloneBox + Send + Sync {
- fn id(&self) -> u32;
- fn players(&self) -> HashSet<Username>;
- fn actions_len(&self) -> usize;
- fn validate_action(&self, action: UserAction) -> Result<ValidatedUserAction, ActionError>;
- fn take_action(&mut self, action: ValidatedUserAction) -> Result<(), ActionError>;
- fn next_dealer_action(&self) -> Option<ValidatedUserAction>;
-}
-
-pub trait CloneBox {
- fn clone_box(&self) -> Box<dyn Game>;
-}
-
-impl<T: Clone + Game + 'static> CloneBox for T {
- fn clone_box(&self) -> Box<dyn Game> {
- Box::new(self.clone())
- }
-}
-
-impl Clone for Box<dyn Game> {
- fn clone(&self) -> Self {
- self.clone_box()
- }
-}
-
-impl dyn Game {
- pub fn new(summary: GameSummary) -> Box<Self> {
- todo!()
- }
-}
+++ /dev/null
-use std::collections::{BTreeMap, HashSet};
-
-use crate::card::*;
-use crate::game::{Action, UserAction};
-use crate::hands::Hands;
-use crate::seats::Seats;
-
-#[derive(Debug, Clone, Serialize, Deserialize)]
-pub struct GameState {
- actions: Vec<UserAction>,
- seats: Seats,
- players_in_hand: Seats,
- hands: Hands,
- trumps: Option<Suit>,
- dealing_to: Option<String>,
- dealer: Option<String>,
- active_player: Option<String>,
- next_active_player: Option<String>,
- has_started: bool,
-}
-
-impl GameState {
- pub fn new() -> Self {
- Self {
- actions: Vec::new(),
- seats: Seats::new(),
- players_in_hand: Seats::new(),
- hands: Hands::new(),
- trumps: None,
- dealing_to: None,
- dealer: None,
- active_player: None,
- next_active_player: None,
- has_started: false,
- }
- }
-
- pub fn actions_len(&self) -> usize {
- self.actions.len()
- }
-
- pub fn view_for(&self, username: &str) -> Self {
- Self {
- actions: self.actions.iter().map(|action| action.view_for(username)).collect(),
- ..self.clone()
- }
- }
-
- pub fn update_view_for(&self, username: &str, from: usize) -> Vec<UserAction> {
- self.actions.iter().skip(from).map(|action| action.view_for(username)).collect()
- }
-
- pub fn player_has_joined(&self, username: &str) -> bool {
- self.seats.contains_player(username)
- }
-
- pub fn players(&self) -> HashSet<&str> {
- self.seats.player_set()
- }
-
- pub fn players_len(&self) -> u32 {
- self.seats.players_len() as u32
- }
-
- pub fn deck(&self) -> HashSet<Card> {
- self.hands.deck()
- }
-
- pub fn dealer(&self) -> Option<String> {
- self.dealer.clone()
- }
-
- pub fn dealing_to(&self) -> Option<String> {
- self.dealing_to.clone()
- }
-
- pub fn is_dealing_to(&self, username: &str) -> bool {
- self.dealing_to.as_ref().map(String::as_str) == Some(username)
- }
-
- pub fn seat_is_available(&self, seat: u32) -> bool {
- self.seats.seat_is_available(seat)
- }
-
- pub fn has_started(&self) -> bool {
- self.has_started
- }
-
- pub fn player_is_active(&self, username: &str) -> bool {
- self.active_player.as_ref().map(String::as_str) == Some(username)
- }
-
- pub fn player_has_card(&self, username: &str, card: Card) -> bool {
- self.hands.player_has_card(username, card)
- }
-
- pub fn all_players_have_played_a_card(&self) -> bool {
- self.hands.all_players_have_played_a_card()
- }
-
- pub fn trick_winner(&self) -> Option<String> {
- self.hands.trick_winner(self.trumps)
- }
-
- pub fn take_action(&mut self, action: UserAction) {
- match &action.action {
- Action::Join{seat, ..} => self.seats.add_player(*seat, &action.username),
- Action::Leave | Action::KnockedOut => self.seats.remove_player(&action.username),
- Action::AddOn{..} => {
- // TODO
- }
- Action::NextToDeal => {
- self.players_in_hand = self.seats.clone();
- self.next_active_player = self.players_in_hand.player_after(&action.username);
- self.dealer = Some(action.username.clone());
- self.dealing_to = self.seats.player_after(&action.username);
- self.has_started = true;
- }
- Action::ReceiveCard{card: Some(card)} if self.dealing_to.as_ref().map(String::as_str) == Some(&action.username) => {
- self.dealing_to = self.seats.player_after(&action.username);
- self.hands.deal_card(action.username.clone(), *card);
- }
- Action::ReceiveCard{..} => {
- error!("Expected {:?} to be dealt a card, but {:?} received one", self.dealing_to, action.username);
- }
- Action::CommunityCard{card} => {
- self.trumps = Some(card.suit);
- self.hands.deal_community_card(*card);
- }
- Action::RevealCard{card} => {
- self.hands.reveal_card(&action.username, *card);
- }
- Action::ChooseTrumps{suit} => {
- self.trumps = Some(*suit);
- }
- Action::PlayCard{card} => {
- self.active_player = self.players_in_hand.player_after(&action.username);
- self.hands.play_card(action.username.clone(), *card);
- }
- Action::Bet{..} => {
- self.active_player = self.players_in_hand.player_after(&action.username);
- }
- Action::Fold => {
- self.active_player = self.players_in_hand.player_after(&action.username);
- self.players_in_hand.remove_player(&action.username);
- }
- Action::WinTrick => {
- self.active_player = Some(action.username.clone());
- self.hands.clear_trick();
- }
- Action::WinHand{..} => {
- self.active_player = None;
- self.dealing_to = self.seats.player_after(&action.username);
- if let Some(dealer) = self.dealer.take() {
- self.dealer = self.seats.player_after(&dealer);
- if let Some(dealer) = &self.dealer {
- self.dealing_to = self.seats.player_after(&dealer);
- }
- }
- self.hands.clear();
- }
- Action::EndDeal => {
- self.active_player = self.next_active_player.clone();
- self.dealing_to = None;
- }
- Action::WinGame => {
- self.active_player = None;
- self.dealing_to = None;
- self.hands.clear();
- }
- Action::Message{..} => {}
- }
- self.actions.push(action);
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
-
- #[test]
- fn player_has_joined() {
- let mut state = GameState::new();
- state.take_action(UserAction{username: "user".to_string(), action: Action::Join{seat: 0, chips: 10000}});
- assert!(state.player_has_joined("user"));
- }
-
- #[test]
- fn players_len() {
- let mut state = GameState::new();
- state.take_action(UserAction{username: "user".to_string(), action: Action::Join{seat: 0, chips: 10000}});
- assert_eq!(1, state.players_len());
- state.take_action(UserAction{username: "user2".to_string(), action: Action::Join{seat: 1, chips: 10000}});
- assert_eq!(2, state.players_len());
- state.take_action(UserAction{username: "user".to_string(), action: Action::Leave});
- assert_eq!(1, state.players_len());
- }
-
- #[test]
- fn players() {
- let mut state = GameState::new();
- state.take_action(UserAction{username: "user".to_string(), action: Action::Join{seat: 0, chips: 10000}});
- state.take_action(UserAction{username: "user2".to_string(), action: Action::Join{seat: 1, chips: 10000}});
- state.take_action(UserAction{username: "user3".to_string(), action: Action::Join{seat: 2, chips: 10000}});
- state.take_action(UserAction{username: "user2".to_string(), action: Action::Leave});
- assert_eq!(vec!["user", "user3"].into_iter().collect::<HashSet<_>>(), state.players());
- }
-
- #[test]
- fn is_dealing_to() {
- let mut state = GameState::new();
- state.take_action(UserAction{username: "user1".to_string(), action: Action::Join{seat: 0, chips: 10000}});
- state.take_action(UserAction{username: "user2".to_string(), action: Action::Join{seat: 1, chips: 10000}});
- state.take_action(UserAction{username: "user3".to_string(), action: Action::Join{seat: 2, chips: 10000}});
- assert!(!state.is_dealing_to("user1"));
- assert!(!state.is_dealing_to("user2"));
- assert!(!state.is_dealing_to("user3"));
- state.take_action(UserAction{username: "user1".to_string(), action: Action::NextToDeal});
- assert!(state.is_dealing_to("user2"));
- state.take_action(UserAction{
- username: "user2".to_string(),
- action: Action::ReceiveCard{card: Some(THREE_OF_SPADES)},
- });
- assert!(state.is_dealing_to("user3"));
- state.take_action(UserAction{
- username: "user3".to_string(),
- action: Action::ReceiveCard{card: Some(ACE_OF_HEARTS)},
- });
- assert!(state.is_dealing_to("user1"));
- state.take_action(UserAction{
- username: "user1".to_string(),
- action: Action::ReceiveCard{card: Some(KING_OF_CLUBS)},
- });
- assert!(state.is_dealing_to("user2"));
- state.take_action(UserAction{username: "dealer".to_string(), action: Action::EndDeal});
- assert!(!state.is_dealing_to("user2"));
- }
-
- #[test]
- fn player_is_active_hold_em() {
- let mut state = GameState::new();
- state.take_action(UserAction{username: "user1".to_string(), action: Action::Join{seat: 0, chips: 10000}});
- state.take_action(UserAction{username: "user2".to_string(), action: Action::Join{seat: 1, chips: 10000}});
- state.take_action(UserAction{username: "user3".to_string(), action: Action::Join{seat: 2, chips: 10000}});
- state.take_action(UserAction{username: "user1".to_string(), action: Action::NextToDeal});
- state.take_action(UserAction{username: "user2".to_string(), action: Action::ReceiveCard{card: Some(ACE_OF_SPADES)}});
- state.take_action(UserAction{username: "user3".to_string(), action: Action::ReceiveCard{card: Some(KING_OF_HEARTS)}});
- state.take_action(UserAction{username: "user1".to_string(), action: Action::ReceiveCard{card: Some(TWO_OF_DIAMONDS)}});
- state.take_action(UserAction{username: "user2".to_string(), action: Action::ReceiveCard{card: Some(QUEEN_OF_SPADES)}});
- state.take_action(UserAction{username: "user3".to_string(), action: Action::ReceiveCard{card: Some(THREE_OF_SPADES)}});
- state.take_action(UserAction{username: "user1".to_string(), action: Action::ReceiveCard{card: Some(FOUR_OF_CLUBS)}});
- state.take_action(UserAction{username: "user1".to_string(), action: Action::EndDeal});
- assert!(state.player_is_active("user2"));
- state.take_action(UserAction{username: "user2".to_string(), action: Action::Bet{chips: 50}});
- assert!(state.player_is_active("user3"));
- state.take_action(UserAction{username: "user3".to_string(), action: Action::Bet{chips: 100}});
- assert!(state.player_is_active("user1"));
- state.take_action(UserAction{username: "user1".to_string(), action: Action::Fold});
- assert!(state.player_is_active("user2"));
- state.take_action(UserAction{username: "user2".to_string(), action: Action::Bet{chips: 250}});
- assert!(state.player_is_active("user3"));
- state.take_action(UserAction{username: "user3".to_string(), action: Action::Bet{chips: 300}});
- assert!(state.player_is_active("user2"));
- state.take_action(UserAction{username: "user2".to_string(), action: Action::Bet{chips: 300}});
- state.take_action(UserAction{username: "user1".to_string(), action: Action::CommunityCard{card: ACE_OF_HEARTS}});
- state.take_action(UserAction{username: "user1".to_string(), action: Action::CommunityCard{card: KING_OF_CLUBS}});
- state.take_action(UserAction{username: "user1".to_string(), action: Action::CommunityCard{card: THREE_OF_HEARTS}});
- }
-
- #[test]
- fn player_is_active_whist() {
- let mut state = GameState::new();
- state.take_action(UserAction{username: "user1".to_string(), action: Action::Join{seat: 0, chips: 0}});
- state.take_action(UserAction{username: "user2".to_string(), action: Action::Join{seat: 1, chips: 0}});
- state.take_action(UserAction{username: "user3".to_string(), action: Action::Join{seat: 2, chips: 0}});
- state.take_action(UserAction{username: "user1".to_string(), action: Action::NextToDeal});
- state.take_action(UserAction{username: "user2".to_string(), action: Action::ReceiveCard{card: Some(TWO_OF_CLUBS)}});
- state.take_action(UserAction{username: "user3".to_string(), action: Action::ReceiveCard{card: Some(TWO_OF_DIAMONDS)}});
- state.take_action(UserAction{username: "user1".to_string(), action: Action::ReceiveCard{card: Some(TWO_OF_HEARTS)}});
- state.take_action(UserAction{username: "user2".to_string(), action: Action::ReceiveCard{card: Some(TWO_OF_SPADES)}});
- state.take_action(UserAction{username: "user3".to_string(), action: Action::ReceiveCard{card: Some(THREE_OF_CLUBS)}});
- state.take_action(UserAction{username: "user1".to_string(), action: Action::ReceiveCard{card: Some(THREE_OF_HEARTS)}});
- state.take_action(UserAction{username: "user2".to_string(), action: Action::ReceiveCard{card: Some(THREE_OF_DIAMONDS)}});
- state.take_action(UserAction{username: "user3".to_string(), action: Action::ReceiveCard{card: Some(THREE_OF_SPADES)}});
- state.take_action(UserAction{username: "user1".to_string(), action: Action::ReceiveCard{card: Some(FOUR_OF_CLUBS)}});
- state.take_action(UserAction{username: "user2".to_string(), action: Action::ReceiveCard{card: Some(FOUR_OF_DIAMONDS)}});
- state.take_action(UserAction{username: "user3".to_string(), action: Action::ReceiveCard{card: Some(FOUR_OF_HEARTS)}});
- state.take_action(UserAction{username: "user1".to_string(), action: Action::ReceiveCard{card: Some(FOUR_OF_SPADES)}});
- state.take_action(UserAction{username: "user2".to_string(), action: Action::ReceiveCard{card: Some(FIVE_OF_CLUBS)}});
- state.take_action(UserAction{username: "user3".to_string(), action: Action::ReceiveCard{card: Some(FIVE_OF_DIAMONDS)}});
- state.take_action(UserAction{username: "user1".to_string(), action: Action::ReceiveCard{card: Some(FIVE_OF_HEARTS)}});
- state.take_action(UserAction{username: "user2".to_string(), action: Action::ReceiveCard{card: Some(FIVE_OF_SPADES)}});
- state.take_action(UserAction{username: "user3".to_string(), action: Action::ReceiveCard{card: Some(SIX_OF_CLUBS)}});
- state.take_action(UserAction{username: "user1".to_string(), action: Action::ReceiveCard{card: Some(SIX_OF_DIAMONDS)}});
- state.take_action(UserAction{username: "user2".to_string(), action: Action::ReceiveCard{card: Some(SIX_OF_HEARTS)}});
- state.take_action(UserAction{username: "user3".to_string(), action: Action::ReceiveCard{card: Some(SIX_OF_SPADES)}});
- state.take_action(UserAction{username: "user1".to_string(), action: Action::ReceiveCard{card: Some(SEVEN_OF_CLUBS)}});
- state.take_action(UserAction{username: "user1".to_string(), action: Action::CommunityCard{card: SEVEN_OF_DIAMONDS}});
- state.take_action(UserAction{username: "user1".to_string(), action: Action::EndDeal});
- assert!(state.player_is_active("user2"));
- assert!(!state.all_players_have_played_a_card());
- state.take_action(UserAction{username: "user2".to_string(), action: Action::PlayCard{card: FIVE_OF_CLUBS}});
- assert!(state.player_is_active("user3"));
- assert!(!state.all_players_have_played_a_card());
- state.take_action(UserAction{username: "user3".to_string(), action: Action::PlayCard{card: SIX_OF_CLUBS}});
- assert!(state.player_is_active("user1"));
- assert!(!state.all_players_have_played_a_card());
- state.take_action(UserAction{username: "user1".to_string(), action: Action::PlayCard{card: SEVEN_OF_CLUBS}});
- assert!(state.all_players_have_played_a_card());
- state.take_action(UserAction{username: "user1".to_string(), action: Action::WinTrick});
- assert!(state.player_is_active("user1"));
- assert!(!state.all_players_have_played_a_card());
- state.take_action(UserAction{username: "user1".to_string(), action: Action::PlayCard{card: FIVE_OF_HEARTS}});
- assert!(state.player_is_active("user2"));
- assert!(!state.all_players_have_played_a_card());
- state.take_action(UserAction{username: "user2".to_string(), action: Action::PlayCard{card: SIX_OF_HEARTS}});
- assert!(state.player_is_active("user3"));
- assert!(!state.all_players_have_played_a_card());
- state.take_action(UserAction{username: "user3".to_string(), action: Action::PlayCard{card: FOUR_OF_HEARTS}});
- assert!(state.all_players_have_played_a_card());
- state.take_action(UserAction{username: "user3".to_string(), action: Action::WinTrick});
- assert!(state.player_is_active("user3"));
- assert!(!state.all_players_have_played_a_card());
- state.take_action(UserAction{username: "user3".to_string(), action: Action::PlayCard{card: SIX_OF_SPADES}});
- assert!(state.player_is_active("user1"));
- assert!(!state.all_players_have_played_a_card());
- state.take_action(UserAction{username: "user1".to_string(), action: Action::PlayCard{card: FOUR_OF_SPADES}});
- assert!(state.player_is_active("user2"));
- assert!(!state.all_players_have_played_a_card());
- state.take_action(UserAction{username: "user2".to_string(), action: Action::PlayCard{card: TWO_OF_SPADES}});
- assert!(state.all_players_have_played_a_card());
- state.take_action(UserAction{username: "user3".to_string(), action: Action::WinTrick});
- assert!(state.player_is_active("user3"));
- }
-}
mod client;
mod dealer;
mod game;
-mod games;
-mod gamestate;
mod hands;
mod seats;
mod server;
let mut game_list = GameList::new("".to_string());
loop {
let games_len = game_list.games_len();
- match server_state.update_game_list(&mut game_list).await {
- Ok(()) => {
- for summary in game_list.update(games_len) {
+ match server_state.game_list(games_len).await {
+ Ok(games) => {
+ for game in games {
+ let id = game.id();
+ game_list.push(game);
let (server_state, update_stream) = server.new_state().await;
- if let Ok(dealer) = Dealer::new(server_state, summary.id()).await {
+ if let Ok(dealer) = Dealer::new(server_state, id).await {
spawn(dealer.start(update_stream));
}
}
use crate::auth::Auth;
use crate::client::ClientInterest;
-use crate::game::{GameList, GameSettings, GameSummary, UserAction};
-use crate::games::ValidatedUserAction;
+use crate::game::{GameList, GameSettings, GameSummary, UserAction, ValidatedUserAction};
use crate::username::Username;
#[derive(Clone)]
self.redis.rpush("games", AsJson(settings)).await.map(|i: u32| i - 1)
}
- pub async fn update_game_list(&mut self, game_list: &mut GameList) -> RedisResult<()> {
- const GAME_LIST_BLOCK_SIZE: isize = 1024;
- debug!("update_game_list: from: {}", game_list.games_len());
- for i in (game_list.games_len() as isize..).step_by(GAME_LIST_BLOCK_SIZE as usize) {
- debug!("update_game_list: LRANGE games {} {}", i, i + GAME_LIST_BLOCK_SIZE);
- let games: Vec<AsJson<GameSettings>> = self.redis.lrange("games", i, i + GAME_LIST_BLOCK_SIZE).await?;
- debug!("update_game_list: received games: {:#?}", games);
- if games.is_empty() { break; }
- for (j, AsJson(settings)) in games.into_iter().enumerate() {
- game_list.push(GameSummary::new(i as u32 + j as u32, settings));
- }
- }
- Ok(())
+ pub async fn game_list(&mut self, from: usize) -> RedisResult<Vec<GameSummary>> {
+ debug!("game_list(from: {})", from);
+ let games: Vec<AsJson<GameSettings>> = self.redis.lrange("games", from as isize, -1).await?;
+ Ok(games.into_iter().map(AsJson::get).enumerate().map(|(id, settings)| GameSummary::new(id as u32, settings)).collect())
}
pub async fn game_state(&mut self, id: u32, from: usize) -> RedisResult<Vec<ValidatedUserAction>> {