Skip to content

Commit

Permalink
RandomE: Added new GetRandom overload that support Unity.Mathematics.…
Browse files Browse the repository at this point in the history
…Random, dictionary random now expects IDictionary
  • Loading branch information
BasmanovDaniil committed Jan 5, 2021
1 parent 1795ae3 commit 8e9477f
Show file tree
Hide file tree
Showing 3 changed files with 132 additions and 20 deletions.
102 changes: 91 additions & 11 deletions Runtime/RandomE.cs
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,22 @@ public static T GetRandom<T>(this IList<T> list)
return list[URandom.Range(0, list.Count)];
}

/// <summary>
/// Returns a random element
/// </summary>
public static T GetRandom<T>(this IList<T> list, ref MRandom random)
{
if (list == null)
{
throw new ArgumentNullException(nameof(list));
}
if (list.Count == 0)
{
throw new ArgumentException("Empty list");
}
return list[random.NextInt(0, list.Count)];
}

/// <summary>
/// Returns a random element
/// </summary>
Expand All @@ -357,27 +373,57 @@ public static T GetRandom<T>(T item1, T item2, params T[] items)
}

/// <summary>
/// Returns a random value from the dictionary
/// Returns a random element with the chances of rolling based on <paramref name="weights"/>
/// </summary>
public static TValue GetRandom<TKey, TValue>(this Dictionary<TKey, TValue> dictionary)
/// <param name="weights">Positive floats representing weights. Negative values may lead to unpredictable behaviour.</param>
public static T GetRandom<T>(this IList<T> list, IList<float> weights)
{
if (dictionary == null)
if (list == null)
{
throw new ArgumentNullException(nameof(dictionary));
throw new ArgumentNullException(nameof(list));
}
var keys = dictionary.Keys;
if (keys.Count == 0)
if (list.Count == 0)
{
throw new ArgumentException("Empty dictionary");
throw new ArgumentException("Empty list");
}
return dictionary[new List<TKey>(keys).GetRandom()];
if (weights == null)
{
throw new ArgumentNullException(nameof(weights));
}
if (weights.Count == 0)
{
throw new ArgumentException("Empty weights");
}
if (list.Count != weights.Count)
{
throw new ArgumentException("Array sizes must be equal");
}

if (list.Count == 1)
{
return list[0];
}

var cumulative = new List<float>(weights);
for (int i = 1; i < cumulative.Count; i++)
{
cumulative[i] += cumulative[i - 1];
}

float randomValue = URandom.Range(0, cumulative[cumulative.Count - 1]);
int index = cumulative.FindIndex(a => a >= randomValue);
if (index == -1)
{
throw new ArgumentException("Weights must be positive");
}
return list[index];
}

/// <summary>
/// Returns a random element with the chances of rolling based on <paramref name="weights"/>
/// </summary>
/// <param name="weights">Positive floats representing weights. Negative values may lead to unpredictable behaviour.</param>
public static T GetRandom<T>(this IList<T> list, IList<float> weights)
public static T GetRandom<T>(this IList<T> list, IList<float> weights, ref MRandom random)
{
if (list == null)
{
Expand Down Expand Up @@ -411,15 +457,49 @@ public static T GetRandom<T>(this IList<T> list, IList<float> weights)
cumulative[i] += cumulative[i - 1];
}

float random = URandom.Range(0, cumulative[cumulative.Count - 1]);
int index = cumulative.FindIndex(a => a >= random);
float randomValue = random.NextFloat(0, cumulative[cumulative.Count - 1]);
int index = cumulative.FindIndex(a => a >= randomValue);
if (index == -1)
{
throw new ArgumentException("Weights must be positive");
}
return list[index];
}

