1
0
Fork 0
mirror of https://github.com/Ellpeck/MLEM.git synced 2024-12-26 02:09:24 +01:00

Added the SingleRandom utility class

This commit is contained in:
Ell 2022-11-29 21:11:59 +01:00
parent 1a1b2025cd
commit 81c69041c3
3 changed files with 162 additions and 0 deletions

View file

@ -18,6 +18,7 @@ Additions
- Added GetDownTime, GetUpTime, GetTimeSincePress, WasModifierDown and WasDown to Keybind and Combination
- Added the ability for UniformTextureAtlases to have padding for each region
- Added UniformTextureAtlas methods ToList and ToDictionary
- Added the SingleRandom utility class
- **Added the ability to find paths to one of multiple goals using AStar**
Improvements

116
MLEM/Misc/SingleRandom.cs Normal file
View file

@ -0,0 +1,116 @@
using System;
namespace MLEM.Misc {
/// <summary>
/// The SingleRandom class allows generating single, one-off pseudorandom numbers based on a seed, or a set of seeds.
/// The types of numbers that can be generated are <see cref="int"/> and <see cref="float"/>, the former of which can be generated with specific minimum and maximum values.
/// Methods in this class are tested to be sufficiently "random", that is, to be sufficiently distributed throughout their range, as well as sufficiently different for neighboring seeds.
/// </summary>
public class SingleRandom {
/// <summary>
/// Generates a single, one-off pseudorandom integer between 0 and <see cref="int.MaxValue"/> based on a given <paramref name="seed"/>.
/// This method is guaranteed to return the same result for the same <paramref name="seed"/>.
/// </summary>
/// <param name="seed">The seed to use.</param>
/// <returns>The generated number.</returns>
public static int Int(int seed) {
return (int) (SingleRandom.Single(seed) * int.MaxValue);
}
/// <summary>
/// Generates a single, one-off pseudorandom integer between 0 and <see cref="int.MaxValue"/> based on a given set of <paramref name="seeds"/>.
/// This method is guaranteed to return the same result for the same set of <paramref name="seeds"/>.
/// </summary>
/// <param name="seeds">The seeds to use.</param>
/// <returns>The generated number.</returns>
public static int Int(params int[] seeds) {
return (int) (SingleRandom.Single(seeds) * int.MaxValue);
}
/// <summary>
/// Generates a single, one-off pseudorandom integer between 0 and <paramref name="maxValue"/> based on a given <paramref name="seed"/>.
/// This method is guaranteed to return the same result for the same <paramref name="seed"/>.
/// </summary>
/// <param name="maxValue">The (exclusive) maximum value.</param>
/// <param name="seed">The seed to use.</param>
/// <returns>The generated number.</returns>
public static int Int(int maxValue, int seed) {
return (int) (maxValue * SingleRandom.Single(seed));
}
/// <summary>
/// Generates a single, one-off pseudorandom integer between 0 and <paramref name="maxValue"/> based on a given set of <paramref name="seeds"/>.
/// This method is guaranteed to return the same result for the same set of <paramref name="seeds"/>.
/// </summary>
/// <param name="maxValue">The (exclusive) maximum value.</param>
/// <param name="seeds">The seeds to use.</param>
/// <returns>The generated number.</returns>
public static int Int(int maxValue, params int[] seeds) {
return (int) (maxValue * SingleRandom.Single(seeds));
}
/// <summary>
/// Generates a single, one-off pseudorandom integer between <paramref name="minValue"/> and <paramref name="maxValue"/> based on a given <paramref name="seed"/>.
/// This method is guaranteed to return the same result for the same <paramref name="seed"/>.
/// </summary>
/// <param name="minValue">The (inclusive) minimum value.</param>
/// <param name="maxValue">The (exclusive) maximum value.</param>
/// <param name="seed">The seed to use.</param>
/// <returns>The generated number.</returns>
public static int Int(int minValue, int maxValue, int seed) {
return (int) ((maxValue - minValue) * SingleRandom.Single(seed)) + minValue;
}
/// <summary>
/// Generates a single, one-off pseudorandom integer between <paramref name="minValue"/> and <paramref name="maxValue"/> based on a given set of <paramref name="seeds"/>.
/// This method is guaranteed to return the same result for the same set of <paramref name="seeds"/>.
/// </summary>
/// <param name="minValue">The (inclusive) minimum value.</param>
/// <param name="maxValue">The (exclusive) maximum value.</param>
/// <param name="seeds">The seeds to use.</param>
/// <returns>The generated number.</returns>
public static int Int(int minValue, int maxValue, params int[] seeds) {
return (int) ((maxValue - minValue) * SingleRandom.Single(seeds)) + minValue;
}
/// <summary>
/// Generates a single, one-off pseudorandom floating point number between 0 and 1 based on a given <paramref name="seed"/>.
/// This method is guaranteed to return the same result for the same <paramref name="seed"/>.
/// </summary>
/// <param name="seed">The seed to use.</param>
/// <returns>The generated number.</returns>
public static float Single(int seed) {
return (SingleRandom.Scramble(seed) / (float) int.MaxValue + 1) / 2;
}
/// <summary>
/// Generates a single, one-off pseudorandom floating point number between 0 and 1 based on a given set of <paramref name="seeds"/>.
/// This method is guaranteed to return the same result for the same set of <paramref name="seeds"/>.
/// </summary>
/// <param name="seeds">The seeds to use.</param>
/// <returns>The generated number.</returns>
public static float Single(params int[] seeds) {
return (SingleRandom.Scramble(seeds) / (float) int.MaxValue + 1) / 2;
}
private static int Scramble(int[] seeds) {
if (seeds == null || seeds.Length <= 0)
throw new ArgumentOutOfRangeException(nameof(seeds));
var ret = 1;
for (var i = 0; i < seeds.Length; i++)
ret *= SingleRandom.Scramble(seeds[i]);
return ret;
}
private static int Scramble(int seed) {
seed ^= (seed << 7);
seed *= 207398809;
seed ^= (seed << 17);
seed *= 928511849;
seed ^= (seed << 12);
return seed;
}
}
}

