Skip to main content

steel_utils/random/
mod.rs

1//! This module contains utilities for random number generation.
2use enum_dispatch::enum_dispatch;
3
4use crate::random::{
5    legacy_random::{LegacyRandom, LegacyRandomSplitter},
6    name_hash::NameHash,
7    xoroshiro::{Xoroshiro, XoroshiroSplitter},
8};
9
10/// This module contains the gaussian random number generator.
11pub mod gaussian;
12/// This module contains the legacy random number generator implementation.
13pub mod legacy_random;
14/// Precomputed name hashes for positional random seeding.
15pub mod name_hash;
16/// Weighted random selection helpers.
17pub mod weighted_random;
18/// This module contains vanilla's feature-decoration `WorldgenRandom` wrapper.
19pub mod worldgen_random;
20/// This module contains the xoroshiro random number generator.
21pub mod xoroshiro;
22
23/// A trait for random number generators.
24#[enum_dispatch]
25#[expect(missing_docs, reason = "method names are self-explanatory")]
26pub trait Random {
27    #[must_use]
28    fn fork(&mut self) -> Self;
29
30    fn next_i32(&mut self) -> i32;
31
32    fn next_i32_bounded(&mut self, bound: i32) -> i32;
33
34    fn next_i32_between(&mut self, min: i32, max: i32) -> i32 {
35        self.next_i32_bounded(max - min + 1) + min
36    }
37
38    fn next_i32_between_exclusive(&mut self, min: i32, max: i32) -> i32 {
39        min + self.next_i32_bounded(max - min)
40    }
41
42    fn next_i64(&mut self) -> i64;
43
44    fn next_f32(&mut self) -> f32;
45
46    fn next_f64(&mut self) -> f64;
47
48    fn next_bool(&mut self) -> bool;
49
50    fn next_gaussian(&mut self) -> f64;
51
52    fn triangle(&mut self, min: f64, max: f64) -> f64 {
53        min + max * (self.next_f64() - self.next_f64())
54    }
55
56    fn triangle_f32(&mut self, min: f32, max: f32) -> f32 {
57        min + max * (self.next_f32() - self.next_f32())
58    }
59
60    fn next_positional(&mut self) -> RandomSplitter;
61
62    fn consume_count(&mut self, count: i32) {
63        for _ in 0..count {
64            self.next_i32();
65        }
66    }
67
68    /// Vanilla `Util.shuffle`: one descending bounded draw per swapped position.
69    fn shuffle<T>(&mut self, items: &mut [T]) {
70        let Ok(size) = i32::try_from(items.len()) else {
71            panic!("shuffle length {} exceeds Java's int range", items.len());
72        };
73        for remaining in (2..=size).rev() {
74            let swap_to = self.next_i32_bounded(remaining) as usize;
75            items.swap(remaining as usize - 1, swap_to);
76        }
77    }
78}
79
80/// A trait for positional random number generators.
81#[enum_dispatch]
82#[expect(missing_docs, reason = "method names are self-explanatory")]
83pub trait PositionalRandom {
84    fn at(&self, x: i32, y: i32, z: i32) -> RandomSource;
85
86    fn with_hash_of(&self, hash: &NameHash) -> RandomSource;
87
88    fn with_seed(&self, seed: u64) -> RandomSource;
89}
90
91/// A source of random numbers.
92#[enum_dispatch(Random)]
93pub enum RandomSource {
94    /// A xoroshiro random number generator.
95    Xoroshiro(Xoroshiro),
96    /// A legacy Minecraft random number generator.
97    Legacy(LegacyRandom),
98}
99
100/// A random number generator that can be split.
101#[derive(Clone)]
102#[enum_dispatch(PositionalRandom)]
103pub enum RandomSplitter {
104    /// A xoroshiro random number generator.
105    Xoroshiro(XoroshiroSplitter),
106    /// A legacy Minecraft random number generator splitter.
107    Legacy(LegacyRandomSplitter),
108}
109
110/// Gets a seed from a position.
111#[must_use]
112pub fn get_seed(x: i32, y: i32, z: i32) -> i64 {
113    let l = i64::from(x.wrapping_mul(3_129_871))
114        ^ (i64::from(z).wrapping_mul(116_129_781_i64))
115        ^ i64::from(y);
116    let l = l
117        .wrapping_mul(l)
118        .wrapping_mul(42_317_861_i64)
119        .wrapping_add(l.wrapping_mul(11));
120    l >> 16
121}