/// <summary>
/// Returns a random value from the dictionary
/// </summary>
public static TValue GetRandom<TKey, TValue>(this IDictionary<TKey, TValue> dictionary)
{
if (dictionary == null)
{
throw new ArgumentNullException(nameof(dictionary));
}
var keys = dictionary.Keys;
if (keys.Count == 0)
{
throw new ArgumentException("Empty dictionary");
}
return dictionary[new List<TKey>(keys).GetRandom()];
}

/// <summary>
/// Returns a random value from the dictionary
/// </summary>
public static TValue GetRandom<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, ref MRandom random)
{
if (dictionary == null)
{
throw new ArgumentNullException(nameof(dictionary));
}
var keys = dictionary.Keys;
if (keys.Count == 0)
{
throw new ArgumentException("Empty dictionary");
}
return dictionary[new List<TKey>(keys).GetRandom(ref random)];
}

/// <summary>
/// Returns a random character from the string
/// </summary>
Expand Down
27 changes: 27 additions & 0 deletions Tests/Editor/RandomETest.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using NUnit.Framework;
using MRandom = Unity.Mathematics.Random;

namespace ProceduralToolkit.Tests.RandomETest
{
Expand All @@ -14,19 +15,24 @@ public class GetRandom_IList
private readonly List<int> nullList = null;
private readonly List<int> emptyList = new List<int>();
private readonly List<int> list = new List<int> {0, 1, 2, 3};
private MRandom random = new MRandom(1337);

[Test]
public void NullListThrowsException()
{
Assert.Catch<ArgumentNullException>(() => nullArray.GetRandom());
Assert.Catch<ArgumentNullException>(() => nullArray.GetRandom(ref random));
Assert.Catch<ArgumentNullException>(() => nullList.GetRandom());
Assert.Catch<ArgumentNullException>(() => nullList.GetRandom(ref random));
}

[Test]
public void EmptyListThrowsException()
{
Assert.Catch<ArgumentException>(() => emptyArray.GetRandom());
Assert.Catch<ArgumentException>(() => emptyArray.GetRandom(ref random));
Assert.Catch<ArgumentException>(() => emptyList.GetRandom());
Assert.Catch<ArgumentException>(() => emptyList.GetRandom(ref random));
}

[Test]
Expand All @@ -35,7 +41,9 @@ public void ListContainsReturnedItem()
for (int i = 0; i < 100; i++)
{
Assert.Contains(array.GetRandom(), array);
Assert.Contains(array.GetRandom(ref random), array);
Assert.Contains(list.GetRandom(), list);
Assert.Contains(list.GetRandom(ref random), list);
}
}
}
Expand All @@ -53,47 +61,60 @@ public class GetRandom_IListWeighted
private readonly float[] weights1 = new float[] {1};
private readonly float[] weights1111 = new float[] {1, 1, 1, 1};
private readonly float[] negativeWeights = new float[] {-100, 1, 1, 1};
private MRandom random = new MRandom(1337);

[Test]
public void NullListThrowsException()
{
Assert.Catch<ArgumentNullException>(() => nullArray.GetRandom(weights1111));
Assert.Catch<ArgumentNullException>(() => nullArray.GetRandom(weights1111, ref random));
Assert.Catch<ArgumentNullException>(() => nullList.GetRandom(weights1111));
Assert.Catch<ArgumentNullException>(() => nullList.GetRandom(weights1111, ref random));
}

[Test]
public void EmptyListThrowsException()
{
Assert.Catch<ArgumentException>(() => emptyArray.GetRandom(weights1111));
Assert.Catch<ArgumentException>(() => emptyArray.GetRandom(weights1111, ref random));
Assert.Catch<ArgumentException>(() => emptyList.GetRandom(weights1111));
Assert.Catch<ArgumentException>(() => emptyList.GetRandom(weights1111, ref random));
}

[Test]
public void NullWeightsThrowsException()
{
Assert.Catch<ArgumentNullException>(() => array.GetRandom(nullWeights));
Assert.Catch<ArgumentNullException>(() => array.GetRandom(nullWeights, ref random));
Assert.Catch<ArgumentNullException>(() => list.GetRandom(nullWeights));
Assert.Catch<ArgumentNullException>(() => list.GetRandom(nullWeights, ref random));
}

