1use crate::random::{
2 PositionalRandom, Random, RandomSource, RandomSplitter, gaussian::MarsagliaPolarGaussian,
3 get_seed, name_hash::NameHash,
4};
5
6pub struct LegacyRandom {
9 seed: i64,
10 next_gaussian: Option<f64>,
11}
12
13#[derive(Clone)]
16pub struct LegacyRandomSplitter {
17 seed: i64,
18}
19
20impl LegacyRandom {
21 #[must_use]
24 pub const fn from_seed(seed: u64) -> Self {
25 Self {
26 seed: (seed as i64 ^ 0x0005_DEEC_E66D) & 0xFFFF_FFFF_FFFF,
27 next_gaussian: None,
28 }
29 }
30
31 #[must_use]
33 pub const fn get_seed(&self) -> i64 {
34 self.seed
35 }
36
37 pub const fn set_seed(&mut self, seed: i64) {
39 self.seed = (seed ^ 0x0005_DEEC_E66D) & 0xFFFF_FFFF_FFFF;
40 self.next_gaussian = None;
41 }
42
43 pub fn set_large_feature_seed(&mut self, seed: i64, chunk_x: i32, chunk_z: i32) {
45 self.set_seed(seed);
46 let x_mul = self.next_i64();
47 let z_mul = self.next_i64();
48 self.set_seed(
49 i64::from(chunk_x).wrapping_mul(x_mul) ^ i64::from(chunk_z).wrapping_mul(z_mul) ^ seed,
50 );
51 }
52
53 pub fn set_large_feature_with_salt(&mut self, seed: i64, x: i32, z: i32, salt: i32) {
55 self.set_seed(
56 i64::from(x)
57 .wrapping_mul(341_873_128_712)
58 .wrapping_add(i64::from(z).wrapping_mul(132_897_987_541))
59 .wrapping_add(seed)
60 .wrapping_add(i64::from(salt)),
61 );
62 }
63
64 const fn next(&mut self, bits: u64) -> i32 {
65 (self.next_random() >> (48 - bits)) as i32
66 }
67
68 const fn next_random(&mut self) -> i64 {
69 let l = self.seed;
70 let m = l.wrapping_mul(0x0005_DEEC_E66D).wrapping_add(0xB) & 0xFFFF_FFFF_FFFF;
71 self.seed = m;
72 m
73 }
74}
75
76impl MarsagliaPolarGaussian for LegacyRandom {
77 fn stored_next_gaussian(&self) -> Option<f64> {
78 self.next_gaussian
79 }
80
81 fn set_stored_next_gaussian(&mut self, value: Option<f64>) {
82 self.next_gaussian = value;
83 }
84}
85
86impl Random for LegacyRandom {
87 fn fork(&mut self) -> Self {
88 Self::from_seed(self.next_i64() as u64)
89 }
90
91 fn next_i32(&mut self) -> i32 {
92 self.next(32)
93 }
94
95 fn next_i32_bounded(&mut self, bound: i32) -> i32 {
96 if bound & bound.wrapping_sub(1) == 0 {
97 (i64::from(bound).wrapping_mul(i64::from(self.next(31))) >> 31) as i32
98 } else {
99 loop {
100 let i = self.next(31);
101 let j = i % bound;
102 if i.wrapping_sub(j).wrapping_add(bound.wrapping_sub(1)) >= 0 {
103 return j;
104 }
105 }
106 }
107 }
108
109 fn next_i64(&mut self) -> i64 {
110 let i = self.next_i32();
111 let j = self.next_i32();
112 (i64::from(i) << 32).wrapping_add(i64::from(j))
113 }
114
115 fn next_f32(&mut self) -> f32 {
116 self.next(24) as f32 * 5.960_464_5e-8_f32
117 }
118
119 fn next_f64(&mut self) -> f64 {
120 let combined = (i64::from(self.next(26)) << 27) + i64::from(self.next(27));
127 combined as f64 * (1.0 / (1_i64 << 53) as f64)
128 }
129
130 fn next_bool(&mut self) -> bool {
131 self.next(1) != 0
132 }
133
134 fn next_gaussian(&mut self) -> f64 {
135 self.calculate_gaussian()
136 }
137
138 fn next_positional(&mut self) -> RandomSplitter {
139 RandomSplitter::Legacy(LegacyRandomSplitter::new(self.next_i64()))
140 }
141}
142
143impl LegacyRandomSplitter {
144 #[must_use]
147 pub const fn new(seed: i64) -> Self {
148 Self { seed }
149 }
150}
151
152impl PositionalRandom for LegacyRandomSplitter {
153 fn at(&self, x: i32, y: i32, z: i32) -> RandomSource {
154 let seed = get_seed(x, y, z);
155 RandomSource::Legacy(LegacyRandom::from_seed((seed as u64) ^ (self.seed as u64)))
156 }
157
158 fn with_hash_of(&self, hash: &NameHash) -> RandomSource {
159 RandomSource::Legacy(LegacyRandom::from_seed(
160 (hash.java_hash as u64) ^ (self.seed as u64),
161 ))
162 }
163
164 fn with_seed(&self, seed: u64) -> RandomSource {
165 RandomSource::Legacy(LegacyRandom::from_seed(seed))
166 }
167}
168
169#[cfg(test)]
170mod test {
171 use crate::random::{PositionalRandom, Random, RandomSplitter, name_hash::NameHash};
172
173 use super::LegacyRandom;
174
175 #[test]
176 fn test_next_i32() {
177 let mut rand = LegacyRandom::from_seed(0);
178
179 let values = [
180 -1_155_484_576,
181 -723_955_400,
182 1_033_096_058,
183 -1_690_734_402,
184 -1_557_280_266,
185 1_327_362_106,
186 -1_930_858_313,
187 502_539_523,
188 -1_728_529_858,
189 -938_301_587,
190 ];
191
192 for value in values {
193 assert_eq!(rand.next_i32(), value);
194 }
195 }
196
197 #[test]
198 fn test_next_i32_bounded() {
199 let mut rand = LegacyRandom::from_seed(0);
200
201 let values = [0, 13, 4, 2, 5, 8, 11, 6, 9, 14];
202
203 for value in values {
204 assert_eq!(rand.next_i32_bounded(0xf), value);
205 }
206
207 let mut rand = LegacyRandom::from_seed(0);
208 for _ in 0..10 {
209 assert_eq!(rand.next_i32_bounded(1), 0);
210 }
211
212 let mut rand = LegacyRandom::from_seed(0);
213 let values = [1, 1, 0, 1, 1, 0, 1, 0, 1, 1];
214 for value in values {
215 assert_eq!(rand.next_i32_bounded(2), value);
216 }
217 }
218
219 #[test]
220 fn test_next_i32_between() {
221 let mut rand = LegacyRandom::from_seed(0);
222
223 let values = [1, 5, 2, 12, 12, 6, 12, 10, 4, 3];
224
225 for value in values {
226 assert_eq!(rand.next_i32_between(1, 12), value);
227 }
228 }
229
230 #[test]
231 fn test_next_i32_between_exclusive() {
232 let mut rand = LegacyRandom::from_seed(0);
233
234 let values = [1, 7, 9, 6, 7, 3, 3, 7, 3, 1];
235
236 for value in values {
237 assert_eq!(rand.next_i32_between_exclusive(1, 12), value);
238 }
239 }
240
241 #[test]
242 #[expect(clippy::float_cmp, reason = "exact match against vanilla test vectors")]
243 fn test_next_f64() {
244 let mut rand = LegacyRandom::from_seed(0);
245
246 let values = [
250 0.730_967_787_376_657,
251 0.240_536_415_671_485_87,
252 0.637_417_425_350_108_3,
253 0.550_437_005_117_633_9,
254 0.597_545_277_797_201_8,
255 0.333_218_399_476_649_8,
256 0.385_189_184_740_718_5,
257 0.984_841_540_199_809,
258 0.879_182_517_872_480_1,
259 0.941_249_179_482_114_4,
260 ];
261
262 for value in values {
263 assert_eq!(rand.next_f64(), value);
264 }
265 }
266
267 #[test]
268 #[expect(clippy::float_cmp, reason = "exact match against vanilla test vectors")]
269 fn test_next_f32() {
270 let mut rand = LegacyRandom::from_seed(0);
271
272 let values: [f32; 10] = [
273 0.730_967_76,
274 0.831_441,
275 0.240_536_39,
276 0.606_345_2,
277 0.637_417_4,
278 0.309_050_56,
279 0.550_437,
280 0.117_006_6,
281 0.597_545_27,
282 0.781_534_6,
283 ];
284
285 for value in values {
286 assert_eq!(rand.next_f32(), value);
287 }
288 }
289
290 #[test]
291 fn test_next_i64() {
292 let mut rand = LegacyRandom::from_seed(0);
293
294 let values: [i64; 10] = [
295 -4_962_768_465_676_381_896,
296 4_437_113_781_045_784_766,
297 -6_688_467_811_848_818_630,
298 -8_292_973_307_042_192_125,
299 -7_423_979_211_207_825_555,
300 6_146_794_652_083_548_235,
301 7_105_486_291_024_734_541,
302 -279_624_296_851_435_688,
303 -2_228_689_144_322_150_137,
304 -1_083_761_183_081_836_303,
305 ];
306
307 for value in values {
308 assert_eq!(rand.next_i64(), value);
309 }
310 }
311
312 #[test]
313 fn test_next_bool() {
314 let mut rand = LegacyRandom::from_seed(0);
315
316 let values = [
317 true, true, false, true, true, false, true, false, true, true,
318 ];
319
320 for value in values {
321 assert_eq!(rand.next_bool(), value);
322 }
323 }
324
325 #[test]
326 #[expect(clippy::float_cmp, reason = "exact match against vanilla test vectors")]
327 fn test_next_gaussian() {
328 let mut rand = LegacyRandom::from_seed(0);
329
330 let values = [
331 0.802_533_063_739_030_5,
332 -0.901_546_088_417_512_2,
333 2.080_920_790_428_163,
334 0.763_770_768_436_489_4,
335 0.984_574_532_882_512_8,
336 -1.683_412_258_767_342_8,
337 -0.027_290_262_907_887_285,
338 0.115_245_702_862_023_15,
339 -0.390_167_041_379_937_74,
340 -0.643_388_813_126_449,
341 ];
342
343 for value in values {
344 assert_eq!(rand.next_gaussian(), value);
345 }
346 }
347
348 #[test]
349 #[expect(clippy::float_cmp, reason = "exact match against vanilla test vectors")]
350 fn test_triangle() {
351 let mut rand = LegacyRandom::from_seed(0);
352
353 let values = [
354 124.521_568_585_258_56,
355 104.349_021_011_623_72,
356 113.216_343_916_027_6,
357 70.017_382_227_045_47,
358 96.896_666_919_518_28,
359 107.302_840_758_085_41,
360 106.168_176_758_131_44,
361 79.112_644_826_080_78,
362 73.967_216_139_270_62,
363 81.724_195_210_806_46,
364 ];
365
366 for value in values {
367 assert_eq!(rand.triangle(100_f64, 50_f64), value);
368 }
369 }
370
371 #[test]
372 fn test_fork() {
373 let mut original_rand = LegacyRandom::from_seed(0);
374 assert_eq!(original_rand.next_i64(), -4_962_768_465_676_381_896_i64);
375
376 let mut original_rand = LegacyRandom::from_seed(0);
377 {
378 let RandomSplitter::Legacy(splitter) = original_rand.next_positional() else {
379 unreachable!()
380 };
381 assert_eq!(splitter.seed, -4_962_768_465_676_381_896_i64);
382
383 let mut rand = splitter.with_hash_of(&NameHash::new("minecraft:offset"));
384 assert_eq!(rand.next_i32(), 103_436_829);
385 }
386
387 let mut original_rand = LegacyRandom::from_seed(0);
388 let mut new_rand = original_rand.fork();
389 {
390 let splitter = new_rand.next_positional();
391
392 let mut rand1 = splitter.with_hash_of(&NameHash::new("TEST STRING"));
393 assert_eq!(rand1.next_i32(), -1_170_413_697);
394
395 let mut rand2 = splitter.with_seed(10);
396 assert_eq!(rand2.next_i32(), -1_157_793_070);
397
398 let mut rand3 = splitter.at(1, 11, -111);
399 assert_eq!(rand3.next_i32(), -1_213_890_343);
400 }
401
402 assert_eq!(original_rand.next_i32(), 1_033_096_058);
403 assert_eq!(new_rand.next_i32(), -888_301_832);
404 }
405
406 #[test]
407 fn test_set_seed_matches_from_seed() {
408 let mut fresh = LegacyRandom::from_seed(12345);
409 let mut reseeded = LegacyRandom::from_seed(0);
410 reseeded.set_seed(12345);
411 for _ in 0..10 {
412 assert_eq!(fresh.next_i64(), reseeded.next_i64());
413 }
414 }
415
416 #[test]
417 fn test_set_large_feature_with_salt_trivial() {
418 let mut rng = LegacyRandom::from_seed(0);
419 rng.set_large_feature_with_salt(0, 0, 0, 10_387_312);
420 let mut expected = LegacyRandom::from_seed(0);
421 expected.set_seed(10_387_312);
422 for _ in 0..5 {
423 assert_eq!(rng.next_i32(), expected.next_i32());
424 }
425 }
426
427 #[test]
428 fn test_set_large_feature_with_salt() {
429 let mut rng = LegacyRandom::from_seed(0);
430 rng.set_large_feature_with_salt(123_456_789, 5, -3, 10_387_312);
431 let expected_seed: i64 =
432 5_i64 * 341_873_128_712 + (-3_i64) * 132_897_987_541 + 123_456_789 + 10_387_312;
433 let mut expected = LegacyRandom::from_seed(0);
434 expected.set_seed(expected_seed);
435 for _ in 0..5 {
436 assert_eq!(rng.next_i32(), expected.next_i32());
437 }
438 }
439
440 #[test]
441 fn test_set_large_feature_seed() {
442 let x_mul = -4_962_768_465_676_381_896_i64;
443 let z_mul = 4_437_113_781_045_784_766_i64;
444 let expected_seed = 3_i64.wrapping_mul(x_mul) ^ 5_i64.wrapping_mul(z_mul);
445
446 let mut rng = LegacyRandom::from_seed(0);
447 rng.set_large_feature_seed(0, 3, 5);
448 let mut expected = LegacyRandom::from_seed(0);
449 expected.set_seed(expected_seed);
450 for _ in 0..5 {
451 assert_eq!(rng.next_i32(), expected.next_i32());
452 }
453 }
454}