2019-08-06 14:20:11 +02:00
using System ;
using System.Collections.Generic ;
2020-01-17 01:33:31 +01:00
using System.Linq ;
2019-08-06 14:20:11 +02:00
namespace MLEM.Extensions {
2020-05-21 12:53:42 +02:00
/// <summary>
/// A set of extensions for dealing with <see cref="Random"/>
/// </summary>
2019-08-06 14:20:11 +02:00
public static class RandomExtensions {
2020-05-20 23:59:40 +02:00
/// <summary>
2022-07-19 15:20:19 +02:00
/// Gets a random entry from the given collection with uniform chance.
2020-05-20 23:59:40 +02:00
/// </summary>
/// <param name="random">The random</param>
/// <param name="entries">The entries to choose from</param>
/// <typeparam name="T">The entries' type</typeparam>
/// <returns>A random entry</returns>
2022-07-19 15:20:19 +02:00
public static T GetRandomEntry < T > ( this Random random , ICollection < T > entries ) {
// ElementAt internally optimizes for IList access so we don't have to here
return entries . ElementAt ( random . Next ( entries . Count ) ) ;
2019-08-06 14:20:11 +02:00
}
2020-05-20 23:59:40 +02:00
/// <summary>
2022-07-19 15:20:19 +02:00
/// Returns a random entry from the given collection based on the specified weight function.
2020-05-20 23:59:40 +02:00
/// A higher weight for an entry increases its likeliness of being picked.
/// </summary>
/// <param name="random">The random</param>
/// <param name="entries">The entries to choose from</param>
/// <param name="weightFunc">A function that applies weight to each entry</param>
/// <typeparam name="T">The entries' type</typeparam>
/// <returns>A random entry, based on the entries' weight</returns>
/// <exception cref="IndexOutOfRangeException">If the weight function returns different weights for the same entry</exception>
2022-07-19 15:20:19 +02:00
public static T GetRandomWeightedEntry < T > ( this Random random , ICollection < T > entries , Func < T , int > weightFunc ) {
2020-01-17 01:33:31 +01:00
var totalWeight = entries . Sum ( weightFunc ) ;
var goalWeight = random . Next ( totalWeight ) ;
var currWeight = 0 ;
foreach ( var entry in entries ) {
currWeight + = weightFunc ( entry ) ;
2023-04-26 21:49:43 +02:00
if ( currWeight > goalWeight )
2020-01-17 01:33:31 +01:00
return entry ;
}
throw new IndexOutOfRangeException ( ) ;
}
2022-07-19 15:20:19 +02:00
/// <inheritdoc cref="GetRandomWeightedEntry{T}(System.Random,System.Collections.Generic.ICollection{T},System.Func{T,int})"/>
public static T GetRandomWeightedEntry < T > ( this Random random , ICollection < T > entries , Func < T , float > weightFunc ) {
2022-02-23 14:35:35 +01:00
var totalWeight = entries . Sum ( weightFunc ) ;
var goalWeight = random . NextDouble ( ) * totalWeight ;
var currWeight = 0F ;
foreach ( var entry in entries ) {
currWeight + = weightFunc ( entry ) ;
2023-04-26 21:49:43 +02:00
if ( currWeight > goalWeight )
2022-02-23 14:35:35 +01:00
return entry ;
}
throw new IndexOutOfRangeException ( ) ;
}
2023-01-07 20:01:22 +01:00
/// <summary>
/// Returns a random floating-point number that is greater than or equal to 0, and less than <paramref name="maxValue"/>.
/// </summary>
/// <param name="random">The random.</param>
/// <param name="maxValue">The (exclusive) maximum value.</param>
/// <returns>A single-precision floating point number that is greater than or equal to 0, and less than <paramref name="maxValue"/>.</returns>
public static float NextSingle ( this Random random , float maxValue ) {
return maxValue * random . NextSingle ( ) ;
}
/// <summary>
/// Returns a random floating-point number that is greater than or equal to <paramref name="minValue"/>, and less than <paramref name="maxValue"/>.
/// </summary>
/// <param name="random">The random.</param>
/// <param name="minValue">The (inclusive) minimum value.</param>
/// <param name="maxValue">The (exclusive) maximum value.</param>
/// <returns>A single-precision floating point number that is greater than or equal to <paramref name="minValue"/>, and less than <paramref name="maxValue"/>.</returns>
public static float NextSingle ( this Random random , float minValue , float maxValue ) {
return ( maxValue - minValue ) * random . NextSingle ( ) + minValue ;
}
#if ! NET6_0_OR_GREATER
/// <summary>
/// Returns a random floating-point number that is greater than or equal to 0, and less than 1.
/// </summary>
/// <param name="random">The random.</param>
/// <returns>A single-precision floating point number that is greater than or equal to 0, and less than 1.</returns>
public static float NextSingle ( this Random random ) {
return ( float ) random . NextDouble ( ) ;
}
#endif
2019-08-06 14:20:11 +02:00
}
2022-06-17 18:23:47 +02:00
}