use std::{
collections::{HashMap, VecDeque},
fmt::Debug,
};
use crate::arena::{HoldemSimulation, errors::HoldemSimulationError, game_state::Round};
pub struct HoldemCompetition<T: Iterator<Item = HoldemSimulation>> {
simulation_iterator: T,
pub num_rounds: usize,
pub total_change: Vec<f32>,
pub max_change: Vec<f32>,
pub min_change: Vec<f32>,
pub win_count: Vec<usize>,
pub loss_count: Vec<usize>,
pub zero_count: Vec<usize>,
pub before_count: HashMap<Round, usize>,
max_sim_history: usize,
}
const MAX_PLAYERS: usize = 12;
impl<T: Iterator<Item = HoldemSimulation>> HoldemCompetition<T> {
pub fn new(simulation_iterator: T) -> HoldemCompetition<T> {
HoldemCompetition {
simulation_iterator,
max_sim_history: 100,
num_rounds: 0,
total_change: vec![0.0; MAX_PLAYERS],
min_change: vec![0.0; MAX_PLAYERS],
max_change: vec![0.0; MAX_PLAYERS],
win_count: vec![0; MAX_PLAYERS],
loss_count: vec![0; MAX_PLAYERS],
zero_count: vec![0; MAX_PLAYERS],
before_count: HashMap::new(),
}
}
pub fn run(
&mut self,
num_rounds: usize,
) -> Result<Vec<HoldemSimulation>, HoldemSimulationError> {
let mut sims = VecDeque::with_capacity(self.max_sim_history);
let mut rand = rand::rng();
for _round in 0..num_rounds {
let mut running_sim = self.simulation_iterator.next().unwrap();
running_sim.run(&mut rand);
self.update_metrics(&running_sim);
self.num_rounds += 1;
if sims.len() >= self.max_sim_history {
sims.pop_front();
}
sims.push_back(running_sim);
}
Ok(sims.into_iter().collect())
}
fn update_metrics(&mut self, running_sim: &HoldemSimulation) {
let changes = running_sim
.game_state
.starting_stacks
.iter()
.zip(running_sim.game_state.stacks.iter())
.enumerate()
.map(|(idx, (starting, ending))| {
(
idx,
(*ending - *starting) / running_sim.game_state.big_blind,
)
});
for (idx, norm_change) in changes {
self.total_change[idx] += norm_change;
self.min_change[idx] = self.min_change[idx].min(norm_change);
self.max_change[idx] = self.max_change[idx].max(norm_change);
if norm_change > 0.0 {
self.win_count[idx] += 1;
} else if norm_change < 0.0 {
self.loss_count[idx] += 1;
} else {
self.zero_count[idx] += 1;
}
}
let count = self
.before_count
.entry(running_sim.game_state.round_before)
.or_default();
*count += 1;
}
}
impl<T: Iterator<Item = HoldemSimulation>> Debug for HoldemCompetition<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("HoldemCompetition")
.field("num_rounds", &self.num_rounds)
.field("total_change", &self.total_change)
.field("max_change", &self.max_change)
.field("min_change", &self.min_change)
.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()
}
}
#[cfg(test)]
mod tests {
use crate::arena::{
AgentGenerator, CloneGameStateGenerator, GameState,
agent::{CallingAgentGenerator, RandomAgentGenerator},
competition::StandardSimulationIterator,
};
use super::*;
#[test]
fn test_standard_simulation() {
let agent_gens: Vec<Box<dyn AgentGenerator>> = vec![
Box::<RandomAgentGenerator>::default(),
Box::<CallingAgentGenerator>::default(),
];
let stacks = vec![100.0; 2];
let game_state = GameState::new_starting(stacks, 10.0, 5.0, 0.0, 0);
let sim_gen = StandardSimulationIterator::new(
agent_gens,
vec![], CloneGameStateGenerator::new(game_state),
);
let mut competition = HoldemCompetition::new(sim_gen);
let _first_results = competition.run(100).unwrap();
}
}