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:
gamer147
2026-06-06 10:33:59 -04:00
parent 201158db5d
commit c47f8d9fa7
3 changed files with 88 additions and 1 deletions

View File

@@ -0,0 +1,42 @@
using Wizard.BattleMgr;
namespace SVSim.BattleEngine.Rng
{
// The headless authoritative single-battle mgr. Overrides the three BattleManagerBase RNG methods
// (now virtual per the Task-4 DP5 patch) to delegate to an injected IRandomSource instead of the
// IsForecast-gated System.Random fields. This is the F2 decoupling: VFX stays suppressed
// (IsForecast == true) while RNG rolls real. The skill RNG path calls BattleManagerBase.GetIns()
// .StableRandom*, and the base ctor registers `this` as the singleton, so constructing the battle as
// HeadlessBattleMgr makes every roll dispatch (virtually) to these overrides.
//
// randomResult is set inside the overrides (it has a protected setter, reachable only from a
// subclass — NOT from RandomSourceBridge); it is read by the Phase-2 NetworkSkill_cost_change emit
// path, so the overrides keep it faithful. The arithmetic itself lives in RandomSourceBridge so it
// stays unit-testable and reusable by a future NetworkBattleManagerBase-derived mgr.
public sealed class HeadlessBattleMgr : SingleBattleMgr
{
private readonly IRandomSource _rng;
public HeadlessBattleMgr(IBattleMgrContentsCreator contentsCreator, IRandomSource rng = null)
: base(contentsCreator)
{
_rng = rng ?? new SeededRandomSource(contentsCreator.RandomSeed);
}
public override int StableRandom(int val)
{
double unit = _rng.NextUnit();
randomResult = unit;
return RandomSourceBridge.Range(val, unit);
}
public override double StableRandomDouble()
{
double unit = _rng.NextUnit();
randomResult = unit;
return unit;
}
public override int StableRandomOnlySelf(int val) => _rng.NextSelf(val);
}
}

View File

@@ -4,7 +4,7 @@ namespace SVSim.BattleEngine.Rng
{
// The ONE place engine roll-logic is re-authored (the virtual-override seam restates it rather than
// body-patching the Engine file). Isolated here so it is unit-testable and pinned to the verbatim
// engine by the parity test (RngSeamTests.SeededSource_matches_engine_generator / Task 5). Mirrors
// engine by the parity test (RngSeamTests.Default_source_matches_engine_generator_and_formula). Mirrors
// BattleManagerBase.StableRandom: `(int)Math.Floor((double)val * unit)`.
public static class RandomSourceBridge
{