Skip to main content

manabrew_engine/
game_rng.rs

1//! Pluggable RNG for game effects (shuffles, coin flips, dice rolls).
2//!
3//! By default, effects use the system's thread-local RNG. For parity testing,
4//! a deterministic RNG (e.g. JavaRandom) can be injected to match Java Forge's
5//! `MyRandom` consumption order exactly.
6//!
7//! # WASM Compatibility
8//!
9//! The `rand` crate 0.8+ supports WASM via `getrandom`. For browser WASM,
10//! ensure the WASM entry point crate (wasm) includes:
11//! ```toml
12//! getrandom = { version = "0.2", features = ["js"] }
13//! ```
14//! This enables `thread_rng()` to work in browser environments.
15
16use crate::ids::CardId;
17
18/// Trait for game-level randomness, used by effect resolvers.
19///
20/// This abstraction lets parity tests inject a Java-compatible RNG
21/// that matches `java.util.Random` and `Collections.shuffle()` exactly,
22/// while normal gameplay uses the default thread-local RNG.
23pub trait GameRng {
24    /// Shuffle a slice of CardIds in-place.
25    /// Must match `java.util.Collections.shuffle(list, rng)` for parity.
26    fn shuffle_cards(&mut self, cards: &mut [CardId]);
27
28    /// Return a random integer in `[0, bound)`.
29    /// Must match `java.util.Random.nextInt(bound)` for parity.
30    fn next_int(&mut self, bound: i32) -> i32;
31
32    /// Debug: return the total number of RNG calls made so far (if tracked).
33    fn call_count(&self) -> u64 {
34        0
35    }
36}
37
38/// Default RNG using `rand::thread_rng()` — non-deterministic, for normal gameplay.
39pub struct ThreadRngAdapter;
40
41impl GameRng for ThreadRngAdapter {
42    fn shuffle_cards(&mut self, cards: &mut [CardId]) {
43        use rand::seq::SliceRandom;
44        cards.shuffle(&mut rand::thread_rng());
45    }
46
47    fn next_int(&mut self, bound: i32) -> i32 {
48        use rand::Rng;
49        rand::thread_rng().gen_range(0..bound)
50    }
51}