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}