Files
TheVVaS-Assets/RPGCoreCommon/Helpers/Runtime/RandomHelper.cs
T
2026-04-25 23:37:10 +02:00

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);
}
}
}