use async_trait::async_trait;
use tracing::trace;
use crate::arena::{GameState, action::Action};
use super::{Historian, HistorianError};
#[derive(Debug, Clone)]
pub struct FnHistorian<F> {
func: F,
}
impl<F: Fn(u128, &GameState, &Action) -> Result<(), HistorianError>> FnHistorian<F> {
pub fn new(f: F) -> Self {
Self { func: f }
}
}
#[async_trait]
impl<F: Clone + Send + Fn(u128, &GameState, &Action) -> Result<(), HistorianError>> Historian
for FnHistorian<F>
{
async fn record_action(
&mut self,
id: u128,
game_state: &GameState,
action: &Action,
) -> Result<(), HistorianError> {
trace!(id, ?action, "FnHistorian invoking closure");
(self.func)(id, game_state, action)
}
}
#[cfg(test)]
mod tests {
use std::sync::{Arc, Mutex};
use crate::arena::{Agent, HoldemSimulationBuilder, agent::RandomAgent, game_state::Round};
use super::*;
use crate::arena::GameStateBuilder;
#[tokio::test]
async fn test_can_record_actions_with_agents() {
let last_action: Arc<Mutex<Option<Action>>> = Arc::new(Mutex::new(None));
let count = Arc::new(Mutex::new(0));
let agents: Vec<Box<dyn Agent>> = (0..2)
.map(|_| Box::<RandomAgent>::default() as Box<dyn Agent>)
.collect();
let game_state = GameStateBuilder::new()
.stacks(vec![100.0, 100.0])
.blinds(10.0, 5.0)
.build()
.unwrap();
let borrow_count = count.clone();
let borrow_last_action = last_action.clone();
let historian = Box::new(FnHistorian::new(move |_id, _game_state, action| {
*borrow_count.lock().unwrap() += 1;
*borrow_last_action.lock().unwrap() = Some(action.clone());
Ok(())
}));
let mut sim = HoldemSimulationBuilder::default()
.agents(agents)
.game_state(game_state)
.historians(vec![historian])
.build()
.unwrap();
sim.run().await;
assert_ne!(0, *count.lock().unwrap());
let act = last_action.lock().unwrap().clone();
assert!(act.is_some());
assert_eq!(Some(Action::RoundAdvance(Round::Complete)), act);
}
#[tokio::test]
async fn test_fn_historian_can_withstand_error() {
let agents: Vec<Box<dyn Agent>> = (0..2)
.map(|_| Box::<RandomAgent>::default() as Box<dyn Agent>)
.collect();
let game_state = GameStateBuilder::new()
.stacks(vec![100.0, 100.0])
.blinds(10.0, 5.0)
.build()
.unwrap();
let historian = Box::new(FnHistorian::new(|_, _, _| {
Err(HistorianError::UnableToRecordAction)
}));
HoldemSimulationBuilder::default()
.agents(agents)
.game_state(game_state)
.historians(vec![historian])
.panic_on_historian_error(false)
.build()
.unwrap()
.run()
.await;
}
}