From: Geoffrey Allott Date: Thu, 4 Mar 2021 18:01:48 +0000 (+0000) Subject: fix logic that detects when the betting round has ended X-Git-Url: https://git.pointlesshacks.com/?a=commitdiff_plain;h=42ea1403f13185b3ca1051b7746a57ad8c0195ea;p=pokerwave.git fix logic that detects when the betting round has ended --- diff --git a/src/dealer.rs b/src/dealer.rs index 20f3570..95ca004 100644 --- a/src/dealer.rs +++ b/src/dealer.rs @@ -48,6 +48,7 @@ impl Dealer { } } } + info!("Dealer for game {} is terminating", self.dealer.game.id()); } async fn retrieve_updates(&mut self) -> RedisResult { diff --git a/src/game/poker/holdem.rs b/src/game/poker/holdem.rs index 4ccd429..6a014c0 100644 --- a/src/game/poker/holdem.rs +++ b/src/game/poker/holdem.rs @@ -13,7 +13,7 @@ use super::super::{Action, ActionError, DealerAction, Game, UserAction, Validate use super::classify::rank_7_card_hand; -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] enum State { NotStarted, Dealing, @@ -127,7 +127,50 @@ impl TexasHoldEm { } fn players_able_to_bet(&self) -> usize { - self.players.iter().map(|&username| self.is_able_to_bet(username)).count() + self.players.iter().filter(|&&username| self.is_able_to_bet(username)).count() + } + + fn all_bets_are_in(&self) -> bool { + self.players.iter().filter(|&&username| self.is_able_to_bet(username)) + .all(|username| self.bets.contains_key(username)) + } + + fn player_is_all_in(&self, username: Username) -> bool { + self.players.contains(&username) && matches!(self.stacks.get(&username), Some(&0) | None) + } + + fn bets_of_players_who_are_not_all_in(&self) -> impl Iterator + '_ { + self.bets.iter() + .filter(move |&(&username, _)| !self.player_is_all_in(username)) + .map(|(_, &bet)| bet) + } + + fn all_bets_are_equal(&self) -> bool { + self.players_able_to_bet() == 0 + || self.bets_of_players_who_are_not_all_in().all_equal() + && self.bets.values().cloned().max() <= self.bets_of_players_who_are_not_all_in().max() + } + + fn big_blind_player(&self) -> Option { + if self.seats.players_len() == 2 { + self.dealer + .and_then(|dealer| self.seats.player_after(dealer)) + } else { + self.dealer + .and_then(|dealer| self.seats.player_after(dealer)) + .and_then(|small_blind| self.seats.player_after(small_blind)) + } + } + + fn betting_round_completed(&self) -> bool { + if self.state == State::PreFlopBetting { + if let Some(big_blind) = self.big_blind_player() { + if self.active != Some(big_blind) && !self.player_is_all_in(big_blind) && self.bets.get(&big_blind) == Some(&self.big_blind) { + return false; + } + } + } + self.all_bets_are_in() && self.all_bets_are_equal() } } @@ -269,8 +312,7 @@ impl Game for TexasHoldEm { *self.bets.entry(username).or_default() += chips; *self.committed.entry(username).or_default() += chips; *self.stacks.entry(username).or_default() -= chips; - if (chips == 0 || !matches!(self.state, State::PreFlopBetting) || self.bets.values().max() > Some(&self.big_blind)) && self.bets.len() == self.players_able_to_bet() && self.bets.values().all_equal() - { + if self.betting_round_completed() { self.active = None; self.pot += self.bets.values().sum::(); self.bets.clear(); @@ -739,4 +781,65 @@ mod tests { test_game(actions, settings, seed); } + + #[test] + fn detects_all_in_correctly() { + let actions = r#"[ + {"timestamp":0,"username":"geoff","action":{"action":"Join","seat":0,"chips":1000}}, + {"timestamp":0,"username":"kat","action":{"action":"Join","seat":1,"chips":1000}}, + {"timestamp":0,"username":"geoff","action":{"action":"NextToDeal"}}, + {"timestamp":0,"username":"kat","action":{"action":"ReceiveCard","card":{"rank":"Four","suit":"Hearts"}}}, + {"timestamp":0,"username":"geoff","action":{"action":"ReceiveCard","card":{"rank":"Two","suit":"Diamonds"}}}, + {"timestamp":0,"username":"kat","action":{"action":"ReceiveCard","card":{"rank":"Four","suit":"Clubs"}}}, + {"timestamp":0,"username":"geoff","action":{"action":"ReceiveCard","card":{"rank":"Six","suit":"Spades"}}}, + {"timestamp":0,"username":"geoff","action":{"action":"EndDeal"}}, + {"timestamp":0,"username":"geoff","action":{"action":"PostBlind","chips":100}}, + {"timestamp":0,"username":"kat","action":{"action":"PostBlind","chips":200}}, + {"timestamp":0,"username":"geoff","action":{"action":"Fold"}}, + {"timestamp":0,"username":"kat","action":{"action":"WinHand","chips":300,"hand":null}}, + {"timestamp":0,"username":"kat","action":{"action":"NextToDeal"}}, + {"timestamp":0,"username":"geoff","action":{"action":"ReceiveCard","card":{"rank":"King","suit":"Diamonds"}}}, + {"timestamp":0,"username":"kat","action":{"action":"ReceiveCard","card":{"rank":"Six","suit":"Hearts"}}}, + {"timestamp":0,"username":"geoff","action":{"action":"ReceiveCard","card":{"rank":"Eight","suit":"Diamonds"}}}, + {"timestamp":0,"username":"kat","action":{"action":"ReceiveCard","card":{"rank":"Ace","suit":"Diamonds"}}}, + {"timestamp":0,"username":"kat","action":{"action":"EndDeal"}}, + {"timestamp":0,"username":"kat","action":{"action":"PostBlind","chips":100}}, + {"timestamp":0,"username":"geoff","action":{"action":"PostBlind","chips":200}}, + {"timestamp":0,"username":"kat","action":{"action":"Bet","chips":300}}, + {"timestamp":0,"username":"geoff","action":{"action":"Bet","chips":200}}, + {"timestamp":0,"username":"kat","action":{"action":"CommunityCard","card":{"rank":"Four","suit":"Spades"}}}, + {"timestamp":0,"username":"kat","action":{"action":"CommunityCard","card":{"rank":"Jack","suit":"Spades"}}}, + {"timestamp":0,"username":"kat","action":{"action":"CommunityCard","card":{"rank":"Seven","suit":"Clubs"}}}, + {"timestamp":0,"username":"geoff","action":{"action":"Bet","chips":0}}, + {"timestamp":0,"username":"kat","action":{"action":"Bet","chips":0}}, + {"timestamp":0,"username":"kat","action":{"action":"CommunityCard","card":{"rank":"Ten","suit":"Diamonds"}}}, + {"timestamp":0,"username":"geoff","action":{"action":"Bet","chips":0}}, + {"timestamp":0,"username":"kat","action":{"action":"Bet","chips":0}}, + {"timestamp":0,"username":"kat","action":{"action":"CommunityCard","card":{"rank":"Four","suit":"Diamonds"}}}, + {"timestamp":0,"username":"geoff","action":{"action":"Bet","chips":0}}, + {"timestamp":0,"username":"kat","action":{"action":"Bet","chips":0}}, + {"timestamp":0,"username":"kat","action":{"action":"WinHand","chips":800,"hand":"Pair of 4s, AJT Kickers"}}, + {"timestamp":0,"username":"geoff","action":{"action":"NextToDeal"}}, + {"timestamp":0,"username":"kat","action":{"action":"ReceiveCard","card":{"rank":"Nine","suit":"Hearts"}}}, + {"timestamp":0,"username":"geoff","action":{"action":"ReceiveCard","card":{"rank":"Ace","suit":"Hearts"}}}, + {"timestamp":0,"username":"kat","action":{"action":"ReceiveCard","card":{"rank":"Eight","suit":"Hearts"}}}, + {"timestamp":0,"username":"geoff","action":{"action":"ReceiveCard","card":{"rank":"Five","suit":"Hearts"}}}, + {"timestamp":0,"username":"geoff","action":{"action":"EndDeal"}}, + {"timestamp":0,"username":"geoff","action":{"action":"PostBlind","chips":100}}, + {"timestamp":0,"username":"kat","action":{"action":"PostBlind","chips":200}}, + {"timestamp":0,"username":"geoff","action":{"action":"Bet","chips":300}}, + {"timestamp":0,"username":"kat","action":{"action":"Bet","chips":400}}, + {"timestamp":0,"username":"geoff","action":{"action":"Bet","chips":100}}, + {"timestamp":0,"username":"geoff","action":{"action":"CommunityCard","card":{"rank":"Two","suit":"Hearts"}}} + ]"#; + 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,"action_timeout":null}"#; + let settings = serde_json::from_str(settings).unwrap(); + + let seed = r#"{"rng":"ChaCha20","seed":"fd87ec4b51fcaf056ef53c0460322e1fa5261cf2801d005065c9add8ec541bb4"}"#; + let seed = serde_json::from_str(seed).unwrap(); + + test_game(actions, settings, seed); + } }