remove old logic
authorGeoffrey Allott <geoffrey@allott.email>
Tue, 23 Feb 2021 21:05:55 +0000 (21:05 +0000)
committerGeoffrey Allott <geoffrey@allott.email>
Tue, 23 Feb 2021 21:05:55 +0000 (21:05 +0000)
12 files changed:
src/api.rs
src/client.rs
src/dealer.rs
src/game.rs [deleted file]
src/game/chatroom.rs [moved from src/games/chatroom.rs with 100% similarity]
src/game/mod.rs [new file with mode: 0644]
src/game/poker.rs [moved from src/games/poker.rs with 100% similarity]
src/game/whist.rs [moved from src/games/whist.rs with 100% similarity]
src/games/mod.rs [deleted file]
src/gamestate.rs [deleted file]
src/main.rs
src/server.rs

index 60804214e9444264dc629ea86ebf28b0d9184e0c..e90176cad67f32d10df1a4cdef7746d1c412dd87 100644 (file)
@@ -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)]
index 45a2be02e9762803d5c703ee35248d45f4c3f01f..6507d6910ddff69053c9a05a2f3c32265d35d850 100644 (file)
@@ -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}
                     }
index 3a2ebc184feb876633df96232cfaba639e89a894..058330e19d18449b57385679c0adf1570952b867 100644 (file)
@@ -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 (file)
index 52452fe..0000000
+++ /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<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(())
-    }
-}
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 (file)
index 0000000..eb40b11
--- /dev/null
@@ -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<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
+    }
+}
similarity index 100%
rename from src/games/poker.rs
rename to src/game/poker.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 (file)
index 8527842..0000000
+++ /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<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!()
-    }
-}
diff --git a/src/gamestate.rs b/src/gamestate.rs
deleted file mode 100644 (file)
index 3b9c853..0000000
+++ /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<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"));
-    }
-}
index 1e0b4f0777b4a0016c1dfaf0478151272bc4e3a5..599a756941d94879610ef77fc7ed7aadd1849cd6 100644 (file)
@@ -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));
                     }
                 }
index 5e4ecbe39b912b4bfca1d17b0470d7c23d59b446..59e6ba4fba423140294a27393e30a3561317f090 100644 (file)
@@ -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<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>> {