139 lines
5.8 KiB
C#
139 lines
5.8 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using RPGCoreCommon.Helpers.Exceptions;
|
|
using UnityEngine;
|
|
|
|
namespace RPGCoreCommon.Helpers
|
|
{
|
|
/// <summary>
|
|
/// This utility class basses on Unity's <see cref="UnityEngine.Random"/> which uses:
|
|
/// <ul>
|
|
/// <li><see cref="UnityEngine.Random.InitState"/> to manage seed</li>
|
|
/// <li><see cref="UnityEngine.Random.value"/> to get random value</li>
|
|
/// </ul>
|
|
/// </summary>
|
|
public static class RandomHelper
|
|
{
|
|
/// <summary>
|
|
/// <inheritdoc cref="UnityEngine.Random.InitState"/>
|
|
/// Alias to Unity's <see cref="UnityEngine.Random.InitState"/>.
|
|
/// </summary>
|
|
/// <param name="seed"><inheritdoc cref="UnityEngine.Random.InitState" path="/param[@name='seed']"/></param>
|
|
public static void InitState(int seed)
|
|
{
|
|
UnityEngine.Random.InitState(seed);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns <b>float</b> between min (inclusive) and max (inclusive).
|
|
/// </summary>
|
|
public static float RandomFloat(float min = 0f, float max = 1f)
|
|
{
|
|
return UnityEngine.Random.value * (max-min) + min;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns integer between minimum (inclusive) and maximum (inclusive).
|
|
/// </summary>
|
|
public static int RandomInt(int minInclusive = 0, int maxInclusive = 100)
|
|
{
|
|
return Mathf.RoundToInt(UnityEngine.Random.value * (maxInclusive-minInclusive)) + minInclusive;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns random key from given list.
|
|
/// </summary>
|
|
public static int RandomKey<T>(IEnumerable<T> enumerable)
|
|
{
|
|
var list = enumerable.ToList();
|
|
if (!list.Any()) throw new NotEnoughElementsForRandomPickException("List need at least one element.");
|
|
|
|
return RandomInt(0, list.Count - 1);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns random element from given list.
|
|
/// </summary>
|
|
public static T RandomElement<T>(IEnumerable<T> enumerable)
|
|
{
|
|
return RandomElements(enumerable, 1).First();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns new list with randomized elements.<br/>
|
|
/// List needs to have at least equal or more elements that given count! If repeatable then list needs to have at least 1 element.<br/>
|
|
/// </summary>
|
|
/// <exception cref="NotEnoughElementsForRandomPickException">When given list does not have enough elements to choose from.</exception>
|
|
public static List<T> RandomElements<T>(IEnumerable<T> enumerable, int count, bool repeatable = true)
|
|
{
|
|
var list = enumerable.ToList();
|
|
var neededCount = repeatable ? 1 : list.Count;
|
|
if (list.Count < neededCount) throw new NotEnoughElementsForRandomPickException($"Not enough elements in list. Need={neededCount}, Provided={list.Count}");
|
|
|
|
if (!repeatable) return list.OrderBy(_ => RandomFloat()).Take(count).ToList();
|
|
|
|
return new T[count].Select(_ => list[RandomInt(0, list.Count - 1)]).ToList();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns keys from given list by its weight.
|
|
/// </summary>
|
|
/// <exception cref="NotEnoughElementsForRandomPickException">When given list does not have any elements to choose from.</exception>
|
|
public static int RandomKeyByWeight<T>(IEnumerable<T> enumerable, Func<T, float> weightGetter)
|
|
{
|
|
var list = enumerable.ToList();
|
|
if (!list.Any()) throw new NotEnoughElementsForRandomPickException("List need at least one element.");
|
|
var maxWeight = list.Sum(weightGetter.Invoke);
|
|
var randomWeight = RandomFloat(0, maxWeight);
|
|
|
|
for (var i = 0; i < list.Count; i++)
|
|
{
|
|
randomWeight -= weightGetter.Invoke(list[i]);
|
|
if (randomWeight <= 0) return i;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns element from given list by its weight.
|
|
/// </summary>
|
|
/// <exception cref="NotEnoughElementsForRandomPickException">When given list does not have any elements to choose from.</exception>
|
|
public static T RandomElementByWeight<T>(IEnumerable<T> enumerable, Func<T, float> weightGetter)
|
|
{
|
|
return RandomElementsByWeight(enumerable, weightGetter, 1).First();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns elements from given list by its weight.
|
|
/// </summary>
|
|
/// <exception cref="NotEnoughElementsForRandomPickException">When given list does not have enough elements to choose from.</exception>
|
|
public static List<T> RandomElementsByWeight<T>(IEnumerable<T> enumerable, Func<T, float> weightGetter, int count, bool repeatable = true)
|
|
{
|
|
var list = enumerable.ToList();
|
|
var listResult = new List<T>();
|
|
var neededCount = repeatable ? 1 : list.Count;
|
|
if (list.Count < neededCount) throw new NotEnoughElementsForRandomPickException($"Not enough elements in list. Need={neededCount}, Provided={list.Count}");
|
|
|
|
for (var i = 0; i < count; i++)
|
|
{
|
|
var randomKey = RandomKeyByWeight(list, weightGetter);
|
|
listResult.Add(list[randomKey]);
|
|
if (!repeatable) list.RemoveAt(randomKey);
|
|
}
|
|
|
|
return listResult;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Percentage chance by given float. Simply checks if random float is smaller than given one.
|
|
/// </summary>
|
|
/// <param name="chance">Chance to get true. <b>0.0f</b> = 0%, <b>1.0f</b> = 100%</param>
|
|
public static bool Chance(float chance)
|
|
{
|
|
return RandomFloat() < Mathf.Clamp01(chance);
|
|
}
|
|
}
|
|
}
|