summaryrefslogtreecommitdiff
path: root/dungeon/src/rng.rs
blob: bb9151b63819683ea84418f8cb8edfb7ae91840f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
//! Implements rng using the xoshiro256++ algorithm

use rand::{
	SeedableRng,
	rand_core::{RngCore, le},
};

/// Deterministic pseudo random number generator.
///
/// Is is a copy of `SmallRng` from `rand`, but is gurenteed
/// to be reproducible across versions and platforms.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct DungeonRng {
	s: [u64; 4],
}
impl SeedableRng for DungeonRng {
	// Fix to 256 bits. Changing this is a breaking change!
	type Seed = [u8; 32];

	#[inline]
	fn from_seed(seed: Self::Seed) -> Self {
		let mut state = [0; 4];
		le::read_u64_into(&seed, &mut state);
		Self { s: state }
	}

	#[inline]
	fn seed_from_u64(mut state: u64) -> Self {
		const PHI: u64 = 0x9e3779b97f4a7c15;
		let mut s = [0; 4];
		for i in &mut s {
			state = state.wrapping_add(PHI);
			let mut z = state;
			z = (z ^ (z >> 30)).wrapping_mul(0xbf58476d1ce4e5b9);
			z = (z ^ (z >> 27)).wrapping_mul(0x94d049bb133111eb);
			z = z ^ (z >> 31);
			*i = z;
		}
		Self { s }
	}
}
impl RngCore for DungeonRng {
	#[inline]
	fn next_u32(&mut self) -> u32 {
		let val = self.next_u64();
		(val >> 32) as u32
	}

	#[inline]
	fn next_u64(&mut self) -> u64 {
		let res = self.s[0]
			.wrapping_add(self.s[3])
			.rotate_left(23)
			.wrapping_add(self.s[0]);

		let t = self.s[1] << 17;

		self.s[2] ^= self.s[0];
		self.s[3] ^= self.s[1];
		self.s[1] ^= self.s[2];
		self.s[0] ^= self.s[3];

		self.s[2] ^= t;

		self.s[3] = self.s[3].rotate_left(45);

		res
	}

	#[inline]
	fn fill_bytes(&mut self, dst: &mut [u8]) {
		le::fill_bytes_via_next(self, dst);
	}
}