use super::classify::rank_7_card_hand;
-#[derive(Copy, Clone, Debug)]
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
enum State {
NotStarted,
Dealing,
}
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<Item=u64> + '_ {
+ 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<Username> {
+ 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()
}
}
*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::<u64>();
self.bets.clear();
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);
+ }
}