Improve Random interface

This commit is contained in:
Andrew Cooper
2022-03-07 22:03:45 +11:00
parent 8165f7a161
commit 5aaf703bf4
3 changed files with 96 additions and 63 deletions

View File

@@ -6,47 +6,17 @@ namespace Games.Common.Randomness;
public interface IRandom public interface IRandom
{ {
/// <summary> /// <summary>
/// Gets a random <see cref="float" /> such that 0 <= n < 1. /// Gets a random <see cref="float" /> such that 0 &lt;= n &lt; 1.
/// </summary> /// </summary>
/// <returns>The random number.</returns> /// <returns>The random number.</returns>
float NextFloat(); float NextFloat();
/// <summary> /// <summary>
/// Gets a random <see cref="float" /> such that 0 <= n < exclusiveMaximum. /// Gets the <see cref="float" /> returned by the previous call to <see cref="NextFloat" />.
/// </summary>
/// <returns>The random number.</returns>
float NextFloat(float exclusiveMaximum);
/// <summary>
/// Gets a random <see cref="float" /> such that inclusiveMinimum <= n < exclusiveMaximum.
/// </summary>
/// <returns>The random number.</returns>
float NextFloat(float inclusiveMinimum, float exclusiveMaximum);
/// <summary>
/// Gets a random <see cref="int" /> such that 0 <= n < exclusiveMaximum.
/// </summary>
/// <returns>The random number.</returns>
int Next(int exclusiveMaximum);
/// <summary>
/// Gets a random <see cref="int" /> such that inclusiveMinimum <= n < exclusiveMaximum.
/// </summary>
/// <returns>The random number.</returns>
int Next(int inclusiveMinimum, int exclusiveMaximum);
/// <summary>
/// Gets the previous random number as a <see cref="float" />.
/// </summary> /// </summary>
/// <returns>The previous random number.</returns> /// <returns>The previous random number.</returns>
float PreviousFloat(); float PreviousFloat();
/// <summary>
/// Gets the previous random number as an <see cref="int" />.
/// </summary>
/// <returns>The previous random number.</returns>
int Previous();
/// <summary> /// <summary>
/// Reseeds the random number generator. /// Reseeds the random number generator.
/// </summary> /// </summary>

View File

@@ -0,0 +1,91 @@
using System;
namespace Games.Common.Randomness;
/// <summary>
/// Provides extension methods to <see cref="IRandom" /> providing random numbers in a given range.
/// </summary>
/// <value></value>
public static class IRandomExtensions
{
/// <summary>
/// Gets a random <see cref="float" /> such that 0 &lt;= n &lt; exclusiveMaximum.
/// </summary>
/// <returns>The random number.</returns>
public static float NextFloat(this IRandom random, float exclusiveMaximum) =>
Scale(random.NextFloat(), exclusiveMaximum);
/// <summary>
/// Gets a random <see cref="float" /> such that inclusiveMinimum &lt;= n &lt; exclusiveMaximum.
/// </summary>
/// <returns>The random number.</returns>
public static float NextFloat(this IRandom random, float inclusiveMinimum, float exclusiveMaximum) =>
Scale(random.NextFloat(), inclusiveMinimum, exclusiveMaximum);
/// <summary>
/// Gets a random <see cref="int" /> such that 0 &lt;= n &lt; exclusiveMaximum.
/// </summary>
/// <returns>The random number.</returns>
public static int Next(this IRandom random, int exclusiveMaximum) => ToInt(random.NextFloat(exclusiveMaximum));
/// <summary>
/// Gets a random <see cref="int" /> such that inclusiveMinimum &lt;= n &lt; exclusiveMaximum.
/// </summary>
/// <returns>The random number.</returns>
public static int Next(this IRandom random, int inclusiveMinimum, int exclusiveMaximum) =>
ToInt(random.NextFloat(inclusiveMinimum, exclusiveMaximum));
/// <summary>
/// Gets the previous unscaled <see cref="float" /> (between 0 and 1) scaled to a new range:
/// 0 &lt;= x &lt; <paramref name="exclusiveMaximum" />.
/// </summary>
/// <returns>The random number.</returns>
public static float PreviousFloat(this IRandom random, float exclusiveMaximum) =>
Scale(random.PreviousFloat(), exclusiveMaximum);
/// <summary>
/// Gets the previous unscaled <see cref="float" /> (between 0 and 1) scaled to a new range:
/// <paramref name="inclusiveMinimum" /> &lt;= n &lt; <paramref name="exclusiveMaximum" />.
/// </summary>
/// <returns>The random number.</returns>
public static float PreviousFloat(this IRandom random, float inclusiveMinimum, float exclusiveMaximum) =>
Scale(random.PreviousFloat(), inclusiveMinimum, exclusiveMaximum);
/// <summary>
/// Gets the previous unscaled <see cref="float" /> (between 0 and 1) scaled to an <see cref="int" /> in a new
/// range: 0 &lt;= n &lt; <paramref name="exclusiveMaximum" />.
/// </summary>
/// <returns>The random number.</returns>
public static int Previous(this IRandom random, int exclusiveMaximum) =>
ToInt(random.PreviousFloat(exclusiveMaximum));
/// <summary>
/// Gets the previous unscaled <see cref="float" /> (between 0 and 1) scaled to an <see cref="int" /> in a new
/// range: <paramref name="inclusiveMinimum" /> &lt;= n &lt; <paramref name="exclusiveMaximum" />.
/// <returns>The random number.</returns>
public static int Previous(this IRandom random, int inclusiveMinimum, int exclusiveMaximum) =>
ToInt(random.PreviousFloat(inclusiveMinimum, exclusiveMaximum));
private static float Scale(float zeroToOne, float exclusiveMaximum)
{
if (exclusiveMaximum <= 0)
{
throw new ArgumentOutOfRangeException(nameof(exclusiveMaximum), "Must be greater than 0.");
}
return Scale(zeroToOne, 0, exclusiveMaximum);
}
private static float Scale(float zeroToOne, float inclusiveMinimum, float exclusiveMaximum)
{
if (exclusiveMaximum <= inclusiveMinimum)
{
throw new ArgumentOutOfRangeException(nameof(exclusiveMaximum), "Must be greater than inclusiveMinimum.");
}
var range = exclusiveMaximum - inclusiveMinimum;
return zeroToOne * range + inclusiveMinimum;
}
private static int ToInt(float value) => (int)Math.Floor(value);
}

View File

@@ -2,6 +2,7 @@ using System;
namespace Games.Common.Randomness; namespace Games.Common.Randomness;
/// <inheritdoc />
public class RandomNumberGenerator : IRandom public class RandomNumberGenerator : IRandom
{ {
private Random _random; private Random _random;
@@ -13,38 +14,9 @@ public class RandomNumberGenerator : IRandom
_random = new Random((int)(DateTime.UtcNow.Ticks / TimeSpan.TicksPerSecond)); _random = new Random((int)(DateTime.UtcNow.Ticks / TimeSpan.TicksPerSecond));
} }
public float NextFloat() => NextFloat(1); public float NextFloat() => _previous = (float)_random.NextDouble();
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 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); public void Reseed(int seed) => _random = new Random(seed);
} }