using System; using System.Runtime.CompilerServices; using Microsoft.Xna.Framework; namespace MonoGame.Extended { /// /// Represents a pseudo-random number generator using a linear congruential generator algorithm. /// /// /// /// This implementation uses the same constants as Microsoft Visual C++ rand() function: /// /// a=214013 /// c=2531011 /// m=2^31 /// /// /// It provides high performance and speed, but comes at the price of having lower statistical quality, or true /// 'randomness' compared to modern algorithms. The algorithm is deterministic based on the initial seed /// value, making it suitable for reproducible sequences. /// /// /// Note: This pseudo-random number generator exhibits noticeable patterns and should not be used for /// cryptographic purposes or when a high-quality random distribution is critical. Consider using /// for better statistical properties. /// /// public class FastRandom { private readonly IFastRandomImpl _impl; /// /// Provides a thread-safe instance that may be used concurrently from any thread. /// public static FastRandom Shared { get; } = new FastRandom(new ThreadSafeFastRandomImpl()); /// /// Initializes a new instance of the class using the default seed value. /// public FastRandom() : this(1) { } /// /// Initializes a new instance of the class using the specified seed value. /// /// A number used to calculate a starting value for the pseudo-random number sequence. public FastRandom(int seed) { _impl = new LinearCongruentialGeneratorImpl(seed); } private FastRandom(IFastRandomImpl impl) { ArgumentNullException.ThrowIfNull(impl); _impl = impl; } /// /// Returns a non-negative random integer. /// /// A 32-bit signed integer that is greater than or equal to 0 and less than 32768. public int Next() { return _impl.Next(); } /// /// Returns a non-negative random integer that is less than or equal to the specified maximum. /// /// The inclusive upper bound of the random number to be generated. /// /// A 32-bit signed integer that is greater than or equal to 0 and less than or equal to . /// public int Next(int max) { return _impl.Next(max); } /// /// Returns a random integer that is within a specified range. /// /// The inclusive lower bound of the random number returned. /// The inclusive upper bound of the random number returned. /// /// A 32-bit signed integer that is greater than or equal to and less than or equal to /// . /// public int Next(int min, int max) { return _impl.Next(min, max); } /// /// Returns a random integer that is within a specified range. /// /// /// A range representing the inclusive lower and upper bound of the random number to return. /// /// /// A 32-bit signed integer that is greater than or equal to the and less than or /// equal to the value of . /// [Obsolete("Use Next(Interval). Range will be removed in 6.0")] public int Next(Range range) { return _impl.Next(range); } /// /// Returns a random integer that is within a closed interval. /// /// /// A closed interval representing the lower and upper bounds of the random number to return. /// /// /// A 32-bit signed integer that is greater than or equal to the value and less /// than or equal to the value of . /// public int Next(Interval interval) { return _impl.Next(interval); } /// /// Returns a random floating-point number that is greater than or equal to 0.0 and less than 1.0. /// /// /// A single-precision floating point number that is greater than or equal to 0.0 and less than 1.0. /// public float NextSingle() { return _impl.NextSingle(); } /// /// Returns a random floating-point number that is greater than or equal to 0.0 and less than the /// specified maximum. /// /// The exclusive upper bond of the random number generated. /// /// A single precision floating-point number that is greater than or equal to 0.0 and less than /// . /// public float NextSingle(float max) { return _impl.NextSingle(max); } /// /// Returns a random floating-point number that is within a specified range. /// /// The inclusive lower bound of the random number returned. /// The exclusive upper bound of the random number returned. /// /// A single-precision floating point number that is greater than or equal to and /// less than . /// public float NextSingle(float min, float max) { return _impl.NextSingle(min, max); } /// /// Returns a random floating-point number that is within a specified range. /// /// /// A range representing the inclusive lower and exclusive upper bound of the random number returned. /// /// /// A single-precision floating point number that is greater than or equal to the /// and less than the value of /// [Obsolete("Use Next(Interval). Range will be removed in 6.0")] public float NextSingle(Range range) { return _impl.NextSingle(range); } /// /// Returns a random floating-point number that is within a closed interval. /// /// /// A closed interval representing the lower and upper bounds of the random number to return. /// /// /// A single-precision floating point number that is greater than or equal to the /// value and less than or equal to the value of . /// public float NextSingle(Interval interval) { return _impl.NextSingle(interval); } /// /// Returns a random angle between -π and π. /// /// /// A random angle value in radians. /// public float NextAngle() { return _impl.NextAngle(); } /// /// Gets a random unit vector. /// /// When this method returns, contains a unit vector with a random direction. public void NextUnitVector(out Vector2 vector) { _impl.NextUnitVector(out vector); } /// /// Gets a random unit vector. /// /// A pointer to the Vector2 where the random unit vector will be stored. public unsafe void NextUnitVector(Vector2* vector) { _impl.NextUnitVector(vector); } #region IFastRandomImplementation private interface IFastRandomImpl { int Next(); int Next(int max); int Next(int min, int max); [Obsolete("Use Next(Interval). Range will be removed in 6.0")] int Next(Range range); int Next(Interval interval); float NextSingle(); float NextSingle(float max); float NextSingle(float min, float max); [Obsolete("Use Next(Interval). Range will be removed in 6.0")] float NextSingle(Range range); float NextSingle(Interval interval); float NextAngle(); void NextUnitVector(out Vector2 vector); unsafe void NextUnitVector(Vector2* vector); } #endregion #region Linear Congruential Generator private sealed class LinearCongruentialGeneratorImpl : IFastRandomImpl { private const int MULTIPLIER = 214013; private const int INCREMENT = 2531011; private int _state; public LinearCongruentialGeneratorImpl() : this(1) { } public LinearCongruentialGeneratorImpl(int seed) { ArgumentOutOfRangeException.ThrowIfNegativeOrZero(seed); _state = seed; } public int Next() { _state = MULTIPLIER * _state + INCREMENT; return (_state >> 16) & 0x7FFF; } public int Next(int max) { return (int)(max * NextSingle() + 0.5f); } public int Next(int min, int max) { return (int)((max - min) * NextSingle() + 0.5f) + min; } [Obsolete("Use Next(Interval). Range will be removed in 6.0")] public int Next(Range range) { return Next(range.Min, range.Max); } public int Next(Interval interval) { return Next(interval.Min, interval.Max); } public float NextSingle() { return Next() / (float)short.MaxValue; } public float NextSingle(float max) { return max * NextSingle(); } public float NextSingle(float min, float max) { return (max - min) * NextSingle() + min; } [Obsolete("Use Next(Interval). Range will be removed in 6.0")] public float NextSingle(Range range) { return NextSingle(range.Min, range.Max); } public float NextSingle(Interval interval) { return NextSingle(interval.Min, interval.Max); } public float NextAngle() { return NextSingle(-MathHelper.Pi, MathHelper.Pi); } public void NextUnitVector(out Vector2 vector) { float angle = NextAngle(); vector.X = MathF.Cos(angle); vector.Y = MathF.Sin(angle); } public unsafe void NextUnitVector(Vector2* vector) { float angle = NextAngle(); vector->X = MathF.Cos(angle); vector->Y = MathF.Sin(angle); } } #endregion #region ThreadSafeImpl private sealed class ThreadSafeFastRandomImpl : IFastRandomImpl { [ThreadStatic] private static LinearCongruentialGeneratorImpl t_random; private static LinearCongruentialGeneratorImpl LocalRandom => t_random ?? Create(); [MethodImpl(MethodImplOptions.NoInlining)] private static LinearCongruentialGeneratorImpl Create() { t_random = new LinearCongruentialGeneratorImpl(); return t_random; } public int Next() { return LocalRandom.Next(); } public int Next(int max) { return LocalRandom.Next(max); } public int Next(int min, int max) { return LocalRandom.Next(min, max); } [Obsolete("Use Next(Interval). Range will be removed in 6.0")] public int Next(Range range) { return LocalRandom.Next(range.Min, range.Max); } public int Next(Interval interval) { return LocalRandom.Next(interval.Min, interval.Max); } public float NextSingle() { return LocalRandom.NextSingle(); } public float NextSingle(float max) { return LocalRandom.NextSingle(max); } public float NextSingle(float min, float max) { return LocalRandom.NextSingle(min, max); } [Obsolete("Use Next(Interval). Range will be removed in 6.0")] public float NextSingle(Range range) { return LocalRandom.NextSingle(range.Min, range.Max); } public float NextSingle(Interval interval) { return LocalRandom.NextSingle(interval.Min, interval.Max); } public float NextAngle() { return LocalRandom.NextAngle(); } public void NextUnitVector(out Vector2 vector) { LocalRandom.NextUnitVector(out vector); } public unsafe void NextUnitVector(Vector2* vector) { LocalRandom.NextUnitVector(vector); } } #endregion } }