65 lines
1.9 KiB
C#
65 lines
1.9 KiB
C#
#nullable enable
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
|
|
namespace ReplacedPerson.Core
|
|
{
|
|
/// <summary>PCG-XSH-RR 32; state is explicit so replays are portable across Unity and .NET.</summary>
|
|
public sealed class Pcg32
|
|
{
|
|
public ulong State { get; private set; }
|
|
public ulong Increment { get; private set; }
|
|
|
|
public Pcg32(ulong seed, ulong stream = 54UL)
|
|
{
|
|
State = 0UL;
|
|
Increment = (stream << 1) | 1UL;
|
|
NextUInt();
|
|
State += seed;
|
|
NextUInt();
|
|
}
|
|
|
|
private Pcg32(ulong state, ulong increment, bool _) { State = state; Increment = increment; }
|
|
|
|
public Pcg32 Clone() => new(State, Increment, true);
|
|
|
|
public uint NextUInt()
|
|
{
|
|
var oldState = State;
|
|
State = unchecked(oldState * 6364136223846793005UL + Increment);
|
|
var xorShifted = (uint)(((oldState >> 18) ^ oldState) >> 27);
|
|
var rotation = (int)(oldState >> 59);
|
|
return (xorShifted >> rotation) | (xorShifted << ((-rotation) & 31));
|
|
}
|
|
|
|
public int NextInt(int exclusiveMax)
|
|
{
|
|
if (exclusiveMax <= 0)
|
|
throw new ArgumentOutOfRangeException(nameof(exclusiveMax));
|
|
|
|
var bound = (uint)exclusiveMax;
|
|
var threshold = unchecked((uint)(-bound)) % bound;
|
|
while (true)
|
|
{
|
|
var value = NextUInt();
|
|
if (value >= threshold)
|
|
return (int)(value % bound);
|
|
}
|
|
}
|
|
|
|
public int RollD6() => NextInt(6) + 1;
|
|
|
|
public void Shuffle<T>(IList<T> values)
|
|
{
|
|
if (values == null)
|
|
throw new ArgumentNullException(nameof(values));
|
|
for (var i = values.Count - 1; i > 0; i--)
|
|
{
|
|
var j = NextInt(i + 1);
|
|
(values[i], values[j]) = (values[j], values[i]);
|
|
}
|
|
}
|
|
}
|
|
}
|