From 31896dcb16a5c41aaa642f37a47714d0bbfcb903 Mon Sep 17 00:00:00 2001 From: Geoffrey Allott Date: Tue, 23 Feb 2021 21:05:55 +0000 Subject: [PATCH] remove old logic --- src/api.rs | 3 +- src/client.rs | 24 ++- src/dealer.rs | 3 +- src/game.rs | 252 ------------------------ src/{games => game}/chatroom.rs | 0 src/game/mod.rs | 168 ++++++++++++++++ src/{games => game}/poker.rs | 0 src/{games => game}/whist.rs | 0 src/games/mod.rs | 57 ------ src/gamestate.rs | 335 -------------------------------- src/main.rs | 12 +- src/server.rs | 20 +- 12 files changed, 197 insertions(+), 677 deletions(-) delete mode 100644 src/game.rs rename src/{games => game}/chatroom.rs (100%) create mode 100644 src/game/mod.rs rename src/{games => game}/poker.rs (100%) rename src/{games => game}/whist.rs (100%) delete mode 100644 src/games/mod.rs delete mode 100644 src/gamestate.rs diff --git a/src/api.rs b/src/api.rs index 6080421..e90176c 100644 --- a/src/api.rs +++ b/src/api.rs @@ -1,6 +1,5 @@ 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)] diff --git a/src/client.rs b/src/client.rs index 45a2be0..6507d69 100644 --- a/src/client.rs +++ b/src/client.rs @@ -3,8 +3,7 @@ use std::collections::HashSet; 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; @@ -50,8 +49,13 @@ impl ConnectionState { 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(), } } @@ -65,7 +69,9 @@ impl ConnectionState { 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() } @@ -133,9 +139,11 @@ impl ConnectionState { } (&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} } diff --git a/src/dealer.rs b/src/dealer.rs index 3a2ebc1..058330e 100644 --- a/src/dealer.rs +++ b/src/dealer.rs @@ -6,8 +6,7 @@ use rand::prelude::*; 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 { diff --git a/src/game.rs b/src/game.rs deleted file mode 100644 index 52452fe..0000000 --- a/src/game.rs +++ /dev/null @@ -1,252 +0,0 @@ -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, -} - -impl GameList { - pub fn new(filter: String) -> Self { - Self { - filter, - games: Vec::new(), - } - } - - pub fn update(&self, from: usize) -> Vec { - 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 }, - 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 { - self.state.deck() - } - - pub fn has_started(&self) -> bool { - self.state.has_started() - } - - pub fn dealing_to(&self) -> Option { - self.state.dealing_to() - } - - pub fn dealer(&self) -> Option { - 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 { - 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 { - 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(()) - } -} diff --git a/src/games/chatroom.rs b/src/game/chatroom.rs similarity index 100% rename from src/games/chatroom.rs rename to src/game/chatroom.rs diff --git a/src/game/mod.rs b/src/game/mod.rs new file mode 100644 index 0000000..eb40b11 --- /dev/null +++ b/src/game/mod.rs @@ -0,0 +1,168 @@ +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; + fn actions_len(&self) -> usize; + fn validate_action(&self, action: UserAction) -> Result; + fn take_action(&mut self, action: ValidatedUserAction) -> Result<(), ActionError>; + fn next_dealer_action(&self) -> Option; +} + +pub trait CloneBox { + fn clone_box(&self) -> Box; +} + +impl CloneBox for T { + fn clone_box(&self) -> Box { + Box::new(self.clone()) + } +} + +impl Clone for Box { + fn clone(&self) -> Self { + self.clone_box() + } +} + +impl dyn Game { + pub fn new(summary: GameSummary) -> Box { + 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, +} + +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 }, + 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 + } +} diff --git a/src/games/poker.rs b/src/game/poker.rs similarity index 100% rename from src/games/poker.rs rename to src/game/poker.rs diff --git a/src/games/whist.rs b/src/game/whist.rs similarity index 100% rename from src/games/whist.rs rename to src/game/whist.rs diff --git a/src/games/mod.rs b/src/games/mod.rs deleted file mode 100644 index 8527842..0000000 --- a/src/games/mod.rs +++ /dev/null @@ -1,57 +0,0 @@ -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; - fn actions_len(&self) -> usize; - fn validate_action(&self, action: UserAction) -> Result; - fn take_action(&mut self, action: ValidatedUserAction) -> Result<(), ActionError>; - fn next_dealer_action(&self) -> Option; -} - -pub trait CloneBox { - fn clone_box(&self) -> Box; -} - -impl CloneBox for T { - fn clone_box(&self) -> Box { - Box::new(self.clone()) - } -} - -impl Clone for Box { - fn clone(&self) -> Self { - self.clone_box() - } -} - -impl dyn Game { - pub fn new(summary: GameSummary) -> Box { - todo!() - } -} diff --git a/src/gamestate.rs b/src/gamestate.rs deleted file mode 100644 index 3b9c853..0000000 --- a/src/gamestate.rs +++ /dev/null @@ -1,335 +0,0 @@ -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, - seats: Seats, - players_in_hand: Seats, - hands: Hands, - trumps: Option, - dealing_to: Option, - dealer: Option, - active_player: Option, - next_active_player: Option, - 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 { - 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 { - self.hands.deck() - } - - pub fn dealer(&self) -> Option { - self.dealer.clone() - } - - pub fn dealing_to(&self) -> Option { - 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 { - 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::>(), 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")); - } -} diff --git a/src/main.rs b/src/main.rs index 1e0b4f0..599a756 100644 --- a/src/main.rs +++ b/src/main.rs @@ -21,8 +21,6 @@ mod card; mod client; mod dealer; mod game; -mod games; -mod gamestate; mod hands; mod seats; mod server; @@ -162,11 +160,13 @@ async fn handle_new_games(server: Server) -> Result<(), std::io::Error> { 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)); } } diff --git a/src/server.rs b/src/server.rs index 5e4ecbe..59e6ba4 100644 --- a/src/server.rs +++ b/src/server.rs @@ -7,8 +7,7 @@ use serde::{Serialize, Deserialize}; 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)] @@ -153,19 +152,10 @@ impl ServerState { 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> = 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> { + debug!("game_list(from: {})", from); + let games: Vec> = 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> { -- 2.34.1