using System;
using System.Collections.Generic;
using System.Linq;
using RPGCoreCommon.Helpers.Exceptions;
using UnityEngine;
namespace RPGCoreCommon.Helpers
{
///
/// This utility class basses on Unity's which uses:
///
/// - to manage seed
/// - to get random value
///
///
public static class RandomHelper
{
///
///
/// Alias to Unity's .
///
///
public static void InitState(int seed)
{
UnityEngine.Random.InitState(seed);
}
///
/// Returns float between min (inclusive) and max (inclusive).
///
public static float RandomFloat(float min = 0f, float max = 1f)
{
return UnityEngine.Random.value * (max-min) + min;
}
///
/// Returns integer between minimum (inclusive) and maximum (inclusive).
///
public static int RandomInt(int minInclusive = 0, int maxInclusive = 100)
{
return Mathf.RoundToInt(UnityEngine.Random.value * (maxInclusive-minInclusive)) + minInclusive;
}
///
/// Returns random key from given list.
///
public static int RandomKey(IEnumerable enumerable)
{
var list = enumerable.ToList();
if (!list.Any()) throw new NotEnoughElementsForRandomPickException("List need at least one element.");
return RandomInt(0, list.Count - 1);
}
///
/// Returns random element from given list.
///
public static T RandomElement(IEnumerable enumerable)
{
return RandomElements(enumerable, 1).First();
}
///
/// Returns new list with randomized elements.
/// List needs to have at least equal or more elements that given count! If repeatable then list needs to have at least 1 element.
///
/// When given list does not have enough elements to choose from.
public static List RandomElements(IEnumerable 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();
}
///
/// Returns keys from given list by its weight.
///
/// When given list does not have any elements to choose from.
public static int RandomKeyByWeight(IEnumerable enumerable, Func 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;
}
///
/// Returns element from given list by its weight.
///
/// When given list does not have any elements to choose from.
public static T RandomElementByWeight(IEnumerable enumerable, Func weightGetter)
{
return RandomElementsByWeight(enumerable, weightGetter, 1).First();
}
///
/// Returns elements from given list by its weight.
///
/// When given list does not have enough elements to choose from.
public static List RandomElementsByWeight(IEnumerable enumerable, Func weightGetter, int count, bool repeatable = true)
{
var list = enumerable.ToList();
var listResult = new List();
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;
}
///
/// Percentage chance by given float. Simply checks if random float is smaller than given one.
///
/// Chance to get true. 0.0f = 0%, 1.0f = 100%
public static bool Chance(float chance)
{
return RandomFloat() < Mathf.Clamp01(chance);
}
}
}