feat(rng-seam): HeadlessBattleMgr override + decoupling/parity tests (F2 resolved)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
using System;
|
||||
using NUnit.Framework;
|
||||
using SVSim.BattleEngine.Rng;
|
||||
using Wizard;
|
||||
using Wizard.Battle;
|
||||
|
||||
namespace SVSim.BattleEngine.Tests
|
||||
{
|
||||
@@ -51,5 +53,48 @@ namespace SVSim.BattleEngine.Tests
|
||||
Assert.That(src.NextSelf(99), Is.EqualTo(3));
|
||||
Assert.That(() => src.NextSelf(99), Throws.InvalidOperationException, "should throw on self overrun");
|
||||
}
|
||||
|
||||
// The decoupling (F2): the override must roll REAL values even though IsForecast == true (which
|
||||
// forces the un-overridden engine methods to return 0). A ScriptedRandomSource proves the value
|
||||
// came from the injected source, not the engine's zeroing.
|
||||
[Test]
|
||||
public void Override_rolls_real_values_under_IsForecast()
|
||||
{
|
||||
HeadlessEngineEnv.EnsureInitialized();
|
||||
BattleManagerBase.IsForecast = true; // would zero the un-overridden engine RNG
|
||||
|
||||
// 3 units; with RandomSourceBridge.Range(val, unit) = floor(val*unit):
|
||||
// StableRandom(7) with 0.5 -> floor(3.5) = 3
|
||||
// StableRandomDouble() -> 0.25
|
||||
// StableRandomOnlySelf(10) -> scripted self pick 4
|
||||
var src = new ScriptedRandomSource(new[] { 0.5, 0.25 }, new[] { 4 });
|
||||
var mgr = new HeadlessBattleMgr(new HeadlessContentsCreator(), src);
|
||||
|
||||
Assert.That(mgr.StableRandom(7), Is.EqualTo(3), "StableRandom did not use the injected source");
|
||||
Assert.That(mgr.randomResult, Is.EqualTo(0.5), "StableRandom must set randomResult to the rolled unit");
|
||||
Assert.That(mgr.StableRandomDouble(), Is.EqualTo(0.25), "StableRandomDouble did not use the injected source");
|
||||
Assert.That(mgr.randomResult, Is.EqualTo(0.25), "StableRandomDouble must set randomResult");
|
||||
Assert.That(mgr.StableRandomOnlySelf(10), Is.EqualTo(4), "StableRandomOnlySelf did not use the injected source");
|
||||
}
|
||||
|
||||
// Parity: with the DEFAULT (seeded) source, HeadlessBattleMgr.StableRandom must equal what the
|
||||
// verbatim engine would compute — floor(val * new System.Random(seed).NextDouble()) — pinning the
|
||||
// re-authored RandomSourceBridge arithmetic to the engine's own formula+generator. (The default
|
||||
// source seeds from HeadlessContentsCreator.RandomSeed == 12345.)
|
||||
[Test]
|
||||
public void Default_source_matches_engine_generator_and_formula()
|
||||
{
|
||||
HeadlessEngineEnv.EnsureInitialized();
|
||||
BattleManagerBase.IsForecast = true;
|
||||
|
||||
var mgr = new HeadlessBattleMgr(new HeadlessContentsCreator()); // default SeededRandomSource(12345)
|
||||
var reference = new System.Random(12345);
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
int expected = (int)System.Math.Floor(7 * reference.NextDouble());
|
||||
Assert.That(mgr.StableRandom(7), Is.EqualTo(expected), $"parity drift at roll {i}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user