diff --git a/00_Common/dotnet/Games.Common/Randomness/IRandom.cs b/00_Common/dotnet/Games.Common/Randomness/IRandom.cs new file mode 100644 index 00000000..0ed4a587 --- /dev/null +++ b/00_Common/dotnet/Games.Common/Randomness/IRandom.cs @@ -0,0 +1,55 @@ +namespace Games.Common.Randomness; + +/// +/// Provides access to a random number generator +/// +public interface IRandom +{ + /// + /// Gets a random such that 0 <= n < 1. + /// + /// The random number. + float NextFloat(); + + /// + /// Gets a random such that 0 <= n < exclusiveMaximum. + /// + /// The random number. + float NextFloat(float exclusiveMaximum); + + /// + /// Gets a random such that inclusiveMinimum <= n < exclusiveMaximum. + /// + /// The random number. + float NextFloat(float inclusiveMinimum, float exclusiveMaximum); + + /// + /// Gets a random such that 0 <= n < exclusiveMaximum. + /// + /// The random number. + int Next(int exclusiveMaximum); + + /// + /// Gets a random such that inclusiveMinimum <= n < exclusiveMaximum. + /// + /// The random number. + int Next(int inclusiveMinimum, int exclusiveMaximum); + + /// + /// Gets the previous random number as a . + /// + /// The previous random number. + float PreviousFloat(); + + /// + /// Gets the previous random number as an . + /// + /// The previous random number. + int Previous(); + + /// + /// Reseeds the random number generator. + /// + /// The seed. + void Reseed(int seed); +} diff --git a/00_Common/dotnet/Games.Common/Randomness/RandomNumberGenerator.cs b/00_Common/dotnet/Games.Common/Randomness/RandomNumberGenerator.cs new file mode 100644 index 00000000..2c8749cb --- /dev/null +++ b/00_Common/dotnet/Games.Common/Randomness/RandomNumberGenerator.cs @@ -0,0 +1,50 @@ +using System; + +namespace Games.Common.Randomness; + +public class RandomNumberGenerator : IRandom +{ + private Random _random; + private float _previous; + + public RandomNumberGenerator() + { + // The BASIC RNG is seeded based on time with a 1 second resolution + _random = new Random((int)(DateTime.UtcNow.Ticks / TimeSpan.TicksPerSecond)); + } + + public float NextFloat() => NextFloat(1); + + public float NextFloat(float exclusiveMaximum) + { + if (exclusiveMaximum <= 0) + { + throw new ArgumentOutOfRangeException(nameof(exclusiveMaximum), "Must be greater than 0."); + } + + return NextFloat(0, exclusiveMaximum); + } + + public float NextFloat(float inclusiveMinimum, float exclusiveMaximum) + { + if (exclusiveMaximum <= inclusiveMinimum) + { + throw new ArgumentOutOfRangeException(nameof(exclusiveMaximum), "Must be greater than inclusiveMinimum."); + } + + var range = exclusiveMaximum - inclusiveMinimum; + return _previous = ((float)_random.NextDouble()) * range + inclusiveMinimum; + } + + public int Next(int exclusiveMaximum) => ToInt(NextFloat(exclusiveMaximum)); + + public int Next(int inclusiveMinimum, int exclusiveMaximum) => ToInt(NextFloat(inclusiveMinimum, exclusiveMaximum)); + + public float PreviousFloat() => _previous; + + public int Previous() => ToInt(_previous); + + private static int ToInt(float value) => (int)Math.Floor(value); + + public void Reseed(int seed) => _random = new Random(seed); +} \ No newline at end of file