Files
2026-05-26 16:15:54 +08:00

252 lines
7.8 KiB
C#

using System.Collections.Generic;
using System.Linq;
using UnityRandom = UnityEngine.Random;
using SystemRandom = System.Random;
using UnityEngine;
public static class FtMathUtils
{
/// <summary>
/// Split number into count pieces randomly.
/// </summary>
/// <param name="number">Number to split.</param>
/// <param name="count">Number of pieces.</param>
/// <param name="res">Result. Meaningless if result if false.</param>
/// <returns>False if fail to split.</returns>
public static bool RandomSplit(int number, int count, out List<int> res)
{
res = new List<int>();
if (number < count || number <= 0 || count <= 0)
return false;
var baseNumbers = new HashSet<int> { 0 };
while (baseNumbers.Count < count)
baseNumbers.Add(UnityRandom.Range(1, number - 1));
var baseList = baseNumbers.OrderBy(e => e).ToList();
for (int i = 1; i < baseList.Count; i++)
res.Add(baseList[i] - baseList[i - 1]);
res.Add(number - baseList[^1]);
return res.Count == count;
}
public static bool ScaleByBase(int num, int baseNum, out int res)
{
res = baseNum;
if (num <= 0 || baseNum <= 0)
return false;
if (num <= baseNum)
return true;
res = num / baseNum * baseNum;
return true;
}
public static int GetRandomIdxFromWeightList(IEnumerable<int> weightList)
{
// Check for null or empty list
if (weightList == null || !weightList.Any())
{
Debug.LogError("[GetRandomIdxFromWeightList]: Weight list is null or empty. Returning 0.");
return 0;
}
var accWeightList = new List<int>();
int tmp = 0;
foreach (int w in weightList)
{
// Check for negative weights
if (w < 0)
{
Debug.LogError($"[GetRandomIdxFromWeightList]: Negative weight detected: {w}. Returning 0.");
return 0;
}
tmp += w;
accWeightList.Add(tmp);
}
// Check for all zero weights
if (accWeightList[^1] <= 0)
{
Debug.LogError("[GetRandomIdxFromWeightList]: Total weight is zero or negative. Returning 0.");
return 0;
}
tmp = UnityRandom.Range(0, accWeightList[^1]);
for (int i = 0; i < accWeightList.Count; i++)
{
if (tmp < accWeightList[i])
return i;
}
return accWeightList.Count - 1;
}
public static int GetRandomIdxFromWeightList(IEnumerable<int> weightList, SystemRandom rng)
{
// Check for null or empty list
if (weightList == null || !weightList.Any())
{
Debug.LogError("[GetRandomIdxFromWeightList]: Weight list is null or empty. Returning 0.");
return 0;
}
var accWeightList = new List<int>();
int tmp = 0;
foreach (int w in weightList)
{
// Check for negative weights
if (w < 0)
{
Debug.LogError("[GetRandomIdxFromWeightList]: Negative weight detected. Returning 0.");
return 0;
}
tmp += w;
accWeightList.Add(tmp);
}
// Check for all zero weights
if (accWeightList[^1] <= 0)
{
Debug.LogError("[GetRandomIdxFromWeightList]: Total weight is zero or negative. Returning 0.");
return 0;
}
tmp = rng.Next(0, accWeightList[^1]);
for (int i = 0; i < accWeightList.Count; i++)
{
if (tmp < accWeightList[i])
return i;
}
return accWeightList.Count - 1;
}
public static int GetRandomIdxFromWeightList(IEnumerable<float> weightList, SystemRandom rng)
{
// Check for null or empty list
if (weightList == null || !weightList.Any())
{
Debug.LogError("[GetRandomIdxFromWeightList]: Weight list is null or empty. Returning 0.");
return 0;
}
var accWeightList = new List<float>();
double tmp = 0d;
foreach (float w in weightList)
{
// Check for negative weights
if (w < 0)
{
Debug.LogError("[GetRandomIdxFromWeightList]: Negative weight detected. Returning 0.");
return 0;
}
tmp += w;
accWeightList.Add((float)tmp);
}
// Check for all zero weights
if (accWeightList[^1] <= 0)
{
Debug.LogError("[GetRandomIdxFromWeightList]: Total weight is zero or negative. Returning 0.");
return 0;
}
tmp = rng.NextDouble() * accWeightList[^1];
for (int i = 0; i < accWeightList.Count; i++)
{
if (tmp < accWeightList[i])
return i;
}
return accWeightList.Count - 1;
}
public static T PickRandomItemWithWeight<T>(IEnumerable<T> items, SystemRandom rng, System.Func<int, float> weightGetter)
{
var weightList = items.Select((x, i) => weightGetter(i)).ToList();
return items.ElementAt(GetRandomIdxFromWeightList(weightList, rng));
}
public static T PickRandomItemWithWeight<T>(IEnumerable<T> items, System.Func<int, int> weightGetter)
{
var weightList = items.Select((x, i) => weightGetter(i)).ToList();
return items.ElementAt(GetRandomIdxFromWeightList(weightList));
}
public static Vector2 CalculateBezierCurve(float t, Vector2 p0, Vector2 p1, Vector2 p2)
{
if (t < 0 || t > 1)
return Vector2.zero;
Vector2 a = Vector2.Lerp(p0, p1, t);
Vector2 b = Vector2.Lerp(p1, p2, t);
return Vector2.Lerp(a, b, t);
}
/// <summary>
/// ("#,##0") Display number with comma, integer part only.
/// </summary>
/// <param name="num"></param>
/// <returns></returns>
public static string GetNumberString(float num)
{
string str;
if (num < 1000)
{
str = num.ToString("#,##0");
}
else if (num < 1000000)
{
str = (num / 1000f).ToString("#,##0") + "K";
}
else if (num < 1000000000)
{
str = (num / 1000000f).ToString("#,##0") + "M";
}
else
{
str = (num / 1000000000f).ToString("#,##0") + "G";
}
return str;
}
public static int[] BitWisePositionScan(int num)
{
var res = new List<int>();
int idx = 0;
while (num != 0)
{
if ((num & 1) == 1)
res.Add(idx);
num >>= 1;
idx++;
}
return res.ToArray();
}
public static void ShuffleList<T>(IList<T> list, System.Random rng)
{
for (int i = list.Count - 1; i > 0; i--)
{
int j = rng.Next(i + 1);
(list[j], list[i]) = (list[i], list[j]);
}
}
/// <summary>
/// Get a random point in a corn-shaped spread.
/// </summary>
/// <param name="spreadHalfAngle">In degree.</param>
/// <param name="spreadNearRange">Close range.</param>
/// <param name="spreadFarRange">Far range.</param>
/// <param name="direction">In radians. Default is upward vertical, 90 degrees.</param>
/// <returns>A random delta</returns>
public static Vector2 GetCornSpread(float spreadHalfAngle, float spreadNearRange, float spreadFarRange, float direction = 90)
{
var halfAngleRadians = Mathf.Deg2Rad * spreadHalfAngle;
var angle = UnityRandom.Range(-halfAngleRadians, halfAngleRadians);
angle += Mathf.Deg2Rad * direction;
var radius = UnityRandom.Range(spreadNearRange, spreadFarRange);
return new Vector2(Mathf.Cos(angle), Mathf.Sin(angle)) * radius;
}
}