use std::{cell::RefCell, rc::Rc};
use crate::arena::{GameState, action::Action};
use super::{Historian, HistorianError};
#[derive(Debug, Clone)]
pub struct HistoryRecord {
pub before_game_state: Option<GameState>,
pub action: Action,
pub after_game_state: GameState,
}
pub struct VecHistorian {
previous: Option<GameState>,
records: Rc<RefCell<Vec<HistoryRecord>>>,
}
impl VecHistorian {
pub fn get_storage(&self) -> Rc<RefCell<Vec<HistoryRecord>>> {
self.records.clone()
}
pub fn new_with_actions(actions: Rc<RefCell<Vec<HistoryRecord>>>) -> Self {
Self {
records: actions,
previous: None,
}
}
pub fn new() -> Self {
VecHistorian::new_with_actions(Rc::new(RefCell::new(vec![])))
}
}
impl Default for VecHistorian {
fn default() -> Self {
VecHistorian::new()
}
}
impl Historian for VecHistorian {
fn record_action(
&mut self,
_id: u128,
game_state: &GameState,
action: Action,
) -> Result<(), HistorianError> {
let mut act = self.records.try_borrow_mut()?;
act.push(HistoryRecord {
before_game_state: self.previous.clone(),
action,
after_game_state: game_state.clone(),
});
self.previous = Some(game_state.clone());
Ok(())
}
}
#[cfg(test)]
mod tests {
use crate::arena::{
Agent, HoldemSimulationBuilder,
agent::{CallingAgent, RandomAgent},
};
use super::*;
#[test]
fn test_vec_historian() {
let hist = Box::new(VecHistorian::default());
let records = hist.get_storage();
let mut rng = rand::rng();
let stacks = vec![100.0; 5];
let agents: Vec<Box<dyn Agent>> = vec![
Box::<RandomAgent>::default(),
Box::<RandomAgent>::default(),
Box::<RandomAgent>::default(),
Box::<RandomAgent>::default(),
Box::<RandomAgent>::default(),
];
let game_state = GameState::new_starting(stacks, 10.0, 5.0, 0.0, 0);
let mut sim = HoldemSimulationBuilder::default()
.game_state(game_state)
.agents(agents)
.historians(vec![hist])
.build()
.unwrap();
sim.run(&mut rng);
assert!(records.borrow().len() > 10);
}
#[test]
fn test_restarting_simulations() {
let hist = Box::new(VecHistorian::default());
let records = hist.get_storage();
let mut rng = rand::rng();
let stacks = vec![100.0; 2];
let agents: Vec<Box<dyn Agent>> = vec![
Box::<CallingAgent>::default(),
Box::<CallingAgent>::default(),
];
let game_state = GameState::new_starting(stacks, 10.0, 5.0, 0.0, 0);
let mut sim = HoldemSimulationBuilder::default()
.game_state(game_state)
.agents(agents)
.historians(vec![hist])
.build()
.unwrap();
sim.run(&mut rng);
for r in records.borrow().iter() {
if let (Action::PlayedAction(played_action), Some(before_game_state)) =
(&r.action, &r.before_game_state)
{
let inner_agents: Vec<Box<dyn Agent>> = vec![
Box::<CallingAgent>::default(),
Box::<CallingAgent>::default(),
];
let inner_hist = Box::new(VecHistorian::default());
let inner_records = inner_hist.get_storage();
let mut inner_sim = HoldemSimulationBuilder::default()
.game_state(before_game_state.clone())
.agents(inner_agents)
.historians(vec![inner_hist])
.build()
.unwrap();
inner_sim.run(&mut rng);
let first_record = inner_records.borrow().first().unwrap().clone();
if let Action::PlayedAction(inner_played_action) = first_record.action {
assert_eq!(played_action.idx, inner_played_action.idx);
} else {
panic!(
"The first action should be a played action, found {:?}",
first_record.action
);
}
}
}
}
}