PkmnLib_rs/src/utils/random.rs

268 lines
7.8 KiB
Rust
Executable File

use rand::distributions::{Distribution, Uniform};
use rand::{Rng, SeedableRng};
use rand_pcg::Pcg32;
/// A random number generator.
#[derive(Clone)]
pub struct Random {
/// The seed of the random number generator.
seed: u128,
/// A float distribution.
distribution: Uniform<f64>,
/// The underlying RNG. PCG for fast, hard to predict random number generation.
random_gen: Pcg32,
}
impl Default for Random {
/// The default for the RNG uses the nanoseconds since epoch as seed.
fn default() -> Self {
let seed = chrono::Utc::now().timestamp_nanos() as u128;
Random::new(seed)
}
}
impl Random {
/// Creates a new RNG with a specific seed.
pub fn new(seed: u128) -> Self {
Random {
seed,
distribution: Uniform::from(0.0..1.0),
random_gen: Pcg32::from_seed(seed.to_be_bytes()),
}
}
/// The seed used for the RNG.
pub fn get_seed(&self) -> u128 {
self.seed
}
/// Get a random 32 bit integer between minimal and maximal 32 bit integer
pub fn get(&mut self) -> i32 {
self.random_gen.gen()
}
/// Get a random 32 bit signed integer between 0 and max. If max equals 0, always returns 0.
pub fn get_max(&mut self, max: i32) -> i32 {
if max <= 0 {
return 0;
}
Uniform::from(0..max).sample(&mut self.random_gen)
}
/// Get a random 32 bit signed integer between min and max. If max is equal or less than min,
/// always returns min.
pub fn get_between(&mut self, min: i32, max: i32) -> i32 {
if max <= min {
return min;
}
Uniform::from(min..max).sample(&mut self.random_gen)
}
/// Get a random 32 bit integer unsigned between 0 and maximal 32 bit unsigned int.
pub fn get_unsigned(&mut self) -> u32 {
self.random_gen.gen()
}
/// Get a random 32 bit signed integer between 0 and max. If max equals 0, always returns 0.
pub fn get_max_unsigned(&mut self, max: u32) -> u32 {
if max == 0 {
return 0;
}
Uniform::from(0..max).sample(&mut self.random_gen)
}
/// Get a random 32 bit unsigned integer between min and max. If max is equal or less than min,
/// always returns min.
pub fn get_between_unsigned(&mut self, min: u32, max: u32) -> u32 {
if max <= min {
return min;
}
Uniform::from(min..max).sample(&mut self.random_gen)
}
/// Gets a random 32 bit float between 0.0 and 1.0
pub fn get_float(&mut self) -> f32 {
self.get_double() as f32
}
/// Gets a random 64 bit float between 0.0 and 1.0
pub fn get_double(&mut self) -> f64 {
self.distribution.sample(&mut self.random_gen)
}
}
#[cfg(test)]
mod tests {
use std::hint::black_box;
use test::Bencher;
use crate::utils::random::Random;
extern crate test;
#[test]
#[cfg_attr(miri, ignore)]
fn create_random() {
let _default = Random::default();
let empty = Random::new(100);
assert_eq!(empty.get_seed(), 100);
}
#[test]
fn get_random_with_seed() {
let mut v = Random::new(10);
assert_eq!(v.get(), 1755576946);
assert_eq!(v.get(), 1254514019);
assert_eq!(v.get(), 1735834837);
assert_eq!(v.get(), 51079449);
assert_eq!(v.get(), 506997516);
assert_eq!(v.get(), -173527621);
assert_eq!(v.get(), 683138464);
assert_eq!(v.get(), 580236580);
}
#[test]
fn get_random_unsigned_with_seed() {
let mut v = Random::new(10);
assert_eq!(v.get_unsigned(), 1755576946);
assert_eq!(v.get_unsigned(), 1254514019);
assert_eq!(v.get_unsigned(), 1735834837);
assert_eq!(v.get_unsigned(), 51079449);
assert_eq!(v.get_unsigned(), 506997516);
assert_eq!(v.get_unsigned(), 4121439675);
assert_eq!(v.get_unsigned(), 683138464);
assert_eq!(v.get_unsigned(), 580236580);
}
#[test]
fn get_random_with_limit_with_seed() {
let mut v = Random::new(10);
assert_eq!(v.get_max(10), 4);
assert_eq!(v.get_max(10), 2);
assert_eq!(v.get_max(10), 4);
assert_eq!(v.get_max(10), 0);
assert_eq!(v.get_max(10), 1);
assert_eq!(v.get_max(10), 9);
assert_eq!(v.get_max(10), 1);
assert_eq!(v.get_max(10), 1);
assert_eq!(v.get_max(2), 0);
assert_eq!(v.get_max(2), 1);
assert_eq!(v.get_max(2), 1);
assert_eq!(v.get_max(2), 1);
assert_eq!(v.get_max(2), 0);
assert_eq!(v.get_max(2), 1);
assert_eq!(v.get_max(2), 0);
assert_eq!(v.get_max(2), 0);
assert_eq!(v.get_max(2), 1);
}
#[test]
fn get_random_unsigned_with_limit_with_seed() {
let mut v = Random::new(10);
assert_eq!(v.get_max_unsigned(4121439675), 1684647164);
}
#[test]
fn get_random_with_limit_with_range() {
let mut v = Random::new(10);
assert_eq!(v.get_between(10, 30), 18);
assert_eq!(v.get_between(10, 30), 15);
assert_eq!(v.get_between(10, 30), 18);
assert_eq!(v.get_between(10, 30), 10);
assert_eq!(v.get_between(10, 30), 12);
assert_eq!(v.get_between(10, 30), 29);
assert_eq!(v.get_between(10, 30), 13);
assert_eq!(v.get_between(10, 30), 12);
}
#[test]
fn get_random_unsigned_with_limit_with_range() {
let mut v = Random::new(10);
assert_eq!(v.get_between_unsigned(4121439675, 4121439678), 4121439676);
}
#[test]
fn get_random_float() {
let mut v = Random::new(10);
assert_eq!(v.get_float(), 0.2920893);
}
#[test]
fn get_random_double() {
let mut v = Random::new(10);
assert_eq!(v.get_double(), 0.2920893066117427);
}
#[test]
fn get_random_clone_then_continue() {
let mut v = Random::new(10);
assert_eq!(v.get_unsigned(), 1755576946);
let mut clone = v.clone();
assert_eq!(clone.get_unsigned(), 1254514019);
assert_eq!(v.get_unsigned(), 1254514019);
}
#[test]
#[cfg_attr(miri, ignore)]
fn check_random_distribution() {
let mut v = Random::new(10);
const AMOUNT: usize = 100_000;
let mut arr: [i32; AMOUNT] = [0; AMOUNT];
for i in 0..AMOUNT {
arr[i] = v.get_between(0, 2);
}
let mut num_zeros = 0;
let mut num_ones = 0;
for v in arr.iter() {
if *v == 0 {
num_zeros += 1;
} else if *v == 1 {
num_ones += 1;
}
}
let div = num_zeros as f32 / num_ones as f32;
assert_approx_eq::assert_approx_eq!(div, 1.0, 0.01);
}
#[test]
#[cfg_attr(miri, ignore)]
fn check_random_distribution_0_to_3() {
let mut v = Random::new(10);
const AMOUNT: usize = 100_000;
let mut arr: [i32; AMOUNT] = [0; AMOUNT];
for i in 0..AMOUNT {
arr[i] = v.get_between(0, 3);
}
let mut num_zeros = 0;
let mut num_ones = 0;
let mut num_twos = 0;
for v in arr.iter() {
if *v == 0 {
num_zeros += 1;
} else if *v == 1 {
num_ones += 1;
} else if *v == 2 {
num_twos += 1;
}
}
assert_approx_eq::assert_approx_eq!(num_zeros as f32 / num_ones as f32, 1.0, 0.01);
assert_approx_eq::assert_approx_eq!(num_zeros as f32 / num_twos as f32, 1.0, 0.01);
assert_approx_eq::assert_approx_eq!(num_ones as f32 / num_twos as f32, 1.0, 0.01);
}
#[bench]
#[cfg_attr(miri, ignore)]
fn bench_1000_random_ints_between(b: &mut Bencher) {
b.iter(|| {
let mut random = Random::new(10);
for _ in 0..1000 {
black_box(random.get_between(0, 100));
}
});
}
}