feat(core): 新增 WeightedPick 加权随机共享原语(+10 单测),为消除四处平行实现做准备
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
using System.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
using BaseGames.Core;
|
||||
|
||||
namespace BaseGames.Tests.EditMode.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// WeightedPick 加权随机取索引的共享原语测试。
|
||||
/// 该原语被敌人选招 / Boss 选招 / 战利品掉落共用,故对"绝不选中零权重项"等
|
||||
/// 确定性性质做强断言(多次采样),避免任一消费端出现选到不合格项的缺陷。
|
||||
/// </summary>
|
||||
public class WeightedPickTests
|
||||
{
|
||||
static List<float> W(params float[] w) => new List<float>(w);
|
||||
|
||||
[Test]
|
||||
public void Null_ReturnsMinusOne()
|
||||
{
|
||||
Assert.AreEqual(-1, WeightedPick.Index(null));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Empty_ReturnsMinusOne()
|
||||
{
|
||||
Assert.AreEqual(-1, WeightedPick.Index(W()));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AllZero_ReturnsMinusOne()
|
||||
{
|
||||
Assert.AreEqual(-1, WeightedPick.Index(W(0f, 0f, 0f)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AllNegative_ReturnsMinusOne()
|
||||
{
|
||||
// 负权重按 0 处理(非法输入不应造成负总和/错误索引)
|
||||
Assert.AreEqual(-1, WeightedPick.Index(W(-1f, -5f)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SinglePositive_AlwaysThatIndex()
|
||||
{
|
||||
var w = W(0f, 3f, 0f);
|
||||
for (int i = 0; i < 50; i++)
|
||||
Assert.AreEqual(1, WeightedPick.Index(w));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void NeverPicksZeroWeight()
|
||||
{
|
||||
// 索引 0/2 权重为 0,无论抽多少次都不该被选中
|
||||
var w = W(0f, 1f, 0f, 2f);
|
||||
for (int i = 0; i < 200; i++)
|
||||
{
|
||||
int idx = WeightedPick.Index(w);
|
||||
Assert.IsTrue(idx == 1 || idx == 3, $"选中了零权重索引 {idx}");
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void NeverPicksNegativeWeight()
|
||||
{
|
||||
var w = W(-10f, 1f);
|
||||
for (int i = 0; i < 200; i++)
|
||||
Assert.AreEqual(1, WeightedPick.Index(w));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AllPositive_EveryIndexReachable()
|
||||
{
|
||||
// 等权重下,足够多次采样应覆盖所有索引(验证不会恒定只返回首个/末个)
|
||||
var w = W(1f, 1f, 1f);
|
||||
var seen = new HashSet<int>();
|
||||
for (int i = 0; i < 500; i++) seen.Add(WeightedPick.Index(w));
|
||||
CollectionAssert.AreEquivalent(new[] { 0, 1, 2 }, seen);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void HeavyWeight_DominatesDistribution()
|
||||
{
|
||||
// 权重 99:1 时,重权重索引应占绝大多数(宽松阈值,避免随机性造成偶发失败)
|
||||
var w = W(99f, 1f);
|
||||
int heavy = 0;
|
||||
const int N = 1000;
|
||||
for (int i = 0; i < N; i++) if (WeightedPick.Index(w) == 0) heavy++;
|
||||
Assert.Greater(heavy, N * 0.9f, $"重权重仅被选中 {heavy}/{N} 次,分布不符合权重");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void WorksWithArray()
|
||||
{
|
||||
// IReadOnlyList 契约:数组同样可用(消费端可复用缓存数组,免 GC)
|
||||
float[] w = { 0f, 5f };
|
||||
Assert.AreEqual(1, WeightedPick.Index(w));
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user