add action timeout to texas holdem
authorGeoffrey Allott <geoffrey@allott.email>
Tue, 2 Mar 2021 23:03:55 +0000 (23:03 +0000)
committerGeoffrey Allott <geoffrey@allott.email>
Tue, 2 Mar 2021 23:03:55 +0000 (23:03 +0000)
src/game/poker/holdem.rs

index 2ed0f179d1c677e300cc917f78fc1d7093a37156..5fdeaf23916b43af7fa9774067b9ec818585eeea 100644 (file)
@@ -1,6 +1,6 @@
 use std::collections::{HashMap, HashSet};
 use std::convert::TryInto;
-use std::time::SystemTime;
+use std::time::{Duration, SystemTime};
 
 use itertools::Itertools;
 
@@ -37,6 +37,7 @@ pub struct TexasHoldEmSettings {
     max_players: u32,
     small_blind: u64,
     starting_stack: u64,
+    action_timeout_secs: Option<u64>,
 }
 
 #[derive(Clone, Debug)]
@@ -45,6 +46,7 @@ pub struct TexasHoldEm {
     settings: TexasHoldEmSettings,
     rng: WaveRng,
     actions_len: usize,
+    last_action_time: Option<SystemTime>,
     state: State,
     seats: Seats,
     stacks: HashMap<Username, u64>,
@@ -71,6 +73,7 @@ impl TexasHoldEm {
             settings,
             rng: seed.into_rng(),
             actions_len: 0,
+            last_action_time: None,
             state: State::NotStarted,
             seats: Seats::new(),
             stacks: HashMap::new(),
@@ -200,8 +203,9 @@ impl Game for TexasHoldEm {
         }
     }
 
-    fn take_action(&mut self, ValidatedUserAction(UserAction{username, action, ..}): ValidatedUserAction) -> Result<(), ActionError> {
+    fn take_action(&mut self, ValidatedUserAction(UserAction{timestamp, username, action}): ValidatedUserAction) -> Result<(), ActionError> {
         self.actions_len += 1;
+        self.last_action_time = Some(timestamp);
         self.rng.advance();
         match (self.state, action) {
             (_, Action::PlayCard{..}) | (_, Action::ChooseTrumps{..}) => {
@@ -372,7 +376,8 @@ impl Game for TexasHoldEm {
                         ValidatedUserAction(UserAction{timestamp, username, action: Action::EndDeal})
                     )
                 } else {
-                    DealerAction::WaitForPlayer
+                    error!("Expected to deal a card but there was no dealer: {:#?}", self);
+                    DealerAction::Leave
                 }
             }
             State::PostingSmallBlind if self.seats.players_len() == 2 => {
@@ -394,7 +399,7 @@ impl Game for TexasHoldEm {
                     )
                 } else {
                     error!("There is no player to post the small blind: {:#?}", self);
-                    DealerAction::WaitForPlayer
+                    DealerAction::Leave
                 }
             }
             State::PostingBigBlind if self.seats.players_len() == 2 => {
@@ -405,7 +410,7 @@ impl Game for TexasHoldEm {
                     )
                 } else {
                     error!("There is no player to post the big blind: {:#?}", self);
-                    DealerAction::WaitForPlayer
+                    DealerAction::Leave
                 }
             }
             State::PostingBigBlind => {
@@ -419,7 +424,7 @@ impl Game for TexasHoldEm {
                     )
                 } else {
                     error!("There is no player to post the big blind: {:#?}", self);
-                    DealerAction::WaitForPlayer
+                    DealerAction::Leave
                 }
             }
             State::PreFlopBetting | State::PostFlopBetting | State::TurnBetting | State::RiverBetting => {
@@ -461,6 +466,15 @@ impl Game for TexasHoldEm {
                         error!("Logic error: no dealer could be chosen: {:#?}", self);
                         DealerAction::Leave
                     }
+                } else if let (Some(last_time), Some(username), Some(timeout)) = (self.last_action_time, self.active, self.settings.action_timeout_secs) {
+                    let timeout_time = last_time + Duration::from_secs(timeout);
+                    if timestamp >= timeout_time {
+                        DealerAction::TakeAction(
+                            ValidatedUserAction(UserAction{timestamp, username, action: Action::Fold})
+                        )
+                    } else {
+                        DealerAction::WaitUntil(timeout_time)
+                    }
                 } else {
                     DealerAction::WaitForPlayer
                 }
@@ -591,7 +605,7 @@ mod tests {
         ]"#;
         let actions = serde_json::from_str(actions).unwrap();
 
-        let settings = r#"{"title":"2-Player TexasHoldEm Test","max_players":2,"small_blind":100,"starting_stack":1000}"#;
+        let settings = r#"{"title":"2-Player TexasHoldEm Test","max_players":2,"small_blind":100,"starting_stack":1000,"action_timeout_secs":null}"#;
         let settings = serde_json::from_str(settings).unwrap();
 
         let seed = r#"{"rng":"ChaCha20","seed":"e0355d5c6c63ef757d1b874b0392a3deec73cadfb0a2aa7947a04db651bf9269"}"#;
@@ -637,7 +651,7 @@ mod tests {
         ]"#;
         let actions = serde_json::from_str(actions).unwrap();
 
-        let settings = r#"{"title":"2-Player TexasHoldEm Test","max_players":2,"small_blind":25,"starting_stack":1000}"#;
+        let settings = r#"{"title":"2-Player TexasHoldEm Test","max_players":2,"small_blind":25,"starting_stack":1000,"action_timeout_secs":null}"#;
         let settings = serde_json::from_str(settings).unwrap();
 
         let seed = r#"{"rng":"ChaCha20","seed":"f05dc83bdce966e72a3a81b19ccded2e70387eb68deacf60ed8de1ee78b9ff0e"}"#;
@@ -720,7 +734,7 @@ mod tests {
         ]"#;
         let actions = serde_json::from_str(actions).unwrap();
 
-        let settings = r#"{"title":"2-Player TexasHoldEm Test","max_players":2,"small_blind":100,"starting_stack":1000}"#;
+        let settings = r#"{"title":"2-Player TexasHoldEm Test","max_players":2,"small_blind":100,"starting_stack":1000,"action_timeout_secs":null}"#;
         let settings = serde_json::from_str(settings).unwrap();
 
         let seed = r#"{"rng":"ChaCha20","seed":"fd87ec4b51fcaf056ef53c0460322e1fa5261cf2801d005065c9add8ec541bb4"}"#;