[Test]
public void EmptyWeightsThrowsException()
{
Assert.Catch<ArgumentException>(() => array.GetRandom(emptyWeights));
Assert.Catch<ArgumentException>(() => array.GetRandom(emptyWeights, ref random));
Assert.Catch<ArgumentException>(() => list.GetRandom(emptyWeights));
Assert.Catch<ArgumentException>(() => list.GetRandom(emptyWeights, ref random));
}

[Test]
public void DifferentSizeThrowsException()
{
Assert.Catch<ArgumentException>(() => array.GetRandom(weights1));
Assert.Catch<ArgumentException>(() => array.GetRandom(weights1, ref random));
Assert.Catch<ArgumentException>(() => list.GetRandom(weights1));
Assert.Catch<ArgumentException>(() => list.GetRandom(weights1, ref random));
}

[Test]
public void NegativeWeightsThrowsException()
{
Assert.Catch<ArgumentException>(() => array.GetRandom(negativeWeights));
Assert.Catch<ArgumentException>(() => array.GetRandom(negativeWeights, ref random));
Assert.Catch<ArgumentException>(() => list.GetRandom(negativeWeights));
Assert.Catch<ArgumentException>(() => list.GetRandom(negativeWeights, ref random));
}

[Test]
Expand All @@ -102,7 +123,9 @@ public void ListContainsReturnedItem()
for (int i = 0; i < 100; i++)
{
Assert.Contains(array.GetRandom(weights1111), array);
Assert.Contains(array.GetRandom(weights1111, ref random), array);
Assert.Contains(list.GetRandom(weights1111), list);
Assert.Contains(list.GetRandom(weights1111, ref random), list);
}
}
}
Expand Down Expand Up @@ -169,17 +192,20 @@ public class GetRandom_Dictionary
{3, 30},
{4, 40},
};
private MRandom random = new MRandom(1337);

[Test]
public void NullDictionaryThrowsException()
{
Assert.Catch<ArgumentNullException>(() => nullDictionary.GetRandom());
Assert.Catch<ArgumentNullException>(() => nullDictionary.GetRandom(ref random));
}

[Test]
public void EmptyDictionaryThrowsException()
{
Assert.Catch<ArgumentException>(() => emptyDictionary.GetRandom());
Assert.Catch<ArgumentException>(() => emptyDictionary.GetRandom(ref random));
}

[Test]
Expand All @@ -188,6 +214,7 @@ public void DictionaryContainsReturnedItem()
for (int i = 0; i < 100; i++)
{
Assert.True(dictionary.ContainsValue(dictionary.GetRandom()));
Assert.True(dictionary.ContainsValue(dictionary.GetRandom(ref random)));
}
}
}
Expand Down
23 changes: 14 additions & 9 deletions Tests/ProceduralToolkit.Tests.asmdef
Original file line number Diff line number Diff line change
@@ -1,19 +1,24 @@
{
"name": "ProceduralToolkit.Tests",
"references": [
"GUID:561cd3090aa423b4584912885a62ef6f"
],
"optionalUnityReferences": [
"TestAssemblies"
"GUID:561cd3090aa423b4584912885a62ef6f",
"GUID:27619889b8ba8c24980f49ee34dbb44a",
"GUID:0acc523941302664db1f4e527237feb3",
"GUID:d8b63aba1907145bea998dd612889d6b"
],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": []
"overrideReferences": true,
"precompiledReferences": [
"nunit.framework.dll"
],
"autoReferenced": false,
"defineConstraints": [
"UNITY_INCLUDE_TESTS"
],
"versionDefines": [],
"noEngineReferences": false
}

0 comments on commit 8e9477f

Please sign in to comment.