MoonTools.ECS/src/Random.cs

165 lines
3.0 KiB
C#
Raw Normal View History

2023-10-05 22:56:45 +00:00
using System;
using System.Runtime.CompilerServices;
2022-04-08 05:52:03 +00:00
namespace MoonTools.ECS
{
2023-10-05 22:56:45 +00:00
// WELLRNG512
// https://stackoverflow.com/a/1227137
public class Random
{
2023-10-05 22:56:45 +00:00
public const int STATE_BYTE_COUNT = 68; // 16 state ints + 1 index int
2023-10-05 22:56:45 +00:00
uint[] State = new uint[16];
uint Index = 0;
uint Seed;
public Random()
{
2023-10-05 22:56:45 +00:00
Init((uint) Environment.TickCount);
}
2023-10-05 22:56:45 +00:00
public void Init(uint seed)
2023-07-27 21:53:50 +00:00
{
2023-10-05 22:56:45 +00:00
Seed = seed;
uint s = seed;
for (int i = 0; i < 16; i++)
{
s = (((s * 214013 + 2531011) >> 16) & 0x7fffffff) | 0;
State[i] = ~ ~s; //i ;
}
Index = 0;
2023-07-27 21:53:50 +00:00
}
2023-10-05 22:56:45 +00:00
public uint GetSeed()
{
2023-10-05 22:56:45 +00:00
return Seed;
}
2023-10-05 22:56:45 +00:00
public string PrintState()
{
2023-10-05 22:56:45 +00:00
var s = "";
for (var i = 0; i < 16; i += 1)
{
2023-10-05 22:56:45 +00:00
s += State[i];
}
2023-10-05 22:56:45 +00:00
s += Index;
return s;
}
2023-10-09 21:06:13 +00:00
// expects a span of STATE_BYTE_COUNT bytes
2023-10-05 22:56:45 +00:00
public unsafe void SaveState(Span<byte> bytes)
{
#if DEBUG
if (bytes.Length < STATE_BYTE_COUNT)
{
throw new ArgumentException("Byte span too short!");
}
#endif
2023-10-05 22:56:45 +00:00
fixed (byte* ptr = bytes)
{
var offset = 0;
for (var i = 0; i < 16; i += 1)
{
Unsafe.Write(ptr + offset, State[i]);
offset += 4;
}
2023-10-05 22:56:45 +00:00
Unsafe.Write(ptr + offset, Index);
}
}
2023-10-09 21:06:13 +00:00
// expects a span of STATE_BYTE_COUNT bytes
2023-10-05 22:56:45 +00:00
public unsafe void LoadState(Span<byte> bytes)
{
2023-10-05 22:56:45 +00:00
#if DEBUG
if (bytes.Length < STATE_BYTE_COUNT)
{
2023-10-05 22:56:45 +00:00
throw new ArgumentException("Byte span too short!");
}
2023-10-05 22:56:45 +00:00
#endif
fixed (byte* ptr = bytes)
{
var offset = 0;
for (var i = 0; i < 16; i += 1)
{
State[i] = Unsafe.Read<uint>(ptr + offset);
offset += 4;
}
Index = Unsafe.Read<uint>(ptr + offset);
}
}
2023-10-05 22:56:45 +00:00
private uint NextInternal()
{
uint a, b, c, d;
a = State[Index];
c = State[(Index+13)&15];
b = a^c^(a<<16)^(c<<15);
c = State[(Index+9)&15];
c ^= (c>>11);
a = State[Index] = b^c;
d = (uint) (a ^((a<<5)&0xDA442D24UL));
Index = (Index + 15)&15;
a = State[Index];
State[Index] = a^b^d^(a<<2)^(b<<18)^(c<<28);
return State[Index];
}
// .NET Random API always returns a non-negative signed int. Fun!
public int Next()
{
return (int) (NextInternal() >>> 1); // unsigned bitshift right to get rid of signed bit
2023-10-05 22:56:45 +00:00
}
public int Next(int n)
{
2023-10-06 20:38:17 +00:00
return (int) (((double) Next()) * n / int.MaxValue);
2023-10-05 22:56:45 +00:00
}
public int Next(int min, int max)
{
var diff = max - min;
var next = Next(diff);
return min + next;
}
public long NextInt64()
{
long next = NextInternal();
next <<= 32;
2023-10-05 22:56:45 +00:00
next |= NextInternal();
next >>>= 1;
2023-10-05 22:56:45 +00:00
return next;
}
public long NextInt64(long n)
{
var next = NextInt64();
2023-10-06 20:38:17 +00:00
return (long) (((double) next) * n / long.MaxValue);
2023-10-05 22:56:45 +00:00
}
public long NextInt64(long min, long max)
{
var diff = max - min;
var next = NextInt64(diff);
return min + next;
}
public float NextFloat()
{
var n = NextInternal();
return ((float) n) / uint.MaxValue;
}
2023-10-05 22:56:45 +00:00
public double NextDouble()
{
2023-10-05 22:56:45 +00:00
var n = NextInternal();
return ((double) n) / uint.MaxValue;
}
}
}