namespace Microsoft.Xna.Framework; /// public static partial class MathHelper { /// /// Random Number Generator based on Permutation-Congruential-Generator (PCG), which is a fancy wording to /// describe a family of RNG which are simple, fast, statistically excellent, and hardly predictable. /// /// More interestingly, PCG allows to generate multiple sequences with the same seed, which is very handy /// in game development to have a unique seed per game session while using different streams for each /// RNG which requires an isolated context (e.g. generating a procedural level, but we don't want the loot /// generation to affect subsequent level generations). /// /// https://www.pcg-random.org/ /// /// This code is derived from the minimal C implementation. /// https://github.com/imneme/pcg-c-basic /// public class Random { // State private ulong _state; private readonly ulong _inc; /// /// State of the random number generator. /// public ulong State { get => _state; set { _state = value; PCG32(); // Advance state to ensure we don't return the same number twice. } } /// /// Initialize a random number generator. /// public Random() : this((ulong)System.Environment.TickCount) { } /// /// Initialize a random number generator. Specified in two parts, state initializer (a.k.a. seed) and a sequence selection constant (a.k.a. stream id). /// /// State initializer (a.k.a. seed). /// Sequence selection constant (a.k.a. stream id). Defaults to 0. public Random(ulong state, ulong streamID = 0) { _state = 0ul; _inc = (streamID << 1) | 1ul; PCG32(); _state += state; PCG32(); } /// /// Generate a uniformly distributed number. /// /// A uniformly distributed 32bit unsigned integer. private uint PCG32() { ulong oldState = _state; // Advance internal state _state = unchecked(_state * 6364136223846793005ul + _inc); // Calculate output function (XSH RR), uses old state for max ILP uint xorshifted = (uint)(((oldState >> 18) ^ oldState) >> 27); int rot = (int)(oldState >> 59); return (xorshifted >> rot) | (xorshifted << ((-rot) & 31)); } /// /// Generate a uniformly distributed number, r, where 0 <= and r < . /// /// Exclusive upper bound of the number to generate. /// A uniformly distributed 32bit unsigned integer strictly less than . private uint PCG32(uint bound) { if (bound <= 0) { bound = 1; } // To avoid bias, we need to make the range of the RNG a multiple of // bound, which we do by dropping output less than a threshold. uint threshold = ((uint)-bound) % bound; // Uniformity guarantees that this loop will terminate. In practice, it // should usually terminate quickly; on average (assuming all bounds are // equally likely), 82.25% of the time, we can expect it to require just // one iteration. In the worst case, someone passes a bound of 2^31 + 1 // (i.e., 2147483649), which invalidates almost 50% of the range. In // practice, bounds are typically small and only a tiny amount of the range // is eliminated. while (true) { uint r = PCG32(); if (r >= threshold) return r % bound; } } /// /// Generate a random positive number. /// /// A random positive number. public int Next() { return Next(int.MaxValue); } /// /// Generate a random number with an exclusive upper bound. /// /// Exclusive upper bound. /// A random number with an exclusive upper bound. public int Next(int maxValue) { if (maxValue < 0) maxValue = 1; return (int)PCG32((uint)maxValue); } /// /// Generate a random number ranging from to . /// /// Lower bound. /// Upper bound. /// A random number ranging from to . public int Next(int minValue, int maxValue) { if (maxValue < minValue) maxValue = minValue; return (int)(minValue + PCG32((uint)((long)maxValue - minValue))); } /// /// Generate a random float ranging from 0.0f to 1.0f. /// /// A random float ranging from 0.0f to 1.0f. public float NextFloat() { // This is quite hackish because we want to avoid BitConverter, but who cares? int bound = int.MaxValue / 2 - 1; return Next(bound) * 1.0f / bound; } /// /// Generate a random float ranging from to . /// /// Lower bound. MUST be greater than zero. /// Upper bound. MUST be greater than zero. /// A random float ranging from to . public float NextFloat(float minValue, float maxValue) { if (minValue < 0) minValue = 0; if (maxValue < minValue) maxValue = minValue; return minValue + (maxValue - minValue) * NextFloat(); } /// /// Generate a random bool. /// /// A random bool. public bool NextBool() { return NextFloat() <= 0.5f; } } }