Unity-WebSocket/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/WeightedRandom.cs
2025-06-28 11:28:54 +03:30

96 lines
3.4 KiB
C#

using GameKit.Dependencies.Utilities.Types;
using System.Collections.Generic;
using UnityEngine;
namespace GameKit.Dependencies.Utilities
{
public interface IWeighted
{
float GetWeight();
ByteRange GetQuantity();
}
public static class WeightedRandom
{
/// <summary>
/// Gets random entries by weight.
/// </summary>
/// <param name="source">Entries to pull from.</param>
/// <param name="countRange">Number of entries to get.</param>
/// <param name="results">Results of entries. Key is the entry, Value is the number of drops.</param>
/// <param name="allowRepeatingDrops">True to allow the same entry to be included within results more than once.</param>
public static void GetEntries<T>(List<T> source, IntRange countRange, ref Dictionary<T, uint> results, bool allowRepeatingDrops = false) where T : IWeighted
{
if (source == null || source.Count == 0)
{
Debug.Log($"Source list of type {typeof(T).Name} cannot be null or empty.");
return;
}
int count = Ints.RandomInclusiveRange(countRange.Minimum, countRange.Maximum);
//If to not return any then exit early.
if (count == 0)
return;
//Number of times each item has dropped.
Dictionary<T, byte> dropCount = CollectionCaches<T, byte>.RetrieveDictionary();
//Get the total weight.
float totalWeight = 0f;
for (int i = 0; i < source.Count; i++)
totalWeight += source[i].GetWeight();
//Make a copy of source to not modify source.
List<T> sourceCopy = CollectionCaches<T>.RetrieveList();
foreach (T item in source)
sourceCopy.Add(item);
while (results.Count < count)
{
int startCount = results.Count;
/* Reset copy to totalWeight.
* totalWeight will be modified if
* a non-repeatable item is pulled. */
float tWeightCopy = totalWeight;
float rnd = UnityEngine.Random.Range(0f, totalWeight);
for (int i = 0; i < sourceCopy.Count; i++)
{
T item = sourceCopy[i];
float weight = item.GetWeight();
if (rnd <= weight)
{
//Try to get current count.
results.TryGetValueIL2CPP(item, out uint currentCount);
//Set new vlaue.
results[item] = (currentCount + 1);
/* If cannot stay in collection then remove it
* from copy and remove its weight
* from total. */
if (!allowRepeatingDrops)
{
sourceCopy.RemoveAt(i);
totalWeight -= weight;
}
break;
}
else
{
tWeightCopy -= weight;
}
}
/* If nothing was added to results then
* something went wrong. */
if (results.Count == startCount)
break;
}
CollectionCaches<T, byte>.Store(dropCount);
}
}
}