diff --git a/src/arena/competition/holdem_competition.rs b/src/arena/competition/holdem_competition.rs index 35b692a3..81600963 100644 --- a/src/arena/competition/holdem_competition.rs +++ b/src/arena/competition/holdem_competition.rs @@ -1,6 +1,9 @@ -use std::{collections::VecDeque, fmt::Debug}; +use std::{ + collections::{HashMap, VecDeque}, + fmt::Debug, +}; -use crate::arena::{errors::HoldemSimulationError, HoldemSimulation}; +use crate::arena::{errors::HoldemSimulationError, game_state::Round, HoldemSimulation}; use super::sim_gen::HoldemSimulationGenerator; @@ -24,6 +27,8 @@ pub struct HoldemCompetition { pub loss_count: Vec, // How many times the agent has lost no money pub zero_count: Vec, + // Count of the round before the simulation stopped + pub before_count: HashMap, /// Maximum number of HoldemSimulation's to /// keep in a long call to `run` @@ -50,6 +55,8 @@ impl HoldemCompetition { win_count: vec![0; MAX_PLAYERS], loss_count: vec![0; MAX_PLAYERS], zero_count: vec![0; MAX_PLAYERS], + // Round before stopping + before_count: HashMap::new(), } } @@ -115,6 +122,12 @@ impl HoldemCompetition { self.zero_count[idx] += 1; } } + // Update the count + let count = self + .before_count + .entry(running_sim.game_state.round_before) + .or_default(); + *count += 1; } } impl Debug for HoldemCompetition { @@ -127,6 +140,7 @@ impl Debug for HoldemCompetition { .field("win_count", &self.win_count) .field("zero_count", &self.zero_count) .field("loss_count", &self.loss_count) + .field("round_before", &self.before_count) .finish() } } diff --git a/src/arena/game_state.rs b/src/arena/game_state.rs index abeb826f..30777d18 100644 --- a/src/arena/game_state.rs +++ b/src/arena/game_state.rs @@ -76,7 +76,7 @@ impl Round { } } -#[derive(Clone, PartialEq)] +#[derive(Clone, PartialEq, Debug)] pub struct RoundData { // Which players were active starting this round. pub starting_player_active: PlayerBitSet, @@ -168,7 +168,7 @@ impl RoundData { } } -#[derive(Clone, PartialEq)] +#[derive(Clone, PartialEq, Debug)] pub struct GameState { /// The number of players that started pub num_players: usize, @@ -199,6 +199,10 @@ pub struct GameState { pub dealer_idx: usize, // What round this is currently pub round: Round, + /// This is the round before we completed the game. + /// Sometimes the game completes because of + /// all the players fold in the preflop. + pub round_before: Round, // ALl the current state of the round. pub round_data: RoundData, // The community cards. @@ -243,6 +247,7 @@ impl GameState { total_pot: 0.0, hands: vec![Hand::default(); num_players], round: Round::Starting, + round_before: Round::Starting, board: vec![], round_data: RoundData::new(num_players, big_blind, player_active, dealer_idx), computed_rank: vec![None; num_players], @@ -304,6 +309,9 @@ impl GameState { fn advance_normal(&mut self) { self.round = self.round.advance(); + // We're advancing (not completing) so + // keep advanding the round_before field as well. + self.round_before = self.round; let mut round_data = RoundData::new( self.num_players, @@ -321,6 +329,7 @@ impl GameState { } pub fn complete(&mut self) { + self.round_before = self.round; self.round = Round::Complete; let round_data = RoundData::new( self.num_players, @@ -453,45 +462,6 @@ impl GameState { } } -impl fmt::Debug for RoundData { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("RoundData") - .field("needs_action", &self.needs_action) - .field("num_players_need_action", &self.num_players_need_action()) - .field("min_raise", &self.min_raise) - .field("bet", &self.bet) - .field("player_bet", &self.player_bet) - .field("bet_count", &self.bet_count) - .field("raise_count", &self.raise_count) - .field("to_act_idx", &self.to_act_idx) - .finish() - } -} - -impl fmt::Debug for GameState { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("GameState") - .field("num_players", &self.num_players) - .field("num_active_players", &self.num_active_players()) - .field("player_active", &self.player_active) - .field("player_all_in", &self.player_all_in) - .field("total_pot", &self.total_pot) - .field("stacks", &self.stacks) - .field("player_winnings", &self.player_winnings) - .field("big_blind", &self.big_blind) - .field("small_blind", &self.small_blind) - .field("ante", &self.ante) - .field("hands", &self.hands) - .field("dealer_idx", &self.dealer_idx) - .field("round", &self.round) - .field("round_data", &self.round_data) - .field("board", &self.board) - .field("sb_posted", &self.sb_posted) - .field("bb_posted", &self.bb_posted) - .finish() - } -} - pub trait GameStateGenerator { fn generate(&mut self) -> GameState; } @@ -688,6 +658,7 @@ mod tests { // Do the start and ante rounds and setup next to act game_state.advance_round(); game_state.advance_round(); + game_state.advance_round(); game_state.do_bet(10.0, true).unwrap(); game_state.do_bet(20.0, true).unwrap(); @@ -702,4 +673,19 @@ mod tests { game_state.do_bet(33.0, false) ); } + + #[test] + fn test_gamestate_keeps_round_before_complete() { + let stacks = vec![100.0; 3]; + let mut game_state = GameState::new(stacks, 10.0, 5.0, 0.0, 0); + // Simulate a game where everyone folds and the big blind wins + game_state.advance_round(); + game_state.advance_round(); + game_state.advance_round(); + game_state.fold(); + game_state.fold(); + game_state.complete(); + assert_eq!(Round::Complete, game_state.round); + assert_eq!(Round::Preflop, game_state.round_before); + } }