mirror of
https://github.com/italicsjenga/agb.git
synced 2025-01-12 01:51:34 +11:00
Add some tests for the rng
This commit is contained in:
parent
3c52f6940b
commit
a57043604d
|
@ -831,7 +831,7 @@ mod test {
|
||||||
use core::cell::RefCell;
|
use core::cell::RefCell;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{rng, Gba};
|
use crate::{rng::RandomNumberGenerator, Gba};
|
||||||
|
|
||||||
#[test_case]
|
#[test_case]
|
||||||
fn can_store_and_retrieve_8_elements(_gba: &mut Gba) {
|
fn can_store_and_retrieve_8_elements(_gba: &mut Gba) {
|
||||||
|
@ -956,13 +956,14 @@ mod test {
|
||||||
#[test_case]
|
#[test_case]
|
||||||
fn extreme_case(_gba: &mut Gba) {
|
fn extreme_case(_gba: &mut Gba) {
|
||||||
let mut map = HashMap::new();
|
let mut map = HashMap::new();
|
||||||
|
let mut rng = RandomNumberGenerator::new();
|
||||||
|
|
||||||
let mut answers: [Option<i32>; 128] = [None; 128];
|
let mut answers: [Option<i32>; 128] = [None; 128];
|
||||||
|
|
||||||
for _ in 0..5_000 {
|
for _ in 0..5_000 {
|
||||||
let command = rng::next().rem_euclid(2);
|
let command = rng.next().rem_euclid(2);
|
||||||
let key = rng::next().rem_euclid(answers.len() as i32);
|
let key = rng.next().rem_euclid(answers.len() as i32);
|
||||||
let value = rng::next();
|
let value = rng.next();
|
||||||
|
|
||||||
match command {
|
match command {
|
||||||
0 => {
|
0 => {
|
||||||
|
|
|
@ -4,21 +4,34 @@ use bare_metal::Mutex;
|
||||||
|
|
||||||
use crate::interrupt::free;
|
use crate::interrupt::free;
|
||||||
|
|
||||||
|
/// A fast pseudo-random number generator. Note that the output of the
|
||||||
|
/// random number generator for a given seed is guaranteed stable
|
||||||
|
/// between minor releases, however could change in a major release.
|
||||||
pub struct RandomNumberGenerator {
|
pub struct RandomNumberGenerator {
|
||||||
state: [u32; 4],
|
state: [u32; 4],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RandomNumberGenerator {
|
impl RandomNumberGenerator {
|
||||||
|
/// Create a new random number generator with a fixed seed
|
||||||
|
///
|
||||||
|
/// Note that this seed is guaranteed to be the same between minor releases.
|
||||||
pub const fn new() -> Self {
|
pub const fn new() -> Self {
|
||||||
Self {
|
Self::new_with_seed([1014776995, 476057059, 3301633994, 706340607])
|
||||||
state: [1014776995, 476057059, 3301633994, 706340607],
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Produces a random number generator with the given initial state / seed.
|
||||||
|
/// None of the values can be 0.
|
||||||
pub const fn new_with_seed(seed: [u32; 4]) -> Self {
|
pub const fn new_with_seed(seed: [u32; 4]) -> Self {
|
||||||
|
// this can't be in a loop because const
|
||||||
|
assert!(seed[0] != 0, "seed must not be 0");
|
||||||
|
assert!(seed[1] != 0, "seed must not be 0");
|
||||||
|
assert!(seed[2] != 0, "seed must not be 0");
|
||||||
|
assert!(seed[3] != 0, "seed must not be 0");
|
||||||
|
|
||||||
Self { state: seed }
|
Self { state: seed }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the next value for the random number generator
|
||||||
pub fn next(&mut self) -> i32 {
|
pub fn next(&mut self) -> i32 {
|
||||||
let result = (self.state[0].wrapping_add(self.state[3]))
|
let result = (self.state[0].wrapping_add(self.state[3]))
|
||||||
.rotate_left(7)
|
.rotate_left(7)
|
||||||
|
@ -40,6 +53,50 @@ impl RandomNumberGenerator {
|
||||||
static GLOBAL_RNG: Mutex<RefCell<RandomNumberGenerator>> =
|
static GLOBAL_RNG: Mutex<RefCell<RandomNumberGenerator>> =
|
||||||
Mutex::new(RefCell::new(RandomNumberGenerator::new()));
|
Mutex::new(RefCell::new(RandomNumberGenerator::new()));
|
||||||
|
|
||||||
|
/// Using a global random number generator, provides the next random number
|
||||||
pub fn next() -> i32 {
|
pub fn next() -> i32 {
|
||||||
free(|cs| GLOBAL_RNG.borrow(*cs).borrow_mut().next())
|
free(|cs| GLOBAL_RNG.borrow(*cs).borrow_mut().next())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
use crate::Gba;
|
||||||
|
|
||||||
|
#[test_case]
|
||||||
|
fn should_be_reasonably_distributed(_gba: &mut Gba) {
|
||||||
|
let mut values: [u32; 16] = Default::default();
|
||||||
|
|
||||||
|
let mut rng = RandomNumberGenerator::new();
|
||||||
|
for _ in 0..500 {
|
||||||
|
values[(rng.next().rem_euclid(16)) as usize] += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i, &value) in values.iter().enumerate() {
|
||||||
|
assert!(
|
||||||
|
value >= 500 / 10 / 3,
|
||||||
|
"{} came up less than expected {}",
|
||||||
|
i,
|
||||||
|
value
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test_case]
|
||||||
|
fn global_rng_should_be_reasonably_distributed(_gba: &mut Gba) {
|
||||||
|
let mut values: [u32; 16] = Default::default();
|
||||||
|
|
||||||
|
for _ in 0..500 {
|
||||||
|
values[super::next().rem_euclid(16) as usize] += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i, &value) in values.iter().enumerate() {
|
||||||
|
assert!(
|
||||||
|
value >= 500 / 10 / 3,
|
||||||
|
"{} came up less than expected {}",
|
||||||
|
i,
|
||||||
|
value
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue