agb_eb_ext 0.25.0

AGB Extension methods
use agb::input::{Button, ButtonController};
use agb::rng::RandomNumberGenerator;

/// RNG seed generator
///
/// Uses input and maths to generate seeds
///
/// # Usage
///
/// Call [`update`](SeedGen::update) up to once per loop/frame
/// then call [`create_rng`](SeedGen::create_rng) to create a [`RandomNumberGenerator`] with that seed
pub struct SeedGen {
    pub seed: [u32; 4],
}

impl Default for SeedGen {
    fn default() -> SeedGen {
        Self {
            seed: [0x15f1c1; 4],
        }
    }
}

impl SeedGen {
    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_u16(rng: &mut RandomNumberGenerator) -> u16 {
    rng.next_i32() as u16
}

///inclusive
#[inline]
pub fn next_u16_in(rng: &mut RandomNumberGenerator, min: u16, max: u16) -> u16 {
    debug_assert!(min <= max, "min must not be greater than max");

    let span = (max as u32).wrapping_sub(min as u32).wrapping_add(1);
    let random_val = next_u16(rng) as u32;

    let scaled = (random_val * span) >> 16;

    (scaled as u16) + min
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test_case]
    fn next_u16_in_stays_in_range(_gba: &mut agb::Gba) {
        let mut rng = RandomNumberGenerator::new_with_seed([1, 2, 3, 4]);
        for _ in 0..1000 {
            let v = next_u16_in(&mut rng, 5, 10);
            assert!((5..=10).contains(&v), "{v} out of range");
        }
    }

    #[test_case]
    fn next_u16_in_handles_extremes(_gba: &mut agb::Gba) {
        let mut rng = RandomNumberGenerator::new_with_seed([5, 6, 7, 8]);
        assert_eq!(next_u16_in(&mut rng, 7, 7), 7);
        for _ in 0..100 {
            next_u16_in(&mut rng, 0, u16::MAX);
            next_u16_in(&mut rng, u16::MAX - 1, u16::MAX);
        }
    }
}