using System.Collections.Generic; using NUnit.Framework; using BaseGames.Core; namespace BaseGames.Tests.EditMode.Core { /// /// WeightedPick 加权随机取索引的共享原语测试。 /// 该原语被敌人选招 / Boss 选招 / 战利品掉落共用,故对"绝不选中零权重项"等 /// 确定性性质做强断言(多次采样),避免任一消费端出现选到不合格项的缺陷。 /// public class WeightedPickTests { static List W(params float[] w) => new List(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(); 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)); } } }