diff options
| author | Daniel Schadt <kingdread@gmx.de> | 2018-04-25 13:32:11 +0200 | 
|---|---|---|
| committer | Daniel Schadt <kingdread@gmx.de> | 2018-04-25 13:32:11 +0200 | 
| commit | 7d35d1a4ecf4d7fe6689c83defd9163b7a66f915 (patch) | |
| tree | 433250fe3db638068d3190a17ef2d3f31ddf7293 | |
| parent | 4ea20d5a7d75c77a36bd4baf768226e3089b48ae (diff) | |
| download | evtclib-7d35d1a4ecf4d7fe6689c83defd9163b7a66f915.tar.gz evtclib-7d35d1a4ecf4d7fe6689c83defd9163b7a66f915.tar.bz2 evtclib-7d35d1a4ecf4d7fe6689c83defd9163b7a66f915.zip | |
add basic boon queue support
This is already pretty good to calculate the overall boon uptime/average
stacks.
| -rw-r--r-- | src/statistics/boon.rs | 175 | ||||
| -rw-r--r-- | src/statistics/mod.rs (renamed from src/statistics.rs) | 2 | 
2 files changed, 177 insertions, 0 deletions
| diff --git a/src/statistics/boon.rs b/src/statistics/boon.rs new file mode 100644 index 0000000..65e5c89 --- /dev/null +++ b/src/statistics/boon.rs @@ -0,0 +1,175 @@ +use std::cmp; + +/// The type of a boon. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum BoonType { +    /// Boon stacks duration, e.g. Regeneration. +    Duration, +    /// Boon stacks intensity, e.g. Might. +    Intensity, +} + +/// A struct that helps with simulating boon changes over time. +/// +/// This basically simulates a single boon-queue (for a single boon). +/// +/// # A quick word about how boon queues work +/// +/// For each boon, you have an internal *boon queue*, limited to a specific +/// capacity. +#[derive(Clone, Debug)] +pub struct BoonQueue { +    capacity: u32, +    queue: Vec<u64>, +    boon_type: BoonType, +} + +impl BoonQueue { +    /// Create a new boon queue. +    /// +    /// * `capacity` - The capacity of the queue. +    /// * `boon_type` - How the boons stack. +    pub fn new(capacity: u32, boon_type: BoonType) -> BoonQueue { +        BoonQueue { +            capacity, +            queue: Vec::new(), +            boon_type, +        } +    } + +    fn fix_queue(&mut self) { +        // Sort reversed, so that the longest stack is at the front. +        self.queue.sort_unstable_by(|a, b| b.cmp(a)); +        // Truncate queue by cutting of the shortest stacks +        if self.queue.len() > self.capacity as usize { +            self.queue.drain(self.capacity as usize..); +        } +    } + +    /// Get the type of this boon. +    pub fn boon_type(&self) -> BoonType { +        self.boon_type +    } + +    /// Add a boon stack to this queue. +    /// +    /// * `duration` - Duration (in milliseconds) of the added stack. +    pub fn add_stack(&mut self, duration: u64) { +        self.queue.push(duration); +        self.fix_queue(); +    } + +    /// Return the amount of current stacks. +    /// +    /// If the boon type is a duration boon, this will always return 0 or 1. +    /// +    /// If the boon type is an intensity boon, it will return the number of +    /// stacks. +    pub fn current_stacks(&self) -> u32 { +        let result = match self.boon_type { +            BoonType::Intensity => self.queue.len(), +            BoonType::Duration => cmp::min(1, self.queue.len()), +        }; +        result as u32 +    } + +    /// Simulate time passing. +    /// +    /// This will decrease the remaining duration of the stacks accordingly. +    /// +    /// * `duration` - The amount of time (in milliseconds) to simulate. +    pub fn simulate(&mut self, duration: u64) { +        let mut remaining = duration; +        match self.boon_type { +            BoonType::Duration => { +                while remaining > 0 && !self.queue.is_empty() { +                    let next = self.queue.remove(0); +                    if next > remaining { +                        self.queue.push(next - remaining); +                        break; +                    } else { +                        remaining -= next; +                    } +                } +                self.fix_queue(); +            } + +            BoonType::Intensity => { +                self.queue = self.queue +                    .iter() +                    .cloned() +                    .filter(|v| *v > duration) +                    .map(|v| v - duration) +                    .collect(); +            } +        } +    } + +    /// Remove all stacks. +    pub fn clear(&mut self) { +        self.queue.clear(); +    } + +    /// Calculate when the stacks will have the next visible change. +    /// +    /// This assumes that the stacks will not be modified during this time. +    /// +    /// The return value is the duration in milliseconds. If the boon queue is +    /// currently empty, 0 is returned. +    pub fn next_change(&self) -> u64 { +        match self.boon_type { +            BoonType::Duration => self.queue.iter().sum(), +            BoonType::Intensity => self.queue.last().cloned().unwrap_or(0), +        } +    } +} + +#[cfg(test)] +mod test { +    use super::*; + +    #[test] +    fn test_queue_capacity() { +        let mut queue = BoonQueue::new(5, BoonType::Intensity); +        assert_eq!(queue.current_stacks(), 0); +        for _ in 0..10 { +            queue.add_stack(10); +        } +        assert_eq!(queue.current_stacks(), 5); +    } + +    #[test] +    fn test_simulate_duration() { +        let mut queue = BoonQueue::new(10, BoonType::Duration); +        queue.add_stack(10); +        queue.add_stack(20); +        assert_eq!(queue.current_stacks(), 1); +        queue.simulate(30); +        assert_eq!(queue.current_stacks(), 0); + +        queue.add_stack(50); +        queue.simulate(30); +        assert_eq!(queue.current_stacks(), 1); +        queue.simulate(10); +        assert_eq!(queue.current_stacks(), 1); +        queue.simulate(15); +        assert_eq!(queue.current_stacks(), 0); +    } + +    #[test] +    fn test_simulate_intensity() { +        let mut queue = BoonQueue::new(5, BoonType::Intensity); + +        queue.add_stack(10); +        queue.add_stack(20); +        assert_eq!(queue.current_stacks(), 2); + +        queue.simulate(5); +        assert_eq!(queue.current_stacks(), 2); + +        queue.simulate(5); +        assert_eq!(queue.current_stacks(), 1); +        queue.simulate(15); +        assert_eq!(queue.current_stacks(), 0); +    } +} diff --git a/src/statistics.rs b/src/statistics/mod.rs index af208ce..a9dd178 100644 --- a/src/statistics.rs +++ b/src/statistics/mod.rs @@ -2,6 +2,8 @@  use super::*;  use std::collections::HashMap; +pub mod boon; +  pub type StatResult<T> = Result<T, StatError>;  quick_error! { | 
