1#[cfg(feature = "parsing")]
2use crate::prelude::{parse_dice_string, DiceParseError, DiceType};
3use rand::{Rng, RngCore, SeedableRng};
4use rand_xorshift::XorShiftRng;
5
6#[cfg(feature = "serde")]
7use serde_crate::{Deserialize, Serialize};
8
9#[cfg(target_arch = "wasm32")]
10fn get_seed() -> u64 {
11 let mut buf = [0u8; 8];
12 if crate::js_seed::getrandom_inner(&mut buf).is_ok() {
13 u64::from_be_bytes(buf)
14 } else {
15 js_sys::Date::now() as u64
16 }
17}
18
19#[cfg(not(target_arch = "wasm32"))]
20fn get_seed() -> u64 {
21 let mut buf = [0u8; 8];
22 if getrandom::getrandom(&mut buf).is_ok() {
23 u64::from_be_bytes(buf)
24 } else {
25 use std::time::{SystemTime, UNIX_EPOCH};
26 SystemTime::now()
27 .duration_since(UNIX_EPOCH)
28 .unwrap()
29 .as_secs() as u64
30 }
31}
32
33#[derive(Clone)]
34#[cfg_attr(
35 feature = "serde",
36 derive(Serialize, Deserialize),
37 serde(crate = "serde_crate")
38)]
39pub struct RandomNumberGenerator {
40 rng: XorShiftRng,
41}
42
43impl RandomNumberGenerator {
44 #[allow(clippy::new_without_default)] pub fn new() -> RandomNumberGenerator {
47 let rng: XorShiftRng = SeedableRng::seed_from_u64(get_seed());
48 RandomNumberGenerator { rng }
49 }
50
51 pub fn seeded(seed: u64) -> RandomNumberGenerator {
53 let rng: XorShiftRng = SeedableRng::seed_from_u64(seed);
54 RandomNumberGenerator { rng }
55 }
56
57 pub fn rand<T>(&mut self) -> T
59 where
60 rand::distributions::Standard: rand::distributions::Distribution<T>,
61 {
62 self.rng.gen::<T>()
63 }
64
65 pub fn range<T>(&mut self, min: T, max: T) -> T
69 where
70 T: rand::distributions::uniform::SampleUniform + PartialOrd,
71 {
72 self.rng.gen_range(min..max)
73 }
74
75 pub fn roll_dice(&mut self, n: i32, die_type: i32) -> i32 {
77 (0..n).map(|_| self.range(1, die_type + 1)).sum()
78 }
79
80 pub fn next_u64(&mut self) -> u64 {
82 self.rng.next_u64()
83 }
84
85 #[cfg(feature = "parsing")]
87 pub fn roll(&mut self, dice: DiceType) -> i32 {
88 self.roll_dice(dice.n_dice, dice.die_type) + dice.bonus
89 }
90
91 #[cfg(feature = "parsing")]
93 pub fn roll_str<S: ToString>(&mut self, dice: S) -> Result<i32, DiceParseError> {
94 match parse_dice_string(&dice.to_string()) {
95 Ok(dt) => Ok(self.roll(dt)),
96 Err(e) => Err(e),
97 }
98 }
99
100 pub fn random_slice_index<T>(&mut self, slice: &[T]) -> Option<usize> {
102 if slice.is_empty() {
103 None
104 } else {
105 let sz = slice.len();
106 if sz == 1 {
107 Some(0)
108 } else {
109 Some(self.roll_dice(1, sz as i32) as usize - 1)
110 }
111 }
112 }
113
114 pub fn random_slice_entry<'a, T>(&mut self, slice: &'a [T]) -> Option<&'a T> {
116 if slice.is_empty() {
117 None
118 } else {
119 let sz = slice.len();
120 if sz == 1 {
121 Some(&slice[0])
122 } else {
123 Some(&slice[self.roll_dice(1, sz as i32) as usize - 1])
124 }
125 }
126 }
127
128 pub fn get_rng(&mut self) -> &mut XorShiftRng {
131 &mut self.rng
132 }
133}
134
135#[cfg(test)]
136mod tests {
137 use crate::prelude::RandomNumberGenerator;
138
139 #[test]
140 fn roll_str_1d6() {
141 let mut rng = RandomNumberGenerator::new();
142 assert!(rng.roll_str("1d6").is_ok());
143 }
144
145 #[test]
146 fn roll_str_3d6plus1() {
147 let mut rng = RandomNumberGenerator::new();
148 assert!(rng.roll_str("3d6+1").is_ok());
149 }
150
151 #[test]
152 fn roll_str_3d20minus1() {
153 let mut rng = RandomNumberGenerator::new();
154 assert!(rng.roll_str("3d20-1").is_ok());
155 }
156
157 #[test]
158 fn roll_str_error() {
159 let mut rng = RandomNumberGenerator::new();
160 assert!(rng.roll_str("blah").is_err());
161 }
162
163 #[test]
164 fn test_roll_range() {
165 let mut rng = RandomNumberGenerator::new();
166 for _ in 0..100 {
167 let n = rng.roll_dice(1, 20);
168 assert!(n > 0 && n < 21);
169 }
170 }
171
172 #[test]
173 fn random_slice_index_empty() {
174 let mut rng = RandomNumberGenerator::new();
175 let test_data: Vec<i32> = Vec::new();
176 assert!(rng.random_slice_index(&test_data).is_none());
177 }
178
179 #[test]
180 fn random_slice_index_valid() {
181 let mut rng = RandomNumberGenerator::new();
182 let test_data: Vec<i32> = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
183 for _ in 0..100 {
184 match rng.random_slice_index(&test_data) {
185 None => assert!(1 == 2),
186 Some(idx) => assert!(idx < test_data.len()),
187 }
188 }
189 }
190
191 #[test]
192 fn random_slice_entry_empty() {
193 let mut rng = RandomNumberGenerator::new();
194 let test_data: Vec<i32> = Vec::new();
195 assert!(rng.random_slice_entry(&test_data).is_none());
196 }
197
198 #[test]
199 fn random_slice_entry_valid() {
200 let mut rng = RandomNumberGenerator::new();
201 let test_data: Vec<i32> = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
202 for _ in 0..100 {
203 match rng.random_slice_entry(&test_data) {
204 None => assert!(1 == 2),
205 Some(e) => assert!(*e > 0 && *e < 11),
206 }
207 }
208 }
209
210 #[cfg(feature = "serde")]
211 #[test]
212 fn serialize_rng() {
213 use serde_crate::{Deserialize, Serialize};
214 let mut rng = RandomNumberGenerator::seeded(1000);
215 let serialized = serde_json::to_string(&rng).unwrap();
216 let n = rng.range(0, 100);
217 let mut deserialized: RandomNumberGenerator = serde_json::from_str(&serialized).unwrap();
218 let n2 = deserialized.range(0, 100);
219 assert!(n == n2);
220 }
221}