use agb::input::{Button, ButtonController};
use agb::rng::RandomNumberGenerator;
use core::ops::{Range, RangeInclusive};
#[derive(Debug, Clone)]
pub struct SeedGen {
pub seed: [u32; 4],
}
impl Default for SeedGen {
fn default() -> SeedGen {
Self::new([0x15f1c1; 4])
}
}
impl SeedGen {
pub const fn new(seed: [u32; 4]) -> Self {
Self { seed }
}
pub fn create_rng(&self) -> RandomNumberGenerator {
RandomNumberGenerator::new_with_seed(self.seed)
}
pub fn update(&mut self, button_controller: &ButtonController) {
self.seed[1] = self.seed[1].rotate_left(1);
self.seed[1] |= button_controller.is_pressed(Button::Left) as u32;
self.seed[1] |= (button_controller.is_pressed(Button::Right) as u32) << 1;
self.seed[1] |= (button_controller.is_pressed(Button::Up) as u32) << 2;
self.seed[1] |= (button_controller.is_pressed(Button::Down) as u32) << 3;
self.seed[1] |= (button_controller.is_pressed(Button::A) as u32) << 4;
self.seed[1] |= (button_controller.is_pressed(Button::B) as u32) << 5;
self.seed[1] |= (button_controller.is_pressed(Button::L) as u32) << 6;
self.seed[1] |= (button_controller.is_pressed(Button::R) as u32) << 7;
self.seed[0] = self.seed[0].wrapping_add(1);
self.seed[2] ^= self.seed[0].wrapping_mul(0x9e3779b9);
self.seed[3] =
self.seed[3].wrapping_add(button_controller.just_pressed_vector::<i32>().x as u32);
self.seed[3] = self.seed[3].rotate_right(1);
}
}
#[inline]
pub fn next_in(rng: &mut RandomNumberGenerator, max: usize) -> usize {
debug_assert!(max > 0, "max must be greater than 0");
mul_shift(rng.next_i32() as u32, max as u32) as usize
}
#[inline]
pub fn next_bool(rng: &mut RandomNumberGenerator) -> bool {
rng.next_i32() < 0
}
#[inline]
pub fn one_in_pow2(rng: &mut RandomNumberGenerator, bits: u8) -> bool {
debug_assert!(bits <= 32, "bits must be at most 32");
if bits == 0 {
return true;
}
(rng.next_i32() as u32) >> (32 - bits as u32) == 0
}
#[inline]
fn mul_shift(random_val: u32, span: u32) -> u32 {
((random_val as u64 * span as u64) >> 32) as u32
}
#[inline]
fn random_offset(rng: &mut RandomNumberGenerator, span: u64) -> u32 {
let random_val = rng.next_i32() as u32;
if span > u32::MAX as u64 {
random_val
} else {
mul_shift(random_val, span as u32)
}
}
pub trait RandomRange {
type Output;
fn random(self, rng: &mut RandomNumberGenerator) -> Self::Output;
}
macro_rules! impl_random_range {
($($t:ty),*) => {$(
impl RandomRange for Range<$t> {
type Output = $t;
#[inline]
fn random(self, rng: &mut RandomNumberGenerator) -> $t {
debug_assert!(self.start < self.end, "range must not be empty");
if self.start >= self.end {
return self.start;
}
let span = (self.end as i64 - self.start as i64) as u64;
self.start.wrapping_add(random_offset(rng, span) as $t)
}
}
impl RandomRange for RangeInclusive<$t> {
type Output = $t;
#[inline]
fn random(self, rng: &mut RandomNumberGenerator) -> $t {
let (start, end) = (*self.start(), *self.end());
debug_assert!(start <= end, "range must not be empty");
if start >= end {
return start;
}
let span = (end as i64 - start as i64) as u64 + 1;
start.wrapping_add(random_offset(rng, span) as $t)
}
}
)*};
}
impl_random_range!(u8, u16, u32, i8, i16, i32);
#[inline]
pub fn next_range<R: RandomRange>(rng: &mut RandomNumberGenerator, range: R) -> R::Output {
range.random(rng)
}
pub fn shuffle<T>(rng: &mut RandomNumberGenerator, items: &mut [T]) {
for i in (1..items.len()).rev() {
let j = next_in(rng, i + 1);
items.swap(i, j);
}
}
#[inline]
pub fn random_get<'a, T>(rng: &mut RandomNumberGenerator, items: &'a [T]) -> Option<&'a T> {
if items.is_empty() {
None
} else {
Some(&items[next_in(rng, items.len())])
}
}
#[inline]
pub fn random_get_mut<'a, T>(
rng: &mut RandomNumberGenerator,
items: &'a mut [T],
) -> Option<&'a mut T> {
if items.is_empty() {
None
} else {
let idx = next_in(rng, items.len());
Some(&mut items[idx])
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test_case]
fn next_range_stays_in_range(_gba: &mut agb::Gba) {
let mut rng = RandomNumberGenerator::new_with_seed([25, 26, 27, 28]);
for _ in 0..1000 {
let a = next_range(&mut rng, 3u8..7);
assert!((3..7).contains(&a), "{a} out of range");
let b = next_range(&mut rng, 3u8..=7);
assert!((3..=7).contains(&b), "{b} out of range");
let c = next_range(&mut rng, 1000u16..2000);
assert!((1000..2000).contains(&c), "{c} out of range");
let d = next_range(&mut rng, 1000u16..=2000);
assert!((1000..=2000).contains(&d), "{d} out of range");
let e = next_range(&mut rng, 100_000u32..200_000);
assert!((100_000..200_000).contains(&e), "{e} out of range");
let f = next_range(&mut rng, 100_000u32..=200_000);
assert!((100_000..=200_000).contains(&f), "{f} out of range");
}
}
#[test_case]
fn next_range_handles_extremes(_gba: &mut agb::Gba) {
let mut rng = RandomNumberGenerator::new_with_seed([29, 30, 31, 32]);
assert_eq!(next_range(&mut rng, 7u8..=7), 7);
assert_eq!(next_range(&mut rng, 7u16..8), 7);
for _ in 0..100 {
next_range(&mut rng, 0u8..=u8::MAX);
next_range(&mut rng, 0u16..=u16::MAX);
next_range(&mut rng, 0u32..=u32::MAX);
assert_eq!(next_range(&mut rng, u32::MAX..=u32::MAX), u32::MAX);
let v = next_range(&mut rng, u32::MAX - 1..u32::MAX);
assert_eq!(v, u32::MAX - 1);
}
}
#[test_case]
fn shuffle_keeps_all_elements(_gba: &mut agb::Gba) {
let mut rng = RandomNumberGenerator::new_with_seed([9, 10, 11, 12]);
let mut items = [1u8, 2, 3, 4, 5, 6, 7, 8];
shuffle(&mut rng, &mut items);
for expected in 1u8..=8 {
assert!(
items.contains(&expected),
"{expected} missing after shuffle"
);
}
}
#[test_case]
fn shuffle_handles_small_slices(_gba: &mut agb::Gba) {
let mut rng = RandomNumberGenerator::new_with_seed([13, 14, 15, 16]);
let mut empty: [u8; 0] = [];
shuffle(&mut rng, &mut empty);
let mut single = [42u8];
shuffle(&mut rng, &mut single);
assert_eq!(single, [42]);
}
#[test_case]
fn choose_returns_contained_element(_gba: &mut agb::Gba) {
let mut rng = RandomNumberGenerator::new_with_seed([17, 18, 19, 20]);
let items = [10u8, 20, 30, 40, 50];
for _ in 0..100 {
let v = *random_get(&mut rng, &items).unwrap();
assert!(items.contains(&v), "{v} not in source slice");
}
}
#[test_case]
fn choose_handles_empty_and_mut(_gba: &mut agb::Gba) {
let mut rng = RandomNumberGenerator::new_with_seed([21, 22, 23, 24]);
let empty: [u8; 0] = [];
assert!(random_get(&mut rng, &empty).is_none());
let mut items = [1u8, 1, 1];
if let Some(v) = random_get_mut(&mut rng, &mut items) {
*v = 9;
}
assert_eq!(items.iter().filter(|&&v| v == 9).count(), 1);
}
#[test_case]
fn signed_ranges_stay_in_range(_gba: &mut agb::Gba) {
let mut rng = RandomNumberGenerator::new_with_seed([33, 34, 35, 36]);
let mut seen_neg = false;
for _ in 0..1000 {
let a = next_range(&mut rng, -2i8..=2);
assert!((-2..=2).contains(&a), "{a} out of range");
seen_neg |= a < 0;
let b = next_range(&mut rng, -100i16..50);
assert!((-100..50).contains(&b), "{b} out of range");
let c = next_range(&mut rng, -1_000_000i32..=1_000_000);
assert!((-1_000_000..=1_000_000).contains(&c), "{c} out of range");
next_range(&mut rng, i8::MIN..=i8::MAX);
next_range(&mut rng, i32::MIN..=i32::MAX);
next_range(&mut rng, i32::MIN..i32::MAX);
}
assert!(seen_neg, "never produced a negative value");
assert_eq!(next_range(&mut rng, -5i32..=-5), -5);
assert_eq!(next_range(&mut rng, i32::MIN..i32::MIN + 1), i32::MIN);
}
#[test_case]
fn bool_and_pow2(_gba: &mut agb::Gba) {
let mut rng = RandomNumberGenerator::new_with_seed([37, 38, 39, 40]);
let (mut t, mut f) = (0, 0);
for _ in 0..1000 {
if next_bool(&mut rng) { t += 1 } else { f += 1 }
}
assert!(t > 300 && f > 300, "biased: {t} true / {f} false");
for _ in 0..100 {
assert!(one_in_pow2(&mut rng, 0));
}
let hits = (0..4000).filter(|_| one_in_pow2(&mut rng, 2)).count();
assert!((700..1300).contains(&hits), "1/4 chance hit {hits} of 4000");
let rare = (0..1000).filter(|_| one_in_pow2(&mut rng, 32)).count();
assert!(rare <= 1, "1/2^32 chance hit {rare} times");
}
}