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