mirror of
https://github.com/Pumpkin-MC/Pumpkin.git
synced 2026-08-30 20:14:23 +00:00
Merge pull request #71 from kralverde/legacy_random
implement legacy_random and java string hash
This commit is contained in:
326
pumpkin-core/src/random/legacy_rand.rs
Normal file
326
pumpkin-core/src/random/legacy_rand.rs
Normal file
@@ -0,0 +1,326 @@
|
||||
use super::{
|
||||
gaussian::GaussianGenerator, hash_block_pos, java_string_hash, Random, RandomSplitter,
|
||||
};
|
||||
|
||||
struct LegacyRand {
|
||||
seed: u64,
|
||||
internal_next_gaussian: f64,
|
||||
internal_has_next_gaussian: bool,
|
||||
}
|
||||
|
||||
impl LegacyRand {
|
||||
fn next_random(&mut self) -> u64 {
|
||||
let l = self.seed;
|
||||
let m = l.wrapping_mul(0x5DEECE66D).wrapping_add(11) & 0xFFFFFFFFFFFF;
|
||||
self.seed = m;
|
||||
m
|
||||
}
|
||||
}
|
||||
|
||||
impl GaussianGenerator for LegacyRand {
|
||||
fn has_next_gaussian(&self) -> bool {
|
||||
self.internal_has_next_gaussian
|
||||
}
|
||||
|
||||
fn stored_next_gaussian(&self) -> f64 {
|
||||
self.internal_next_gaussian
|
||||
}
|
||||
|
||||
fn set_has_next_gaussian(&mut self, value: bool) {
|
||||
self.internal_has_next_gaussian = value;
|
||||
}
|
||||
|
||||
fn set_stored_next_gaussian(&mut self, value: f64) {
|
||||
self.internal_next_gaussian = value;
|
||||
}
|
||||
}
|
||||
|
||||
impl Random for LegacyRand {
|
||||
fn from_seed(seed: u64) -> Self {
|
||||
LegacyRand {
|
||||
seed: (seed ^ 0x5DEECE66D) & 0xFFFFFFFFFFFF,
|
||||
internal_has_next_gaussian: false,
|
||||
internal_next_gaussian: 0f64,
|
||||
}
|
||||
}
|
||||
|
||||
fn next(&mut self, bits: u64) -> u64 {
|
||||
self.next_random() >> (48 - bits)
|
||||
}
|
||||
|
||||
fn split(&mut self) -> Self {
|
||||
LegacyRand::from_seed(self.next_i64() as u64)
|
||||
}
|
||||
|
||||
fn next_i32(&mut self) -> i32 {
|
||||
self.next(32) as i32
|
||||
}
|
||||
|
||||
fn next_i64(&mut self) -> i64 {
|
||||
let i = self.next_i32();
|
||||
let j = self.next_i32();
|
||||
((i as i64) << 32).wrapping_add(j as i64)
|
||||
}
|
||||
|
||||
fn next_f32(&mut self) -> f32 {
|
||||
self.next(24) as f32 * 5.9604645E-8f32
|
||||
}
|
||||
|
||||
fn next_f64(&mut self) -> f64 {
|
||||
let i = self.next(26);
|
||||
let j = self.next(27);
|
||||
let l = (i << 27).wrapping_add(j);
|
||||
l as f64 * 1.110223E-16f32 as f64
|
||||
}
|
||||
|
||||
fn next_bool(&mut self) -> bool {
|
||||
self.next(1) != 0
|
||||
}
|
||||
|
||||
fn next_splitter(&mut self) -> impl RandomSplitter {
|
||||
LegacySplitter::new(self.next_i64() as u64)
|
||||
}
|
||||
|
||||
fn next_gaussian(&mut self) -> f64 {
|
||||
self.calculate_gaussian()
|
||||
}
|
||||
|
||||
fn next_bounded_i32(&mut self, bound: i32) -> i32 {
|
||||
if bound & (bound - 1) == 0 {
|
||||
(bound as u64).wrapping_mul(self.next(31) >> 31) as i32
|
||||
} else {
|
||||
loop {
|
||||
let i = self.next(31) as i32;
|
||||
let j = i % bound;
|
||||
if (i - j + (bound - 1)) > 0 {
|
||||
return j;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct LegacySplitter {
|
||||
seed: u64,
|
||||
}
|
||||
|
||||
impl LegacySplitter {
|
||||
fn new(seed: u64) -> Self {
|
||||
LegacySplitter { seed }
|
||||
}
|
||||
}
|
||||
|
||||
impl RandomSplitter for LegacySplitter {
|
||||
fn split_u64(&self, seed: u64) -> impl Random {
|
||||
LegacyRand::from_seed(seed)
|
||||
}
|
||||
|
||||
fn split_string(&self, seed: &str) -> impl Random {
|
||||
let string_hash = java_string_hash(seed);
|
||||
LegacyRand::from_seed((string_hash as u64) ^ self.seed)
|
||||
}
|
||||
|
||||
fn split_pos(&self, x: i32, y: i32, z: i32) -> impl Random {
|
||||
let pos_hash = hash_block_pos(x, y, z);
|
||||
LegacyRand::from_seed((pos_hash as u64) ^ self.seed)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::random::{Random, RandomSplitter};
|
||||
|
||||
use super::LegacyRand;
|
||||
|
||||
#[test]
|
||||
fn test_next_i32() {
|
||||
let mut rand = LegacyRand::from_seed(0);
|
||||
|
||||
let values = [
|
||||
-1155484576,
|
||||
-723955400,
|
||||
1033096058,
|
||||
-1690734402,
|
||||
-1557280266,
|
||||
1327362106,
|
||||
-1930858313,
|
||||
502539523,
|
||||
-1728529858,
|
||||
-938301587,
|
||||
];
|
||||
|
||||
for value in values {
|
||||
assert_eq!(rand.next_i32(), value);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_next_bounded_i32() {
|
||||
let mut rand = LegacyRand::from_seed(0);
|
||||
|
||||
let values = [0, 13, 4, 2, 5, 8, 11, 6, 9, 14];
|
||||
|
||||
for value in values {
|
||||
assert_eq!(rand.next_bounded_i32(0xf), value);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_next_inbetween_i32() {
|
||||
let mut rand = LegacyRand::from_seed(0);
|
||||
|
||||
let values = [1, 5, 2, 12, 12, 6, 12, 10, 4, 3];
|
||||
|
||||
for value in values {
|
||||
assert_eq!(rand.next_inbetween_i32(1, 12), value);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_next_inbetween_exclusive_i32() {
|
||||
let mut rand = LegacyRand::from_seed(0);
|
||||
|
||||
let values = [1, 7, 9, 6, 7, 3, 3, 7, 3, 1];
|
||||
|
||||
for value in values {
|
||||
assert_eq!(rand.next_inbetween_i32_exclusive(1, 12), value);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_next_f64() {
|
||||
let mut rand = LegacyRand::from_seed(0);
|
||||
|
||||
let values = [
|
||||
0.730967787376657,
|
||||
0.24053641567148587,
|
||||
0.6374174253501083,
|
||||
0.5504370051176339,
|
||||
0.5975452777972018,
|
||||
0.3332183994766498,
|
||||
0.3851891847407185,
|
||||
0.984841540199809,
|
||||
0.8791825178724801,
|
||||
0.9412491794821144,
|
||||
];
|
||||
|
||||
for value in values {
|
||||
assert_eq!(rand.next_f64(), value);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_next_f32() {
|
||||
let mut rand = LegacyRand::from_seed(0);
|
||||
|
||||
let values: [f32; 10] = [
|
||||
0.73096776, 0.831441, 0.24053639, 0.6063452, 0.6374174, 0.30905056, 0.550437,
|
||||
0.1170066, 0.59754527, 0.7815346,
|
||||
];
|
||||
|
||||
for value in values {
|
||||
assert_eq!(rand.next_f32(), value);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_next_i64() {
|
||||
let mut rand = LegacyRand::from_seed(0);
|
||||
|
||||
let values: [i64; 10] = [
|
||||
-4962768465676381896,
|
||||
4437113781045784766,
|
||||
-6688467811848818630,
|
||||
-8292973307042192125,
|
||||
-7423979211207825555,
|
||||
6146794652083548235,
|
||||
7105486291024734541,
|
||||
-279624296851435688,
|
||||
-2228689144322150137,
|
||||
-1083761183081836303,
|
||||
];
|
||||
|
||||
for value in values {
|
||||
assert_eq!(rand.next_i64(), value);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_next_bool() {
|
||||
let mut rand = LegacyRand::from_seed(0);
|
||||
|
||||
let values = [
|
||||
true, true, false, true, true, false, true, false, true, true,
|
||||
];
|
||||
|
||||
for value in values {
|
||||
assert_eq!(rand.next_bool(), value);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_next_gaussian() {
|
||||
let mut rand = LegacyRand::from_seed(0);
|
||||
|
||||
let values = [
|
||||
0.8025330637390305,
|
||||
-0.9015460884175122,
|
||||
2.080920790428163,
|
||||
0.7637707684364894,
|
||||
0.9845745328825128,
|
||||
-1.6834122587673428,
|
||||
-0.027290262907887285,
|
||||
0.11524570286202315,
|
||||
-0.39016704137993774,
|
||||
-0.643388813126449,
|
||||
];
|
||||
|
||||
for value in values {
|
||||
assert_eq!(rand.next_gaussian(), value);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_next_triangular() {
|
||||
let mut rand = LegacyRand::from_seed(0);
|
||||
|
||||
let values = [
|
||||
124.52156858525856,
|
||||
104.34902101162372,
|
||||
113.2163439160276,
|
||||
70.01738222704547,
|
||||
96.89666691951828,
|
||||
107.30284075808541,
|
||||
106.16817675813144,
|
||||
79.11264482608078,
|
||||
73.96721613927062,
|
||||
81.72419521080646,
|
||||
];
|
||||
|
||||
for value in values {
|
||||
assert_eq!(rand.next_triangular(100f64, 50f64), value);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_split() {
|
||||
let mut original_rand = LegacyRand::from_seed(0);
|
||||
let mut new_rand = original_rand.split();
|
||||
|
||||
{
|
||||
let splitter = new_rand.next_splitter();
|
||||
|
||||
let mut rand1 = splitter.split_string("TEST STRING");
|
||||
assert_eq!(rand1.next_i32(), -1170413697);
|
||||
|
||||
let mut rand2 = splitter.split_u64(10);
|
||||
assert_eq!(rand2.next_i32(), -1157793070);
|
||||
|
||||
let mut rand3 = splitter.split_pos(1, 11, -111);
|
||||
assert_eq!(rand3.next_i32(), -1213890343);
|
||||
}
|
||||
|
||||
assert_eq!(original_rand.next_i32(), 1033096058);
|
||||
assert_eq!(new_rand.next_i32(), -888301832);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
pub mod xoroshiro128;
|
||||
|
||||
mod gaussian;
|
||||
pub mod legacy_rand;
|
||||
pub mod xoroshiro128;
|
||||
|
||||
pub trait Random {
|
||||
fn from_seed(seed: u64) -> Self;
|
||||
@@ -35,7 +35,7 @@ pub trait Random {
|
||||
|
||||
fn skip(&mut self, count: i32) {
|
||||
for _ in 0..count {
|
||||
self.next_i32();
|
||||
self.next_i64();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,3 +51,82 @@ pub trait RandomSplitter {
|
||||
|
||||
fn split_pos(&self, x: i32, y: i32, z: i32) -> impl Random;
|
||||
}
|
||||
|
||||
fn hash_block_pos(x: i32, y: i32, z: i32) -> i64 {
|
||||
let l = (x.wrapping_mul(3129871) as i64) ^ ((z as i64).wrapping_mul(116129781i64)) ^ (y as i64);
|
||||
let l = l
|
||||
.wrapping_mul(l)
|
||||
.wrapping_mul(42317861i64)
|
||||
.wrapping_add(l.wrapping_mul(11i64));
|
||||
l >> 16
|
||||
}
|
||||
|
||||
fn java_string_hash(string: &str) -> u32 {
|
||||
// All byte values of latin1 align with
|
||||
// the values of U+0000 - U+00FF making this code
|
||||
// equivalent to both java hash implementations
|
||||
|
||||
let mut result = 0u32;
|
||||
|
||||
for char_encoding in string.encode_utf16() {
|
||||
result = 31u32
|
||||
.wrapping_mul(result)
|
||||
.wrapping_add(char_encoding as u32);
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use crate::random::java_string_hash;
|
||||
|
||||
use super::hash_block_pos;
|
||||
|
||||
#[test]
|
||||
fn block_position_hash() {
|
||||
let values: [((i32, i32, i32), i64); 8] = [
|
||||
((0, 0, 0), 0),
|
||||
((1, 1, 1), 60311958971344),
|
||||
((4, 4, 4), 120566413180880),
|
||||
((25, 25, 25), 111753446486209),
|
||||
((676, 676, 676), 75210837988243),
|
||||
((458329, 458329, 458329), -43764888250),
|
||||
((-387008604, -387008604, -387008604), 8437923733503),
|
||||
((176771161, 176771161, 176771161), 18421337580760),
|
||||
];
|
||||
|
||||
for ((x, y, z), value) in values {
|
||||
assert_eq!(hash_block_pos(x, y, z), value);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_java_string_hash() {
|
||||
let values = [
|
||||
("", 0),
|
||||
("1", 49),
|
||||
("TEST", 2571410),
|
||||
("TEST1", 79713759),
|
||||
("TEST0123456789", 506557463),
|
||||
(
|
||||
" !\"#$%&'()*+,-./0123456789:\
|
||||
;<=>?@ABCDEFGHIJKLMNOPQRST\
|
||||
UVWXYZ[\\]^_`abcdefghijklm\
|
||||
nopqrstuvwxyz{|}~¡¢£¤¥¦§¨©\
|
||||
ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄ\
|
||||
ÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞ\
|
||||
ßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþ",
|
||||
(-1992287231i32) as u32,
|
||||
),
|
||||
("求同存异", 847053876),
|
||||
// This might look wierd because hebrew is text is right to left
|
||||
("אבְּרֵאשִׁ֖ית בָּרָ֣א אֱלֹהִ֑ים אֵ֥ת הַשָּׁמַ֖יִם וְאֵ֥ת הָאָֽרֶץ:", 1372570871),
|
||||
("संस्कृत-", 1748614838),
|
||||
];
|
||||
|
||||
for (string, value) in values {
|
||||
assert_eq!(java_string_hash(string), value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use super::{gaussian::GaussianGenerator, Random, RandomSplitter};
|
||||
use super::{gaussian::GaussianGenerator, hash_block_pos, Random, RandomSplitter};
|
||||
|
||||
pub struct Xoroshiro {
|
||||
lo: u64,
|
||||
@@ -130,12 +130,6 @@ impl Random for Xoroshiro {
|
||||
fn next_gaussian(&mut self) -> f64 {
|
||||
self.calculate_gaussian()
|
||||
}
|
||||
|
||||
fn skip(&mut self, count: i32) {
|
||||
for _ in 0..count {
|
||||
self.next_random();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct XoroshiroSplitter {
|
||||
@@ -143,19 +137,9 @@ pub struct XoroshiroSplitter {
|
||||
hi: u64,
|
||||
}
|
||||
|
||||
fn hash_pos(x: i32, y: i32, z: i32) -> i64 {
|
||||
let l =
|
||||
((x.wrapping_mul(3129871)) as i64) ^ ((z as i64).wrapping_mul(116129781i64)) ^ (y as i64);
|
||||
let l = l
|
||||
.wrapping_mul(l)
|
||||
.wrapping_mul(42317861i64)
|
||||
.wrapping_add(l.wrapping_mul(11i64));
|
||||
l >> 16
|
||||
}
|
||||
|
||||
impl RandomSplitter for XoroshiroSplitter {
|
||||
fn split_pos(&self, x: i32, y: i32, z: i32) -> impl Random {
|
||||
let l = hash_pos(x, y, z) as u64;
|
||||
let l = hash_block_pos(x, y, z) as u64;
|
||||
let m = l ^ self.lo;
|
||||
Xoroshiro::new(m, self.hi)
|
||||
}
|
||||
@@ -177,28 +161,10 @@ impl RandomSplitter for XoroshiroSplitter {
|
||||
mod tests {
|
||||
use crate::random::{Random, RandomSplitter};
|
||||
|
||||
use super::{hash_pos, mix_stafford_13, Xoroshiro};
|
||||
use super::{mix_stafford_13, Xoroshiro};
|
||||
|
||||
// Values checked against results from the equivalent Java source
|
||||
|
||||
#[test]
|
||||
fn block_position_hash() {
|
||||
let values: [((i32, i32, i32), i64); 8] = [
|
||||
((0, 0, 0), 0),
|
||||
((1, 1, 1), 60311958971344),
|
||||
((4, 4, 4), 120566413180880),
|
||||
((25, 25, 25), 111753446486209),
|
||||
((676, 676, 676), 75210837988243),
|
||||
((458329, 458329, 458329), -43764888250),
|
||||
((-387008604, -387008604, -387008604), 8437923733503),
|
||||
((176771161, 176771161, 176771161), 18421337580760),
|
||||
];
|
||||
|
||||
for ((x, y, z), value) in values {
|
||||
assert_eq!(hash_pos(x, y, z), value);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mix_stafford_13() {
|
||||
let values: [(u64, i64); 31] = [
|
||||
|
||||
Reference in New Issue
Block a user