feat(core): 新增 WeightedPick 加权随机共享原语(+10 单测),为消除四处平行实现做准备
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace BaseGames.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// 加权随机取索引的共享原语(纯逻辑、无状态、可单测)。
|
||||
///
|
||||
/// 项目中"按权重随机选一个"的场景(敌人选招 / Boss 选招 / 战利品掉落)此前各写了一份
|
||||
/// 累加-掷点-回退的循环,语义相同却各自维护。统一到本处,消除平行实现。
|
||||
///
|
||||
/// 调用方职责:把候选的**有效权重**填入一个列表/数组(不合格候选填 0),
|
||||
/// 本原语只负责"按权重挑一个索引",不关心候选是什么、为何不合格。
|
||||
/// 列表可由调用方缓存复用 → 选取过程零 GC 分配。
|
||||
/// </summary>
|
||||
public static class WeightedPick
|
||||
{
|
||||
/// <summary>
|
||||
/// 按 <paramref name="weights"/> 加权随机返回一个索引。
|
||||
/// 权重 ≤ 0 的项永不被选中(负权重按 0 处理);无任何正权重时返回 -1。
|
||||
/// </summary>
|
||||
public static int Index(IReadOnlyList<float> weights)
|
||||
{
|
||||
if (weights == null) return -1;
|
||||
|
||||
int count = weights.Count;
|
||||
float total = 0f;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
float w = weights[i];
|
||||
if (w > 0f) total += w;
|
||||
}
|
||||
if (total <= 0f) return -1;
|
||||
|
||||
float roll = Random.value * total;
|
||||
float accum = 0f;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
float w = weights[i];
|
||||
if (w <= 0f) continue;
|
||||
accum += w;
|
||||
if (roll <= accum) return i;
|
||||
}
|
||||
|
||||
// 浮点累加误差兜底:返回最后一个正权重项(绝不返回零权重项)
|
||||
for (int i = count - 1; i >= 0; i--)
|
||||
if (weights[i] > 0f) return i;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user