use std::sync::Arc;
use crate::{
Game, GameTree, InvalidMove, Move, Payoff, PerPlayer, Playable, PlayerIndex, Profile, Record,
SimultaneousOutcome, Utility,
};
#[derive(Clone)]
pub struct Simultaneous<M, U, const P: usize> {
move_fn: Arc<dyn Fn(PlayerIndex<P>, M) -> bool + Send + Sync>,
payoff_fn: Arc<dyn Fn(Profile<M, P>) -> Payoff<U, P> + Send + Sync>,
}
impl<M: Move, U: Utility, const P: usize> Simultaneous<M, U, P> {
pub fn from_payoff_fn<MoveFn, PayoffFn>(move_fn: MoveFn, payoff_fn: PayoffFn) -> Self
where
MoveFn: Fn(PlayerIndex<P>, M) -> bool + Send + Sync + 'static,
PayoffFn: Fn(Profile<M, P>) -> Payoff<U, P> + Send + Sync + 'static,
{
Simultaneous {
move_fn: Arc::new(move_fn),
payoff_fn: Arc::new(payoff_fn),
}
}
pub fn from_utility_fns<MoveFn, UtilFn>(move_fn: MoveFn, util_fns: PerPlayer<UtilFn, P>) -> Self
where
MoveFn: Fn(PlayerIndex<P>, M) -> bool + Send + Sync + 'static,
UtilFn: Fn(M) -> U + Send + Sync + 'static,
{
let payoff_fn = move |profile: Profile<M, P>| {
Payoff::new(PerPlayer::generate(|player| {
util_fns[player](profile[player])
}))
};
Self::from_payoff_fn(move_fn, payoff_fn)
}
pub fn trivial() -> Self {
Self::from_utility_fns(|_, _| true, PerPlayer::generate(|_| |_| U::default()))
}
pub fn is_valid_move_for_player(&self, player: PlayerIndex<P>, the_move: M) -> bool {
(*self.move_fn)(player, the_move)
}
pub fn is_valid_profile(&self, profile: Profile<M, P>) -> bool {
PlayerIndex::all().all(|player| self.is_valid_move_for_player(player, profile[player]))
}
pub fn payoff(&self, profile: Profile<M, P>) -> Payoff<U, P> {
(*self.payoff_fn)(profile)
}
}
impl<M: Move, U: Utility, const P: usize> Game<P> for Simultaneous<M, U, P> {
type Move = M;
type Utility = U;
type State = ();
type View = ();
fn state_view(&self, _state: &(), _player: PlayerIndex<P>) {}
}
impl<M: Move, U: Utility, const P: usize> Playable<P> for Simultaneous<M, U, P> {
type Outcome = SimultaneousOutcome<M, U, P>;
fn into_game_tree(self) -> GameTree<(), M, U, SimultaneousOutcome<M, U, P>, P> {
GameTree::all_players((), move |_, profile| {
for ply in profile.plies() {
let player = ply.player.unwrap();
if !self.is_valid_move_for_player(player, ply.the_move) {
return Err(InvalidMove::new((), player, ply.the_move));
}
}
Ok(GameTree::end(
(),
SimultaneousOutcome::new(profile, self.payoff(profile)),
))
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use impls::impls;
use test_log::test;
#[test]
fn simultaneous_is_send_sync() {
assert!(impls!(Simultaneous<(), u8, 2>: Send & Sync));
}
}