68 lines
2.2 KiB
C#
68 lines
2.2 KiB
C#
|
|
namespace Theriapolis.Core.Util;
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// SplitMix64-based pseudo-random number generator with named sub-stream support.
|
|||
|
|
/// All game randomness must go through this class — no new System.Random() anywhere.
|
|||
|
|
/// </summary>
|
|||
|
|
public sealed class SeededRng
|
|||
|
|
{
|
|||
|
|
private ulong _state;
|
|||
|
|
|
|||
|
|
public SeededRng(ulong seed)
|
|||
|
|
{
|
|||
|
|
// Mix the seed to avoid bad low-entropy states
|
|||
|
|
_state = seed == 0 ? 0x9e3779b97f4a7c15UL : seed;
|
|||
|
|
// Warm up the state
|
|||
|
|
NextUInt64();
|
|||
|
|
NextUInt64();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>Create a sub-stream for a specific subsystem using the world seed and a named tag constant.</summary>
|
|||
|
|
public static SeededRng ForSubsystem(ulong worldSeed, ulong subsystemTag)
|
|||
|
|
=> new(worldSeed ^ subsystemTag);
|
|||
|
|
|
|||
|
|
public ulong NextUInt64()
|
|||
|
|
{
|
|||
|
|
// SplitMix64 step
|
|||
|
|
_state += 0x9e3779b97f4a7c15UL;
|
|||
|
|
ulong z = _state;
|
|||
|
|
z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9UL;
|
|||
|
|
z = (z ^ (z >> 27)) * 0x94d049bb133111ebUL;
|
|||
|
|
return z ^ (z >> 31);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public uint NextUInt32() => (uint)(NextUInt64() >> 32);
|
|||
|
|
|
|||
|
|
/// <summary>Returns a double in [0, 1).</summary>
|
|||
|
|
public double NextDouble() => (NextUInt64() >> 11) * (1.0 / (1UL << 53));
|
|||
|
|
|
|||
|
|
/// <summary>Returns a float in [0, 1).</summary>
|
|||
|
|
public float NextFloat() => (float)NextDouble();
|
|||
|
|
|
|||
|
|
/// <summary>Returns a float in [min, max).</summary>
|
|||
|
|
public float NextFloat(float min, float max) => min + NextFloat() * (max - min);
|
|||
|
|
|
|||
|
|
/// <summary>Returns an int in [min, max).</summary>
|
|||
|
|
public int NextInt(int min, int max)
|
|||
|
|
{
|
|||
|
|
if (max <= min) return min;
|
|||
|
|
return min + (int)(NextUInt64() % (ulong)(max - min));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>Returns an int in [0, max).</summary>
|
|||
|
|
public int NextInt(int max) => NextInt(0, max);
|
|||
|
|
|
|||
|
|
/// <summary>Returns true with probability p (0–1).</summary>
|
|||
|
|
public bool NextBool(double p = 0.5) => NextDouble() < p;
|
|||
|
|
|
|||
|
|
/// <summary>Shuffles a span in place using Fisher–Yates.</summary>
|
|||
|
|
public void Shuffle<T>(Span<T> span)
|
|||
|
|
{
|
|||
|
|
for (int i = span.Length - 1; i > 0; i--)
|
|||
|
|
{
|
|||
|
|
int j = NextInt(0, i + 1);
|
|||
|
|
(span[i], span[j]) = (span[j], span[i]);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|