use allocative::Allocative; use std::cmp::Ordering; #[derive(Clone, Copy, PartialEq, Eq, Allocative, Debug, PartialOrd, Ord)] pub enum MVSGameState { Win = 1, Tie = 0, Loss = -1, } #[derive(Clone, Copy, Debug, Allocative, PartialEq, Eq, Default, PartialOrd)] pub struct MoveValueStats { state: Option, wins: u16, losses: u16, ties: u16, pub value: i32, } impl MoveValueStats { #[cfg(test)] pub fn new_from_wins_losses(wins: u16, losses: u16) -> Self { Self { wins, losses, ..Default::default() } } #[cfg(test)] pub fn new_from_value(value: i32) -> Self { Self { value, ..Default::default() } } #[cfg(test)] pub fn new_from_state(state: Option) -> Self { Self { state, ..Default::default() } } fn chance_win(&self) -> Option { let sum = self.losses + self.wins + self.ties; if 20 > sum { return None; } Some(self.wins as f32 / sum as f32) } pub const fn set_state(&mut self, state: Option) { self.state = state; } pub fn populate_self_from_children(&mut self, others: &[Self]) { self.wins = others.iter().map(|x| x.wins).sum::() + others .iter() .filter(|x| x.state == Some(MVSGameState::Win)) .count() as u16; self.losses = others.iter().map(|x| x.losses).sum::() + others .iter() .filter(|x| x.state == Some(MVSGameState::Loss)) .count() as u16; self.ties = others.iter().map(|x| x.ties).sum::() + others .iter() .filter(|x| x.state == Some(MVSGameState::Tie)) .count() as u16; } } impl Ord for MoveValueStats { fn cmp(&self, other: &Self) -> Ordering { if self.state.is_some() || other.state.is_some() { return self.state.cmp(&other.state); } let (s_cw, o_cw) = (self.chance_win(), other.chance_win()); if s_cw.is_some() | o_cw.is_some() { if s_cw > o_cw { return Ordering::Greater; } else if o_cw > s_cw { return Ordering::Less; } } self.value.cmp(&other.value) } } #[cfg(test)] mod tests { use super::*; #[test] fn two_prob() { let one = MoveValueStats::new_from_wins_losses(10, 4); let two = MoveValueStats::new_from_wins_losses(4, 6); assert!(one > two); } #[test] fn one_prob_one_non() { let one = MoveValueStats::new_from_wins_losses(10, 4); let two = MoveValueStats::new_from_value(10); assert!(one > two); } #[test] fn one_prob_one_win() { let one = MoveValueStats::new_from_wins_losses(10, 4); let two = MoveValueStats::new_from_state(Some(MVSGameState::Win)); assert!(one < two); } #[test] fn two_prob_zero() { let one = MoveValueStats::new_from_wins_losses(10, 0); let two = MoveValueStats::new_from_wins_losses(0, 6); assert!(one > two); } }