View file

@ -0,0 +1,45 @@
using System.Collections.Generic;
using System.Linq;
using MLEM.Misc;
using NUnit.Framework;
namespace Tests;
public class SingleRandomTests {
[Test]
public void TestEquality() {
for (var i = 0; i < 1000000; i++) {
Assert.AreEqual(SingleRandom.Single(i), SingleRandom.Single(new[] {i}));
Assert.AreEqual(SingleRandom.Int(i), SingleRandom.Int(new[] {i}));
// test if all methods that accept mins and max are identical
Assert.AreEqual(SingleRandom.Int(i), SingleRandom.Int(int.MaxValue, i));
Assert.AreEqual(SingleRandom.Int(i), SingleRandom.Int(0, int.MaxValue, i));
}
}
[Test]
public void TestBounds() {
for (var i = 0; i < 1000000; i++) {
Assert.That(SingleRandom.Single(i), Is.LessThan(1).And.GreaterThanOrEqualTo(0));
Assert.That(SingleRandom.Int(i), Is.LessThan(int.MaxValue).And.GreaterThanOrEqualTo(0));
Assert.That(SingleRandom.Int(17, i), Is.LessThan(17).And.GreaterThanOrEqualTo(0));
Assert.That(SingleRandom.Int(19283, 832498394, i), Is.LessThan(832498394).And.GreaterThanOrEqualTo(19283));
}
}
[Test]
public void TestAverages() {
var ints = new List<int>();
var flts = new List<float>();
for (var i = 0; i < 1000000; i++) {
ints.Add(SingleRandom.Int(i));
flts.Add(SingleRandom.Single(i));
}
// allow being off by 0.00001 of the total
Assert.AreEqual(ints.Average(), int.MaxValue / 2, 0.00001 * int.MaxValue);
Assert.AreEqual(flts.Average(), 0.5, 0.00001);
}
}