#[cfg(feature = "parsing")]
use crate::prelude::{parse_dice_string, DiceParseError, DiceType};
use rand::{Rng, RngCore, SeedableRng};
use rand_xorshift::XorShiftRng;
pub struct RandomNumberGenerator {
rng: XorShiftRng,
}
impl RandomNumberGenerator {
#[allow(clippy::new_without_default)] pub fn new() -> RandomNumberGenerator {
let rng: XorShiftRng = SeedableRng::from_entropy();
RandomNumberGenerator { rng }
}
pub fn seeded(seed: u64) -> RandomNumberGenerator {
let rng: XorShiftRng = SeedableRng::seed_from_u64(seed);
RandomNumberGenerator { rng }
}
pub fn rand<T>(&mut self) -> T
where
rand::distributions::Standard: rand::distributions::Distribution<T>,
{
self.rng.gen::<T>()
}
pub fn range<T>(&mut self, min: T, max: T) -> T
where
T: rand::distributions::uniform::SampleUniform,
{
self.rng.gen_range(min, max)
}
pub fn roll_dice(&mut self, n: i32, die_type: i32) -> i32 {
(0..n).map(|_| self.range(1, die_type + 1)).sum()
}
pub fn next_u64(&mut self) -> u64 {
self.rng.next_u64()
}
#[cfg(feature = "parsing")]
pub fn roll(&mut self, dice: DiceType) -> i32 {
self.roll_dice(dice.n_dice, dice.die_type) + dice.bonus
}
#[cfg(feature = "parsing")]
pub fn roll_str<S: ToString>(&mut self, dice: S) -> Result<i32, DiceParseError> {
match parse_dice_string(&dice.to_string()) {
Ok(dt) => Ok(self.roll(dt)),
Err(e) => Err(e),
}
}
pub fn random_slice_index<T>(&mut self, slice: &[T]) -> Option<usize> {
if slice.is_empty() {
None
} else {
let sz = slice.len();
if sz == 1 {
Some(0)
} else {
Some(self.roll_dice(1, sz as i32) as usize - 1)
}
}
}
pub fn random_slice_entry<'a, T>(&mut self, slice: &'a [T]) -> Option<&'a T> {
if slice.is_empty() {
None
} else {
let sz = slice.len();
if sz == 1 {
Some(&slice[0])
} else {
Some(&slice[self.roll_dice(1, sz as i32) as usize - 1])
}
}
}
}
#[cfg(test)]
mod tests {
use crate::prelude::RandomNumberGenerator;
#[test]
fn roll_str_1d6() {
let mut rng = RandomNumberGenerator::new();
assert!(rng.roll_str("1d6").is_ok());
}
#[test]
fn roll_str_3d6plus1() {
let mut rng = RandomNumberGenerator::new();
assert!(rng.roll_str("3d6+1").is_ok());
}
#[test]
fn roll_str_3d20minus1() {
let mut rng = RandomNumberGenerator::new();
assert!(rng.roll_str("3d20-1").is_ok());
}
#[test]
fn roll_str_error() {
let mut rng = RandomNumberGenerator::new();
assert!(rng.roll_str("blah").is_err());
}
#[test]
fn test_roll_range() {
let mut rng = RandomNumberGenerator::new();
for _ in 0..100 {
let n = rng.roll_dice(1, 20);
assert!(n > 0 && n < 21);
}
}
#[test]
fn random_slice_index_empty() {
let mut rng = RandomNumberGenerator::new();
let test_data: Vec<i32> = Vec::new();
assert!(rng.random_slice_index(&test_data).is_none());
}
#[test]
fn random_slice_index_valid() {
let mut rng = RandomNumberGenerator::new();
let test_data: Vec<i32> = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
for _ in 0..100 {
match rng.random_slice_index(&test_data) {
None => assert!(1 == 2),
Some(idx) => assert!(idx < test_data.len()),
}
}
}
#[test]
fn random_slice_entry_empty() {
let mut rng = RandomNumberGenerator::new();
let test_data: Vec<i32> = Vec::new();
assert!(rng.random_slice_entry(&test_data).is_none());
}
#[test]
fn random_slice_entry_valid() {
let mut rng = RandomNumberGenerator::new();
let test_data: Vec<i32> = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
for _ in 0..100 {
match rng.random_slice_entry(&test_data) {
None => assert!(1 == 2),
Some(e) => assert!(*e > 0 && *e < 11),
}
}
}
}