diff options
Diffstat (limited to 'src/statistics/boon.rs')
-rw-r--r-- | src/statistics/boon.rs | 123 |
1 files changed, 123 insertions, 0 deletions
diff --git a/src/statistics/boon.rs b/src/statistics/boon.rs index 425f4a5..8175537 100644 --- a/src/statistics/boon.rs +++ b/src/statistics/boon.rs @@ -1,5 +1,14 @@ +//! Module providing functions and structs to deal with boon related statistics. use std::cmp; +use std::collections::HashMap; +use std::fmt; +use std::ops::Mul; + +use super::math::{Monoid, RecordFunc, Semigroup}; + +use fnv::FnvHashMap; + /// The type of a boon. #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum BoonType { @@ -40,6 +49,7 @@ pub struct BoonQueue { capacity: u32, queue: Vec<u64>, boon_type: BoonType, + next_update: u64, } impl BoonQueue { @@ -52,6 +62,7 @@ impl BoonQueue { capacity, queue: Vec::new(), boon_type, + next_update: 0, } } @@ -75,6 +86,7 @@ impl BoonQueue { pub fn add_stack(&mut self, duration: u64) { self.queue.push(duration); self.fix_queue(); + self.next_update = self.next_change(); } /// Return the amount of current stacks. @@ -100,6 +112,10 @@ impl BoonQueue { if duration == 0 { return; } + if duration < self.next_update { + self.next_update -= duration; + return; + } let mut remaining = duration; match self.boon_type { BoonType::Duration => { @@ -125,6 +141,7 @@ impl BoonQueue { .collect(); } } + self.next_update = self.next_change(); } /// Remove all stacks. @@ -163,6 +180,112 @@ impl BoonQueue { } } +/// Amount of stacks of a boon. +// Since this is also used to represent changes in stacks, we need access to +// negative numbers too, as stacks can drop. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct Stacks(i32); + +impl Semigroup for Stacks { + #[inline] + fn combine(&self, other: &Self) -> Self { + Stacks(self.0 + other.0) + } +} + +impl Monoid for Stacks { + #[inline] + fn mempty() -> Self { + Stacks(0) + } +} + +// This shouldn't be negative, as total stacks are always greater than 0, thus +// the area below the curve will always be positive. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[doc(hidden)] +pub struct BoonArea(u64); + +impl Semigroup for BoonArea { + #[inline] + fn combine(&self, other: &Self) -> Self { + BoonArea(self.0 + other.0) + } +} + +impl Monoid for BoonArea { + #[inline] + fn mempty() -> Self { + BoonArea(0) + } +} + +impl Mul<u64> for Stacks { + type Output = BoonArea; + + #[inline] + fn mul(self, rhs: u64) -> BoonArea { + BoonArea(self.0 as u64 * rhs) + } +} + +/// A boon log for a specific player. +/// +/// This logs the amount of stacks of each boon a player had at any given time. +#[derive(Clone, Default)] +pub struct BoonLog { + // Keep a separate RecordFunc for each boon. + inner: FnvHashMap<u16, RecordFunc<u64, (), Stacks>>, +} + +impl BoonLog { + /// Create a new, empty boon log. + pub fn new() -> Self { + Default::default() + } + + /// Add an event to the boon log. + pub fn log(&mut self, time: u64, boon_id: u16, stacks: u32) { + let func = self.inner.entry(boon_id).or_insert_with(Default::default); + let current = func.tally(); + if current.0 == stacks as i32 { + return; + } + let diff = stacks as i32 - current.0; + func.insert(time, (), Stacks(diff)); + } + + /// Get the average amount of stacks between the two given time points. + /// + /// * `a` - Start time point. + /// * `b` - End time point. + /// * `boon_id` - ID of the boon that you want to get the average for. + pub fn average_stacks(&self, a: u64, b: u64, boon_id: u16) -> f32 { + assert!(b > a); + let func = if let Some(f) = self.inner.get(&boon_id) { + f + } else { + return 0.; + }; + let area = func.integral(&a, &b); + area.0 as f32 / (b - a) as f32 + } + + /// Get the amount of stacks at the given time point. + /// + /// * `x` - Time point. + /// * `boon_id` - ID of the boon that you want to get. + pub fn stacks_at(&self, x: u64, boon_id: u16) -> u32 { + self.inner.get(&boon_id).map(|f| f.get(&x)).unwrap_or(0) + } +} + +impl fmt::Debug for BoonLog { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "BoonLog {{ .. }}") + } +} + #[cfg(test)] mod test { use super::*; |