Files
2026-08-17 18:18:13 +08:00

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]);
}
}
}
}