use std::fmt::Debug;
use std::sync::Arc;
use crate::{
Move, Payoff, PlayerIndex, PossibleProfiles, Profile, Record, State, Transcript, Utility,
};
pub trait Outcome<M: Move, U: Utility, const P: usize>:
Clone + Debug + PartialEq + Send + Sync + 'static
{
type Record: Record<M, P>;
fn record(&self) -> &Self::Record;
fn payoff(&self) -> &Payoff<U, P>;
}
#[derive(Clone, Debug, PartialEq, Hash)]
pub struct SimultaneousOutcome<M: Move, U: Utility, const P: usize> {
profile: Profile<M, P>,
payoff: Payoff<U, P>,
}
impl<M: Move, U: Utility, const P: usize> SimultaneousOutcome<M, U, P> {
pub fn new(profile: Profile<M, P>, payoff: Payoff<U, P>) -> Self {
SimultaneousOutcome { profile, payoff }
}
pub fn profile(&self) -> &Profile<M, P> {
&self.profile
}
}
impl<M: Move, U: Utility, const P: usize> Outcome<M, U, P> for SimultaneousOutcome<M, U, P> {
type Record = Profile<M, P>;
fn record(&self) -> &Profile<M, P> {
&self.profile
}
fn payoff(&self) -> &Payoff<U, P> {
&self.payoff
}
}
#[derive(Clone, Debug, PartialEq, Hash)]
pub struct SequentialOutcome<S: State, M: Move, U: Utility, const P: usize> {
final_state: S,
transcript: Transcript<M, P>,
payoff: Payoff<U, P>,
}
impl<S: State, M: Move, U: Utility, const P: usize> SequentialOutcome<S, M, U, P> {
pub fn new(final_state: S, transcript: Transcript<M, P>, payoff: Payoff<U, P>) -> Self {
SequentialOutcome {
final_state,
transcript,
payoff,
}
}
pub fn final_state(&self) -> &S {
&self.final_state
}
pub fn transcript(&self) -> &Transcript<M, P> {
&self.transcript
}
}
impl<S: State, M: Move, U: Utility, const P: usize> Outcome<M, U, P>
for SequentialOutcome<S, M, U, P>
{
type Record = Transcript<M, P>;
fn record(&self) -> &Transcript<M, P> {
&self.transcript
}
fn payoff(&self) -> &Payoff<U, P> {
&self.payoff
}
}
#[derive(Clone)]
pub struct PossibleOutcomes<'g, M: Move, U: Utility, const P: usize> {
profile_iter: PossibleProfiles<'g, M, P>,
payoff_fn: Arc<dyn Fn(Profile<M, P>) -> Payoff<U, P> + Send + Sync + 'g>,
}
impl<'g, M: Move, U: Utility, const P: usize> PossibleOutcomes<'g, M, U, P> {
pub fn new(
profile_iter: PossibleProfiles<'g, M, P>,
payoff_fn: Arc<dyn Fn(Profile<M, P>) -> Payoff<U, P> + Send + Sync + 'g>,
) -> Self {
PossibleOutcomes {
profile_iter,
payoff_fn,
}
}
pub fn include(self, player: PlayerIndex<P>, the_move: M) -> Self {
PossibleOutcomes {
profile_iter: self.profile_iter.include(player, the_move),
..self
}
}
pub fn exclude(self, player: PlayerIndex<P>, the_move: M) -> Self {
PossibleOutcomes {
profile_iter: self.profile_iter.exclude(player, the_move),
..self
}
}
pub fn adjacent(self, player: PlayerIndex<P>, profile: Profile<M, P>) -> Self {
PossibleOutcomes {
profile_iter: self.profile_iter.adjacent(player, profile),
..self
}
}
}
impl<'g, M: Move, U: Utility, const P: usize> Iterator for PossibleOutcomes<'g, M, U, P> {
type Item = SimultaneousOutcome<M, U, P>;
fn next(&mut self) -> Option<Self::Item> {
self.profile_iter.next().map(|profile| {
let payoff = (*self.payoff_fn)(profile);
SimultaneousOutcome::new(profile, payoff)
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use impls::impls;
use test_log::test;
#[test]
fn possible_outcomes_is_send_sync() {
assert!(impls!(PossibleOutcomes<'_, (), u8, 2>: Send & Sync));
}
}