doryen_extra/
random.rs

1/* BSD 3-Clause License
2 *
3 * Copyright © 2019, Alexander Krivács Schrøder <alexschrod@gmail.com>.
4 * Copyright © 2008-2019, Jice and the libtcod contributors.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright notice,
11 *    this list of conditions and the following disclaimer.
12 *
13 * 2. Redistributions in binary form must reproduce the above copyright notice,
14 *    this list of conditions and the following disclaimer in the documentation
15 *    and/or other materials provided with the distribution.
16 *
17 * 3. Neither the name of the copyright holder nor the names of its
18 *    contributors may be used to endorse or promote products derived from
19 *    this software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
25 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 * POSSIBILITY OF SUCH DAMAGE.
32 */
33
34//! Pseudorandom number generator using the Mersenne Twister or Complementary Multiply With Carry
35//! algorithms.
36//!
37//! This toolkit used to be named `mersenne` in libtcod.
38
39pub mod algorithms;
40
41use crate::random::algorithms::Algorithm;
42use crate::random::algorithms::{ComplementaryMultiplyWithCarry, MersenneTwister};
43use std::cmp::Ordering;
44use std::time::SystemTime;
45
46/// Trait providing methods for generating random numbers.
47pub trait Rng {
48    /// Get an `i32` between `min` and `max`.
49    fn get_i32(&mut self, min: i32, max: i32) -> i32;
50
51    /// Get an `f32` between `min` and `max`.
52    fn get_f32(&mut self, min: f32, max: f32) -> f32;
53
54    /// Get an `f64` between `min` and `max`.
55    fn get_f64(&mut self, min: f64, max: f64) -> f64;
56
57    /// Get an `i32` between `min` and `max`, using gaussian distribution with the given `mean`.
58    fn get_i32_mean(&mut self, min: i32, max: i32, mean: i32) -> i32;
59
60    /// Get an `f32` between `min` and `max`, using gaussian distribution with the given `mean`.
61    fn get_f32_mean(&mut self, min: f32, max: f32, mean: f32) -> f32;
62
63    /// Get an `f64` between `min` and `max`, using gaussian distribution with the given `mean`.
64    fn get_f64_mean(&mut self, min: f64, max: f64, mean: f64) -> f64;
65}
66
67/// pseudorandom number generator toolkit
68#[derive(Clone, Debug)]
69pub struct Random<A: Algorithm> {
70    /* algorithm identifier */
71    algo: A,
72    /* distribution */
73    /// Decides the distribution used for generating random numbers
74    pub distribution: Distribution,
75
76    // Used for gaussian result caching
77    y2: Option<f64>,
78}
79
80impl<A: Algorithm> Random<A> {
81    fn default_seed() -> u64 {
82        let now = SystemTime::now();
83        let duration_since = now.duration_since(SystemTime::UNIX_EPOCH).unwrap();
84        duration_since.as_secs()
85    }
86
87    fn get_i(&mut self, mut min: i32, mut max: i32) -> i32 {
88        match max.cmp(&min) {
89            Ordering::Less => std::mem::swap(&mut min, &mut max),
90            Ordering::Equal => return min,
91            Ordering::Greater => (),
92        }
93
94        let delta = max - min + 1; // + 1 because it's used for modulo
95
96        (self.algo.get_int() % delta as u32) as i32 + min
97    }
98
99    fn get_f(&mut self, mut min: f32, mut max: f32) -> f32 {
100        if (max - min).abs() < 0.000_001 {
101            return min;
102        } else if max < min {
103            std::mem::swap(&mut min, &mut max);
104        }
105
106        let delta = max - min;
107
108        self.algo.get_float() * delta + min
109    }
110
111    fn get_d(&mut self, mut min: f64, mut max: f64) -> f64 {
112        if (max - min).abs() < 0.000_001 {
113            return min;
114        } else if max < min {
115            std::mem::swap(&mut min, &mut max);
116        }
117
118        let delta = max - min;
119
120        self.algo.get_double() * delta + min
121    }
122
123    /* Box-Muller transform (Gaussian distribution) */
124
125    fn get_gaussian_double(&mut self, mean: f64, std_deviation: f64) -> f64 {
126        if let Some(y2) = self.y2.take() {
127            return mean + y2 * std_deviation;
128        }
129
130        let (x1, x2, w) = loop {
131            let x1 = self.algo.get_double() * 2.0 - 1.0;
132            let x2 = self.algo.get_double() * 2.0 - 1.0;
133            let w = x1.powi(2) + x2.powi(2);
134            if w < 1.0 {
135                break (x1, x2, (-2.0 * w.ln() / w).sqrt());
136            }
137        };
138
139        let y1 = x1 * w;
140        self.y2 = Some(x2 * w);
141
142        mean + y1 * std_deviation
143    }
144
145    fn get_gaussian_float(&mut self, mean: f32, std_deviation: f32) -> f32 {
146        self.get_gaussian_double(f64::from(mean), f64::from(std_deviation)) as f32
147    }
148
149    fn get_gaussian_int(&mut self, mean: i32, std_deviation: i32) -> i32 {
150        self.get_gaussian_double(f64::from(mean), f64::from(std_deviation))
151            .round() as i32
152    }
153
154    /* Box-Muller, ranges */
155
156    fn get_gaussian_double_range(&mut self, mut min: f64, mut max: f64) -> f64 {
157        if max < min {
158            std::mem::swap(&mut min, &mut max);
159        }
160
161        let mean = (min + max) / 2.0;
162        let std_deviation = (max - min) / 6.0; /* 6.0 is used because of the three-sigma rule */
163
164        self.get_gaussian_double(mean, std_deviation)
165            .max(min)
166            .min(max)
167    }
168
169    fn get_gaussian_float_range(&mut self, min: f32, max: f32) -> f32 {
170        self.get_gaussian_double_range(f64::from(min), f64::from(max)) as f32
171    }
172
173    fn get_gaussian_int_range(&mut self, min: i32, max: i32) -> i32 {
174        self.get_gaussian_double_range(f64::from(min), f64::from(max))
175            .round() as i32
176    }
177
178    /* Box-Muller, ranges with a custom mean */
179
180    fn get_gaussian_double_range_custom(&mut self, mut min: f64, mut max: f64, mean: f64) -> f64 {
181        if max < min {
182            std::mem::swap(&mut min, &mut max);
183        }
184        let d1 = max - mean;
185        let d2 = mean - min;
186        let std_deviation = d1.max(d2) / 3.0;
187
188        self.get_gaussian_double(mean, std_deviation)
189            .max(min)
190            .min(max)
191    }
192
193    fn get_gaussian_float_range_custom(&mut self, min: f32, max: f32, mean: f32) -> f32 {
194        self.get_gaussian_double_range_custom(f64::from(min), f64::from(max), f64::from(mean))
195            as f32
196    }
197
198    fn get_gaussian_int_range_custom(&mut self, min: i32, max: i32, mean: i32) -> i32 {
199        (self
200            .get_gaussian_double_range_custom(f64::from(min), f64::from(max), f64::from(mean))
201            .round() as i32)
202            .max(min)
203            .min(max)
204    }
205
206    /* Box-Muller, inverted distribution */
207
208    fn get_gaussian_double_inv(&mut self, mean: f64, std_deviation: f64) -> f64 {
209        let num = self.get_gaussian_double(mean, std_deviation);
210        if num >= mean {
211            num - 3.0 * std_deviation
212        } else {
213            num + 3.0 * std_deviation
214        }
215    }
216
217    fn get_gaussian_float_inv(&mut self, mean: f32, std_deviation: f32) -> f32 {
218        let num = self.get_gaussian_double(f64::from(mean), f64::from(std_deviation));
219        if num >= f64::from(mean) {
220            (num - 3.0 * f64::from(std_deviation)) as f32
221        } else {
222            (num + 3.0 * f64::from(std_deviation)) as f32
223        }
224    }
225
226    fn get_gaussian_int_inv(&mut self, mean: i32, std_deviation: i32) -> i32 {
227        let num = self.get_gaussian_double(f64::from(mean), f64::from(std_deviation));
228        let integer = num.round() as i32;
229        if num >= f64::from(mean) {
230            integer - 3 * std_deviation
231        } else {
232            integer + 3 * std_deviation
233        }
234    }
235
236    /* Box-Muller, ranges, inverted distribution */
237
238    fn get_gaussian_double_range_inv(&mut self, mut min: f64, mut max: f64) -> f64 {
239        if max < min {
240            std::mem::swap(&mut min, &mut max);
241        }
242        let mean = (min + max) / 2.0;
243        let std_deviation = (max - min) / 6.0; /* 6.0 is used because of the three-sigma rule */
244
245        self.get_gaussian_double_inv(mean, std_deviation)
246            .max(min)
247            .min(max)
248    }
249
250    fn get_gaussian_float_range_inv(&mut self, min: f32, max: f32) -> f32 {
251        self.get_gaussian_double_range_inv(f64::from(min), f64::from(max)) as f32
252    }
253
254    fn get_gaussian_int_range_inv(&mut self, min: i32, max: i32) -> i32 {
255        (self
256            .get_gaussian_double_range_inv(f64::from(min), f64::from(max))
257            .round() as i32)
258            .max(min)
259            .min(max)
260    }
261
262    /* Box-Muller, ranges with a custom mean, inverted distribution */
263
264    fn get_gaussian_double_range_custom_inv(
265        &mut self,
266        mut min: f64,
267        mut max: f64,
268        mean: f64,
269    ) -> f64 {
270        if max < min {
271            std::mem::swap(&mut min, &mut max);
272        }
273
274        let d1 = max - mean;
275        let d2 = mean - min;
276        let std_deviation = d1.max(d2) / 3.0;
277
278        self.get_gaussian_double_inv(mean, std_deviation)
279            .max(min)
280            .min(max)
281    }
282
283    fn get_gaussian_float_range_custom_inv(&mut self, min: f32, max: f32, mean: f32) -> f32 {
284        self.get_gaussian_double_range_custom_inv(f64::from(min), f64::from(max), f64::from(mean))
285            as f32
286    }
287
288    fn get_gaussian_int_range_custom_inv(&mut self, min: i32, max: i32, mean: i32) -> i32 {
289        (self
290            .get_gaussian_double_range_custom_inv(f64::from(min), f64::from(max), f64::from(mean))
291            .round() as i32)
292            .max(min)
293            .min(max)
294    }
295}
296
297impl<A: Algorithm> Rng for Random<A> {
298    fn get_i32(&mut self, min: i32, max: i32) -> i32 {
299        match self.distribution {
300            Distribution::Linear => self.get_i(min, max),
301            Distribution::Gaussian => self.get_gaussian_int(min, max),
302            Distribution::GaussianRange => self.get_gaussian_int_range(min, max),
303            Distribution::GaussianInverse => self.get_gaussian_int_inv(min, max),
304            Distribution::GaussianRangeInverse => self.get_gaussian_int_range_inv(min, max),
305        }
306    }
307
308    fn get_f32(&mut self, min: f32, max: f32) -> f32 {
309        match self.distribution {
310            Distribution::Linear => self.get_f(min, max),
311            Distribution::Gaussian => self.get_gaussian_float(min, max),
312            Distribution::GaussianRange => self.get_gaussian_float_range(min, max),
313            Distribution::GaussianInverse => self.get_gaussian_float_inv(min, max),
314            Distribution::GaussianRangeInverse => self.get_gaussian_float_range_inv(min, max),
315        }
316    }
317
318    fn get_f64(&mut self, min: f64, max: f64) -> f64 {
319        match self.distribution {
320            Distribution::Linear => self.get_d(min, max),
321            Distribution::Gaussian => self.get_gaussian_double(min, max),
322            Distribution::GaussianRange => self.get_gaussian_double_range(min, max),
323            Distribution::GaussianInverse => self.get_gaussian_double_inv(min, max),
324            Distribution::GaussianRangeInverse => self.get_gaussian_double_range_inv(min, max),
325        }
326    }
327
328    fn get_i32_mean(&mut self, min: i32, max: i32, mean: i32) -> i32 {
329        match self.distribution {
330            Distribution::GaussianInverse | Distribution::GaussianRangeInverse => {
331                self.get_gaussian_int_range_custom_inv(min, max, mean)
332            }
333            _ => self.get_gaussian_int_range_custom(min, max, mean),
334        }
335    }
336
337    fn get_f32_mean(&mut self, min: f32, max: f32, mean: f32) -> f32 {
338        match self.distribution {
339            Distribution::GaussianInverse | Distribution::GaussianRangeInverse => {
340                self.get_gaussian_float_range_custom_inv(min, max, mean)
341            }
342            _ => self.get_gaussian_float_range_custom(min, max, mean),
343        }
344    }
345
346    fn get_f64_mean(&mut self, min: f64, max: f64, mean: f64) -> f64 {
347        match self.distribution {
348            Distribution::GaussianInverse | Distribution::GaussianRangeInverse => {
349                self.get_gaussian_double_range_custom_inv(min, max, mean)
350            }
351            _ => self.get_gaussian_double_range_custom(min, max, mean),
352        }
353    }
354}
355
356impl Random<MersenneTwister> {
357    /// Returns a new `Random` using the Mersenne Twister algorithm.
358    pub fn new_mt() -> Self {
359        Self::new_mt_from_seed(Self::default_seed() as u32)
360    }
361
362    /// Returns a new `Random` using the Mersenne Twister algorithm, seeded with the given `seed`.
363    pub fn new_mt_from_seed(seed: u32) -> Self {
364        Self {
365            algo: MersenneTwister::new(seed),
366            distribution: Distribution::Linear,
367
368            y2: None,
369        }
370    }
371}
372
373impl Random<ComplementaryMultiplyWithCarry> {
374    /// Returns a new `Random` using the Complementary Multiply With Carry algorithm.
375    pub fn new_cmwc() -> Self {
376        Self::new_cmwc_from_seed(Self::default_seed() as u32)
377    }
378
379    /// Returns a new `Random` using the Complementary Multiply With Carry algorithm,
380    /// seeded with the given `seed`.
381    pub fn new_cmwc_from_seed(seed: u32) -> Self {
382        Self {
383            algo: ComplementaryMultiplyWithCarry::new(seed),
384            distribution: Distribution::Linear,
385
386            y2: None,
387        }
388    }
389}
390
391/// The distribution to use when generating random numbers
392#[derive(Clone, Copy, Debug)]
393pub enum Distribution {
394    /// Linear distribution; all numbers are equally likely.
395    Linear,
396    /// Gaussian distribution; uses a mean and standard deviation to generate numbers.
397    Gaussian,
398    /// Gaussian range distribution; uses the given min and max values to derive a mean and
399    /// standard deviation for generating numbers.
400    GaussianRange,
401    /// Gaussian inverse distribution; uses a mean and standard deviation to generate numbers.
402    GaussianInverse,
403    /// Gaussian inverse range distribution; uses the given min and max values to derive a mean and
404    /// standard deviation for generating numbers.
405    GaussianRangeInverse,
406}
407
408/* string hashing function */
409/* not used (yet)
410fn hash(data: &[u8]) -> u32 {
411    let mut hash: u32 = 0;
412    for d in data {
413        hash = (hash << 4) + *d as u32;
414        let x = hash & 0xF000_0000;
415        if x != 0 {
416            hash ^= x >> 24;
417            hash &= !x;
418        }
419    }
420
421    hash & 0x7FFF_FFFF
422}
423*/
424
425/// Represents a set of dice and rules for calculating their value when rolled
426#[derive(Debug, Copy, Clone)]
427pub struct Dice {
428    nb_rolls: i32,
429    nb_faces: i32,
430    multiplier: f32,
431    add_sub: f32,
432}
433
434impl Dice {
435    /// Create a new `Dice` with the given dice specification. The specification is as follows:
436    /// `[mul*]<rolls>d<faces>[+/-offset]`, where
437    /// * `rolls` number of dice is thrown,
438    /// * these dice have `faces` number of faces,
439    /// * once all the dice have been thrown, `offset` is added to their value,
440    /// * and finally, that number is multiplied by `mul`.
441    ///
442    /// # Example
443    /// ```
444    /// # use doryen_extra::random::Dice;
445    /// let dice = Dice::new("5*3d6+2");
446    /// ```
447    pub fn new<S: AsRef<str>>(s: S) -> Self {
448        let mut s = s.as_ref();
449
450        /* get multiplier */
451        let multiplier = if let Some(m) = s.find(|c| c == '*' || c == 'x') {
452            let value = s[0..m].parse::<f32>().unwrap_or_default();
453            s = &s[m + 1..];
454
455            value
456        } else {
457            1.0
458        };
459
460        /* get rolls */
461        let r = s
462            .find(|c| c == 'd' || c == 'D')
463            .expect("Incorrect dice specification format");
464        let nb_rolls = s[0..r].parse::<i32>().unwrap_or_default();
465        s = &s[r + 1..];
466
467        /* get faces */
468        let nb_faces = if let Some(f) = s.find(|c| c == '+' || c == '-') {
469            let value = s[0..f].parse::<i32>().unwrap_or_default();
470            s = &s[f..];
471
472            value
473        } else {
474            let value = s[0..].parse::<i32>().unwrap_or_default();
475            s = &s[s.len()..];
476
477            value
478        };
479
480        /* get add_sub */
481        let add_sub = if s.is_empty() {
482            0.0
483        } else {
484            s[0..].parse::<f32>().unwrap_or_default()
485        };
486
487        Self {
488            multiplier,
489            nb_rolls,
490            nb_faces,
491            add_sub,
492        }
493    }
494
495    /// Roll the dice according to their parameters. See the documentation of `new()` for how these
496    /// parameters get used.
497    pub fn roll<R: Rng>(&self, mersenne: &mut R) -> i32 {
498        let mut result = 0;
499        for _ in 0..self.nb_rolls {
500            result += mersenne.get_i32(1, self.nb_faces);
501        }
502
503        ((result as f32 + self.add_sub) * self.multiplier) as i32
504    }
505
506    /// Create a `Dice` and roll these dice once according to the given dice specification. See the
507    /// documentation of `new()` for how this specification works. If you intend to use this dice
508    /// set more than once, it's generally better to store the `Dice` instance and call `roll()`
509    /// rather than to call this method over and over.
510    pub fn single_roll<R: Rng, S: AsRef<str>>(mersenne: &mut R, s: S) -> i32 {
511        Self::new(s).roll(mersenne)
512    }
513}
514
515#[cfg(feature = "rng_support")]
516impl<A: Algorithm> rand_core::RngCore for Random<A> {
517    fn next_u32(&mut self) -> u32 {
518        self.algo.get_int()
519    }
520
521    fn next_u64(&mut self) -> u64 {
522        use rand_core::impls;
523        impls::next_u64_via_u32(self)
524    }
525
526    fn fill_bytes(&mut self, dest: &mut [u8]) {
527        use rand_core::impls;
528        impls::fill_bytes_via_next(self, dest)
529    }
530
531    #[allow(clippy::unit_arg)] // Recommended by documentation
532    fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core::Error> {
533        Ok(self.fill_bytes(dest))
534    }
535}
536
537#[cfg(feature = "rng_support")]
538impl rand_core::SeedableRng for Random<MersenneTwister> {
539    type Seed = [u8; 4];
540
541    fn from_seed(seed: Self::Seed) -> Self {
542        let seed = u32::from(seed[0]) << 24
543            | u32::from(seed[1]) << 16
544            | u32::from(seed[2]) << 8
545            | u32::from(seed[3]);
546        Self::new_mt_from_seed(seed)
547    }
548}
549
550#[cfg(feature = "rng_support")]
551impl rand_core::SeedableRng for Random<ComplementaryMultiplyWithCarry> {
552    type Seed = [u8; 4];
553
554    fn from_seed(seed: Self::Seed) -> Self {
555        let seed = u32::from(seed[0]) << 24
556            | u32::from(seed[1]) << 16
557            | u32::from(seed[2]) << 8
558            | u32::from(seed[3]);
559        Self::new_cmwc_from_seed(seed)
560    }
561}