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