1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
use crate::util::*;
use fnv::FnvHasher;
use rand::Rng;
use rand_xoshiro::rand_core::SeedableRng;
use rand_xoshiro::Xoshiro256PlusPlus;
use std::{cell::RefCell, hash::Hasher};
/// Ranty's random number generator, which is a thin wrapper around a xoshiro256++ PRNG.
#[derive(Debug)]
pub struct RantyRng {
seed: u64,
rng: RefCell<Xoshiro256PlusPlus>,
}
impl RantyRng {
/// Creates a new RNG with the supplied seed.
pub fn new(seed: u64) -> Self {
Self {
seed,
rng: RefCell::new(Xoshiro256PlusPlus::seed_from_u64(seed)),
}
}
/// Creates a new RNG by hashing the parent seed with the supplied `u64` to produce a new seed.
/// Uses the Fowler-Noll-Vo hash function.
pub fn fork_u64(&self, seed: u64) -> Self {
let mut hasher = FnvHasher::default();
hasher.write_u64(self.seed);
hasher.write_u64(seed);
RantyRng::new(hasher.finish())
}
/// Creates a new RNG by hashing the parent seed with the supplied `i64` to produce a new seed.
/// Uses the Fowler-Noll-Vo hash function.
pub fn fork_i64(&self, seed: i64) -> Self {
let mut hasher = FnvHasher::default();
hasher.write_u64(self.seed);
hasher.write_i64(seed);
RantyRng::new(hasher.finish())
}
/// Creates a new RNG by hashing the parent seed with the supplied string to produce a new seed.
/// Uses the Fowler-Noll-Vo hash function.
pub fn fork_str(&self, seed: &str) -> Self {
let mut hasher = FnvHasher::default();
hasher.write_u64(self.seed);
hasher.write(seed.as_bytes());
RantyRng::new(hasher.finish())
}
/// Creates a new RNG by hashing the parent seed and with the current generation to produce a new seed.
/// Uses the Fowler-Noll-Vo hash function.
pub fn fork_random(&self) -> Self {
let mut hasher = FnvHasher::default();
hasher.write_u64(self.seed);
hasher.write_u64(self.rng.borrow_mut().gen());
RantyRng::new(hasher.finish())
}
}
impl RantyRng {
/// Gets the current seed of the RNG.
pub fn seed(&self) -> u64 {
self.seed
}
/// Generates a pseudorandom `i64` between two inclusive values. The range may be specified in either order.
#[inline]
pub fn next_i64(&self, a: i64, b: i64) -> i64 {
if a == b {
return a;
}
let (min, max) = minmax(a, b);
self.rng.borrow_mut().gen_range(min..=max)
}
/// Generates a pseudorandom `f64` between two inclusive values. The range may be specified in either order.
#[inline]
pub fn next_f64(&self, a: f64, b: f64) -> f64 {
if a.eq(&b) {
return a;
}
let (min, max) = minmax(a, b);
self.rng.borrow_mut().gen_range(min..max)
}
/// Generates a pseudorandom `usize` between 0 and `max` (exclusive).
#[inline]
pub fn next_usize(&self, max: usize) -> usize {
self.rng.borrow_mut().gen_range(0..max)
}
#[inline]
pub(crate) fn next_usize_weighted(
&self,
max: usize,
weights: &[f64],
weight_sum: f64,
) -> usize {
if weight_sum > 0.0 {
let mut rem = self.rng.borrow_mut().gen_range(0.0..weight_sum);
for (i, w) in weights.iter().enumerate() {
if *w == 0.0 {
continue;
}
if &rem < w {
return i;
}
rem -= w;
}
}
max - 1
}
/// Generates a pseudorandom `f64` between 0 and 1.
#[inline]
pub fn next_normal_f64(&self) -> f64 {
self.rng.borrow_mut().gen()
}
/// Generates a `bool` with `p` probability of being `true`.
#[inline]
pub fn next_bool(&self, p: f64) -> bool {
self.rng.borrow_mut().gen_bool(saturate(p))
}
}