use derive_more::{AsMut, AsRef, Index, IndexMut};
use num::{FromPrimitive, Num};
use std::fmt::Debug;
use std::hash::Hash;
use std::ops::{Add, Mul, Sub};
use crate::{PerPlayer, PlayerIndex};
pub trait Utility:
Copy + Debug + Default + Num + PartialEq + PartialOrd + Send + Sized + Sync + 'static
{
}
impl<T: Copy + Debug + Default + Num + PartialEq + PartialOrd + Send + Sync + 'static> Utility
for T
{
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash, AsMut, AsRef, Index, IndexMut)]
pub struct Payoff<U, const P: usize> {
utilities: PerPlayer<U, P>,
}
impl<U: Utility, const P: usize> Payoff<U, P> {
pub fn new(utilities: PerPlayer<U, P>) -> Self {
Payoff { utilities }
}
pub fn flat(utility: U) -> Self {
Payoff::from([utility; P])
}
pub fn zeros() -> Self {
Payoff::flat(U::zero())
}
pub fn except(mut self, player: PlayerIndex<P>, utility: U) -> Self {
self.utilities[player] = utility;
self
}
pub fn num_players(&self) -> usize {
P
}
pub fn for_player(&self, i: usize) -> Option<U> {
self.utilities.get(i).copied()
}
pub fn for_player_mut(&mut self, i: usize) -> Option<&mut U> {
self.utilities.get_mut(i)
}
pub fn is_zeros(&self) -> bool {
self.utilities.iter().all(|&v| v.is_zero())
}
pub fn is_zero_sum(&self) -> bool {
let mut sum = U::zero();
for v in &self.utilities {
sum = sum.add(*v);
}
sum == U::zero()
}
pub fn pareto_improvement(&self, other: Self) -> Option<U> {
let mut improvement = U::zero();
for (v_self, v_other) in self.utilities.into_iter().zip(other.utilities) {
if v_self.le(&v_other) {
improvement = improvement.add(v_other.sub(v_self));
} else {
return None;
}
}
if improvement.is_zero() {
None
} else {
Some(improvement)
}
}
fn map(self, f: impl Fn(U) -> U) -> Self {
let mut result = [U::zero(); P];
for (r, v) in result.iter_mut().zip(self) {
*r = f(v);
}
Payoff::from(result)
}
fn zip_with(self, other: Self, combine: impl Fn(U, U) -> U) -> Self {
let mut result = [U::zero(); P];
for ((r, v), w) in result.iter_mut().zip(self).zip(other) {
*r = combine(v, w);
}
Payoff::from(result)
}
}
impl<U: Utility + FromPrimitive, const P: usize> Payoff<U, P> {
pub fn zero_sum_loser(loser: PlayerIndex<P>) -> Self {
let reward = U::one();
let penalty = U::one().sub(U::from_usize(P).unwrap());
Payoff::flat(reward).except(loser, penalty)
}
pub fn zero_sum_winner(winner: PlayerIndex<P>) -> Self {
let penalty = U::zero().sub(U::one());
let reward = U::from_usize(P).unwrap().sub(U::one());
Payoff::flat(penalty).except(winner, reward)
}
pub fn is_zero_sum_loser(&self, player: PlayerIndex<P>) -> bool {
self == &Payoff::zero_sum_loser(player)
}
pub fn is_zero_sum_winner(&self, player: PlayerIndex<P>) -> bool {
self == &Payoff::zero_sum_winner(player)
}
}
impl<U: Utility, const P: usize> From<[U; P]> for Payoff<U, P> {
fn from(utilities: [U; P]) -> Self {
Payoff::new(PerPlayer::new(utilities))
}
}
impl<U: Utility, const P: usize> Add<U> for Payoff<U, P> {
type Output = Self;
fn add(self, constant: U) -> Self {
self.map(|v| v + constant)
}
}
impl<U: Utility, const P: usize> Sub<U> for Payoff<U, P> {
type Output = Self;
fn sub(self, constant: U) -> Self {
self.map(|v| v - constant)
}
}
impl<U: Utility, const P: usize> Mul<U> for Payoff<U, P> {
type Output = Self;
fn mul(self, constant: U) -> Self {
self.map(|v| v * constant)
}
}
impl<U: Utility, const P: usize> Add<Self> for Payoff<U, P> {
type Output = Self;
fn add(self, other_payoff: Self) -> Self {
self.zip_with(other_payoff, |a, b| a + b)
}
}
impl<U: Utility, const P: usize> Sub<Self> for Payoff<U, P> {
type Output = Self;
fn sub(self, other_payoff: Self) -> Self {
self.zip_with(other_payoff, |a, b| a - b)
}
}
impl<U: Utility, const P: usize> Mul<Self> for Payoff<U, P> {
type Output = Self;
fn mul(self, other_payoff: Self) -> Self {
self.zip_with(other_payoff, |a, b| a * b)
}
}
impl<U, const P: usize> Payoff<U, P> {
pub fn iter(&self) -> <&PerPlayer<U, P> as IntoIterator>::IntoIter {
self.utilities.iter()
}
pub fn iter_mut(&mut self) -> <&mut PerPlayer<U, P> as IntoIterator>::IntoIter {
self.utilities.iter_mut()
}
}
impl<U, const P: usize> IntoIterator for Payoff<U, P> {
type Item = <PerPlayer<U, P> as IntoIterator>::Item;
type IntoIter = <PerPlayer<U, P> as IntoIterator>::IntoIter;
fn into_iter(self) -> <PerPlayer<U, P> as IntoIterator>::IntoIter {
self.utilities.into_iter()
}
}
impl<'a, U, const P: usize> IntoIterator for &'a Payoff<U, P> {
type Item = <&'a PerPlayer<U, P> as IntoIterator>::Item;
type IntoIter = <&'a PerPlayer<U, P> as IntoIterator>::IntoIter;
fn into_iter(self) -> <&'a PerPlayer<U, P> as IntoIterator>::IntoIter {
self.utilities.iter()
}
}
impl<'a, U, const P: usize> IntoIterator for &'a mut Payoff<U, P> {
type Item = <&'a mut PerPlayer<U, P> as IntoIterator>::Item;
type IntoIter = <&'a mut PerPlayer<U, P> as IntoIterator>::IntoIter;
fn into_iter(self) -> <&'a mut PerPlayer<U, P> as IntoIterator>::IntoIter {
self.utilities.iter_mut()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{for1, for2, for3, for4};
use test_log::test;
#[test]
fn zero_sum_loser_correct() {
assert_eq!(
Payoff::<i64, 1>::zero_sum_loser(for1::P0),
Payoff::from([0])
);
assert_eq!(
Payoff::<i64, 2>::zero_sum_loser(for2::P0),
Payoff::from([-1, 1])
);
assert_eq!(
Payoff::<i64, 2>::zero_sum_loser(for2::P1),
Payoff::from([1, -1])
);
assert_eq!(
Payoff::<i64, 3>::zero_sum_loser(for3::P0),
Payoff::from([-2, 1, 1])
);
assert_eq!(
Payoff::<i64, 3>::zero_sum_loser(for3::P1),
Payoff::from([1, -2, 1])
);
assert_eq!(
Payoff::<i64, 3>::zero_sum_loser(for3::P2),
Payoff::from([1, 1, -2])
);
assert_eq!(
Payoff::<i64, 4>::zero_sum_loser(for4::P0),
Payoff::from([-3, 1, 1, 1])
);
assert_eq!(
Payoff::<i64, 4>::zero_sum_loser(for4::P1),
Payoff::from([1, -3, 1, 1])
);
assert_eq!(
Payoff::<i64, 4>::zero_sum_loser(for4::P2),
Payoff::from([1, 1, -3, 1])
);
assert_eq!(
Payoff::<i64, 4>::zero_sum_loser(for4::P3),
Payoff::from([1, 1, 1, -3])
);
for i in 0..100 {
let p = Payoff::<i64, 100>::zero_sum_loser(PlayerIndex::new(i).unwrap()) * (i as i64);
assert!(p.is_zero_sum());
}
}
#[test]
fn zero_sum_winner_correct() {
assert_eq!(
Payoff::<i64, 1>::zero_sum_winner(for1::P0),
Payoff::from([0])
);
assert_eq!(
Payoff::<i64, 2>::zero_sum_winner(for2::P0),
Payoff::from([1, -1])
);
assert_eq!(
Payoff::<i64, 2>::zero_sum_winner(for2::P1),
Payoff::from([-1, 1])
);
assert_eq!(
Payoff::<i64, 3>::zero_sum_winner(for3::P0),
Payoff::from([2, -1, -1])
);
assert_eq!(
Payoff::<i64, 3>::zero_sum_winner(for3::P1),
Payoff::from([-1, 2, -1])
);
assert_eq!(
Payoff::<i64, 3>::zero_sum_winner(for3::P2),
Payoff::from([-1, -1, 2])
);
assert_eq!(
Payoff::<i64, 4>::zero_sum_winner(for4::P0),
Payoff::from([3, -1, -1, -1])
);
assert_eq!(
Payoff::<i64, 4>::zero_sum_winner(for4::P1),
Payoff::from([-1, 3, -1, -1])
);
assert_eq!(
Payoff::<i64, 4>::zero_sum_winner(for4::P2),
Payoff::from([-1, -1, 3, -1])
);
assert_eq!(
Payoff::<i64, 4>::zero_sum_winner(for4::P3),
Payoff::from([-1, -1, -1, 3])
);
for i in 0..100 {
let p = Payoff::<i64, 100>::zero_sum_winner(PlayerIndex::new(i).unwrap()) * (i as i64);
assert!(p.is_zero_sum());
}
}
#[test]
fn default_is_zeros() {
assert_eq!(Payoff::<u8, 5>::default(), Payoff::zeros());
assert_eq!(Payoff::<f64, 7>::default(), Payoff::zeros());
assert_eq!(Payoff::<i32, 101>::default(), Payoff::zeros());
}
}