use std::ops::ControlFlow;
use cast_trait::Cast;
use internal_iterator::{InternalIterator, IntoInternalIterator};
use crate::board::{Outcome, Player};
use crate::pov::{NonPov, Pov, ScalarAbs};
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum OutcomeWDL {
Win,
Draw,
Loss,
}
#[derive(Default, Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct WDL<V> {
pub win: V,
pub draw: V,
pub loss: V,
}
#[derive(Default, Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct WDLAbs<V> {
pub win_a: V,
pub draw: V,
pub win_b: V,
}
impl Outcome {
pub fn to_wdl_abs<V: num_traits::One + Default>(self) -> WDLAbs<V> {
let mut result = WDLAbs::default();
*match self {
Outcome::WonBy(Player::A) => &mut result.win_a,
Outcome::WonBy(Player::B) => &mut result.win_b,
Outcome::Draw => &mut result.draw,
} = V::one();
result
}
pub fn sign<V: num_traits::Zero + num_traits::One + std::ops::Neg<Output = V>>(self) -> ScalarAbs<V> {
match self {
Outcome::WonBy(Player::A) => ScalarAbs::new(V::one()),
Outcome::Draw => ScalarAbs::new(V::zero()),
Outcome::WonBy(Player::B) => ScalarAbs::new(-V::one()),
}
}
}
impl OutcomeWDL {
pub fn to_wdl<V: num_traits::One + Default>(self) -> WDL<V> {
let mut result = WDL::default();
*match self {
OutcomeWDL::Win => &mut result.win,
OutcomeWDL::Draw => &mut result.draw,
OutcomeWDL::Loss => &mut result.loss,
} = V::one();
result
}
pub fn sign<V: num_traits::Zero + num_traits::One + std::ops::Neg<Output = V>>(self) -> V {
match self {
OutcomeWDL::Win => V::one(),
OutcomeWDL::Draw => V::zero(),
OutcomeWDL::Loss => -V::one(),
}
}
pub fn un_pov(self, pov: Player) -> Outcome {
match self {
OutcomeWDL::Win => Outcome::WonBy(pov),
OutcomeWDL::Draw => Outcome::Draw,
OutcomeWDL::Loss => Outcome::WonBy(pov.other()),
}
}
pub fn best<I: IntoInternalIterator<Item = OutcomeWDL>>(children: I) -> OutcomeWDL {
Self::best_maybe(children.into_internal_iter().map(Some)).unwrap()
}
pub fn best_maybe<I: IntoInternalIterator<Item = Option<OutcomeWDL>>>(children: I) -> Option<OutcomeWDL> {
let mut any_unknown = false;
let mut all_known_are_loss = true;
let control = children.into_internal_iter().try_for_each(|child| {
match child {
None => {
any_unknown = true;
}
Some(OutcomeWDL::Win) => {
return ControlFlow::Break(());
}
Some(OutcomeWDL::Draw) => {
all_known_are_loss = false;
}
Some(OutcomeWDL::Loss) => {}
}
ControlFlow::Continue(())
});
if let ControlFlow::Break(()) = control {
Some(OutcomeWDL::Win)
} else if any_unknown {
None
} else if all_known_are_loss {
Some(OutcomeWDL::Loss)
} else {
Some(OutcomeWDL::Draw)
}
}
}
impl<V> NonPov for WDLAbs<V> {
type Output = WDL<V>;
fn pov(self, pov: Player) -> WDL<V> {
let (win, loss) = match pov {
Player::A => (self.win_a, self.win_b),
Player::B => (self.win_b, self.win_a),
};
WDL {
win,
draw: self.draw,
loss,
}
}
}
impl<V> Pov for WDL<V> {
type Output = WDLAbs<V>;
fn un_pov(self, pov: Player) -> Self::Output {
let (win_a, win_b) = match pov {
Player::A => (self.win, self.loss),
Player::B => (self.loss, self.win),
};
WDLAbs {
win_a,
draw: self.draw,
win_b,
}
}
}
impl<V> WDLAbs<V> {
pub fn new(win_a: V, draw: V, win_b: V) -> Self {
Self { win_a, draw, win_b }
}
}
impl<V> WDL<V> {
pub fn new(win: V, draw: V, loss: V) -> Self {
WDL { win, draw, loss }
}
pub fn to_slice(self) -> [V; 3] {
[self.win, self.draw, self.loss]
}
}
impl<V: num_traits::Float> WDL<V> {
pub fn nan() -> WDL<V> {
WDL {
win: V::nan(),
draw: V::nan(),
loss: V::nan(),
}
}
pub fn normalized(self) -> WDL<V> {
self / self.sum()
}
}
impl<V: num_traits::Float> WDLAbs<V> {
pub fn nan() -> WDLAbs<V> {
WDLAbs {
win_a: V::nan(),
draw: V::nan(),
win_b: V::nan(),
}
}
}
impl<V: num_traits::One + Default + PartialEq> WDLAbs<V> {
pub fn try_to_outcome(self) -> Option<Outcome> {
let outcomes = [Outcome::WonBy(Player::A), Outcome::Draw, Outcome::WonBy(Player::B)];
outcomes.iter().copied().find(|&o| o.to_wdl_abs() == self)
}
}
impl<V: num_traits::One + Default + PartialEq> WDL<V> {
pub fn try_to_outcome_wdl(self) -> Option<OutcomeWDL> {
let outcomes = [OutcomeWDL::Win, OutcomeWDL::Draw, OutcomeWDL::Loss];
outcomes.iter().copied().find(|&o| o.to_wdl() == self)
}
}
impl<V: Copy> WDL<V> {
pub fn cast<W>(self) -> WDL<W>
where
V: Cast<W>,
{
WDL {
win: self.win.cast(),
draw: self.draw.cast(),
loss: self.loss.cast(),
}
}
}
impl<V: Copy + std::ops::Sub<V, Output = V>> WDL<V> {
pub fn value(self) -> V {
self.win - self.loss
}
}
impl<V: Copy + std::ops::Sub<V, Output = V>> WDLAbs<V> {
pub fn value(self) -> ScalarAbs<V> {
ScalarAbs::new(self.win_a - self.win_b)
}
}
impl<V: Copy + std::ops::Add<V, Output = V>> WDL<V> {
pub fn sum(self) -> V {
self.win + self.draw + self.loss
}
}
impl<V: Copy + std::ops::Add<V, Output = V>> WDLAbs<V> {
pub fn sum(self) -> V {
self.win_a + self.draw + self.win_b
}
}
impl NonPov for Outcome {
type Output = OutcomeWDL;
fn pov(self, pov: Player) -> OutcomeWDL {
match self {
Outcome::WonBy(player) => {
if player == pov {
OutcomeWDL::Win
} else {
OutcomeWDL::Loss
}
}
Outcome::Draw => OutcomeWDL::Draw,
}
}
}
impl Pov for OutcomeWDL {
type Output = Outcome;
fn un_pov(self, pov: Player) -> Outcome {
match self {
OutcomeWDL::Win => Outcome::WonBy(pov),
OutcomeWDL::Draw => Outcome::Draw,
OutcomeWDL::Loss => Outcome::WonBy(pov.other()),
}
}
}
impl<V: std::ops::Add<V, Output = V>> std::ops::Add<WDL<V>> for WDL<V> {
type Output = WDL<V>;
fn add(self, rhs: WDL<V>) -> Self::Output {
WDL {
win: self.win + rhs.win,
draw: self.draw + rhs.draw,
loss: self.loss + rhs.loss,
}
}
}
impl<V: Copy + std::ops::Sub<V, Output = V>> std::ops::Sub<WDL<V>> for WDL<V> {
type Output = WDL<V>;
fn sub(self, rhs: WDL<V>) -> Self::Output {
WDL {
win: self.win - rhs.win,
draw: self.draw - rhs.draw,
loss: self.loss - rhs.loss,
}
}
}
impl<V: Copy + std::ops::Add<V, Output = V>> std::ops::AddAssign<WDL<V>> for WDL<V> {
fn add_assign(&mut self, rhs: WDL<V>) {
*self = *self + rhs;
}
}
impl<V: Copy + std::ops::Mul<V, Output = V>> std::ops::Mul<V> for WDL<V> {
type Output = WDL<V>;
fn mul(self, rhs: V) -> Self::Output {
WDL {
win: self.win * rhs,
draw: self.draw * rhs,
loss: self.loss * rhs,
}
}
}
impl<V: Copy + std::ops::Div<V, Output = V>> std::ops::Div<V> for WDL<V> {
type Output = WDL<V>;
fn div(self, rhs: V) -> Self::Output {
WDL {
win: self.win / rhs,
draw: self.draw / rhs,
loss: self.loss / rhs,
}
}
}
impl<V: Default + Copy + std::ops::Add<Output = V>> std::iter::Sum<Self> for WDL<V> {
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
iter.fold(Self::default(), |a, v| a + v)
}
}
impl<'a, V: Default + Copy + std::ops::Add<Output = V>> std::iter::Sum<&'a Self> for WDL<V> {
fn sum<I: Iterator<Item = &'a Self>>(iter: I) -> Self {
iter.fold(Self::default(), |a, &v| a + v)
}
}
impl<V: std::ops::Add<V, Output = V>> std::ops::Add<WDLAbs<V>> for WDLAbs<V> {
type Output = WDLAbs<V>;
fn add(self, rhs: WDLAbs<V>) -> Self::Output {
WDLAbs {
win_a: self.win_a + rhs.win_a,
draw: self.draw + rhs.draw,
win_b: self.win_b + rhs.win_b,
}
}
}
impl<V: Copy + std::ops::Sub<V, Output = V>> std::ops::Sub<WDLAbs<V>> for WDLAbs<V> {
type Output = WDLAbs<V>;
fn sub(self, rhs: WDLAbs<V>) -> Self::Output {
WDLAbs {
win_a: self.win_a - rhs.win_a,
draw: self.draw - rhs.draw,
win_b: self.win_b - rhs.win_b,
}
}
}
impl<V: Copy + std::ops::Add<V, Output = V>> std::ops::AddAssign<WDLAbs<V>> for WDLAbs<V> {
fn add_assign(&mut self, rhs: WDLAbs<V>) {
*self = *self + rhs;
}
}
impl<V: Copy + std::ops::Mul<V, Output = V>> std::ops::Mul<V> for WDLAbs<V> {
type Output = WDLAbs<V>;
fn mul(self, rhs: V) -> Self::Output {
WDLAbs {
win_a: self.win_a * rhs,
draw: self.draw * rhs,
win_b: self.win_b * rhs,
}
}
}
impl<V: Copy + std::ops::Div<V, Output = V>> std::ops::Div<V> for WDLAbs<V> {
type Output = WDLAbs<V>;
fn div(self, rhs: V) -> Self::Output {
WDLAbs {
win_a: self.win_a / rhs,
draw: self.draw / rhs,
win_b: self.win_b / rhs,
}
}
}
impl<V: Default + Copy + std::ops::Add<Output = V>> std::iter::Sum for WDLAbs<V> {
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
iter.fold(Self::default(), |a, v| a + v)
}
}