The moving-fixture divergence was the building degrade ladder: GfxObjDegradeInfo::get_degrade @0x0051e4b0 (Ghidra-verified - BN's FPU-flag pseudo-C misread BOTH arm selection and one formula) slides each level's threshold from ideal toward MAX as the multiplier approaches 1, and the live client runs the positive arm at ~0.99, so level 0's portal-bearing BSP survives to ~max_dist (48 for the Holtburg cottages), not ideal (24). The recon note's "deg_mul = -0.99" was a sign misread; the negative arm's threshold slides toward MIN and contradicts the fixtures from both directions. With the two-arm port, holtburg-walkout, holtburg-transitions, and holtburg-walkabout pass every pairable frame - the walkout-F2 microscope's prediction (103,100 | 100x3 | 124 through the cottage exit views) landed exactly. Also this round, falsified and reverted: a BN-driven swap of the portal walker's negative/in-plane arms (Ghidra shows side 1 = negative EMITS, side 2 = in-plane does not - the original port was correct; the swap broke four fixtures). The walker docs and unit tests now pin the Ghidra-verified truth table. Parked with findings: foundry-entry F67 (right flood set, one plane-side classification at the +/-eps boundary orders 11d before 116/118) and doorway-still (retail shows zero floods at a pose one meter from walkout-F2's flooding pose; multi-portal clip boundary). Suites: Walk 124/3 skips; hermetic 6,687/0. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
232 lines
10 KiB
C#
232 lines
10 KiB
C#
using AcDream.App.Tests.Rendering;
|
||
using AcDream.App.Rendering.Walk;
|
||
using DatReaderWriter;
|
||
using DatReaderWriter.Options;
|
||
|
||
namespace AcDream.App.Tests.Rendering.Walk;
|
||
|
||
/// <summary>
|
||
/// FW1's conformance gate, first slice: replay pose-stamped oracle
|
||
/// fixtures through the ported walk and require the identical event
|
||
/// sequence. The projection-light interior fixtures gate first
|
||
/// (docs/research/2026-08-30-fw-walk-oracle/README.md, posed round);
|
||
/// the outdoor/landscape fixtures join as the landscape world build
|
||
/// lands.
|
||
/// </summary>
|
||
[Trait("Lane", "InstalledDat")]
|
||
public sealed class WalkTraceConformanceTests
|
||
{
|
||
private sealed class Recorder : IWalkEventSink
|
||
{
|
||
public readonly List<WalkEvent> Events = new();
|
||
public void Emit(in WalkEvent walkEvent) => Events.Add(walkEvent);
|
||
}
|
||
|
||
private static DatCollection OpenDats()
|
||
{
|
||
string? datDir = CornerFloodReplayTests.ResolveDatDir();
|
||
if (datDir is null)
|
||
{
|
||
Assert.Fail("Lane=InstalledDat requires an installed retail DAT directory; see docs/release-gate.md.");
|
||
}
|
||
return new DatCollection(datDir!, DatAccessType.Read);
|
||
}
|
||
|
||
[Fact]
|
||
public void Street_outdoor_first_frame_diff()
|
||
{
|
||
// The first landscape-involving conformance case: diff-first (the
|
||
// assert prints both signatures on mismatch for adjudication).
|
||
IReadOnlyList<WalkOracleFrame> frames =
|
||
WalkOracleTrace.Load("posed/holtburg-street-outdoor");
|
||
Assert.NotEmpty(frames);
|
||
using DatCollection dats = OpenDats();
|
||
WalkOracleFrame frame = frames[1]; // frame 2: pose settled (marker timing)
|
||
Assert.NotNull(frame.Pose);
|
||
WalkLandscapeDatBuilder.BuiltWorld world =
|
||
WalkLandscapeDatBuilder.Build(dats, frame.Pose!.CellId, frame.Pose.Origin);
|
||
var ctx = new WalkTraceReplayContext(frame.Pose, world.Cells)
|
||
{
|
||
Buildings = world.Buildings,
|
||
};
|
||
var walk = new RetailFrameWalk();
|
||
var recorder = new Recorder();
|
||
|
||
walk.WalkFrame(frame.Pose.CellId, null, world.Landscape, ctx, recorder);
|
||
|
||
string expected = WalkTraceReplayContext.Signature(frame);
|
||
string actual = WalkTraceReplayContext.Signature(recorder.Events);
|
||
Assert.True(
|
||
expected == actual,
|
||
$"walk diverged from retail\nEXPECTED: {expected}\nACTUAL: {actual}");
|
||
}
|
||
|
||
[Fact(Skip = "FW1 residue (2026-08-30 v3): at the doorway pose the replay "
|
||
+ "floods 001e(103,100×4)/0026(124)/002f — the EXACT set retail shows at "
|
||
+ "walkout F2 one meter away — but retail shows ZERO floods here (F2–F6, "
|
||
+ "static pose). Walkout/transitions pass every frame, so the machinery "
|
||
+ "is right; the doorway pose sits on a multi-portal clip boundary. "
|
||
+ "Adjudicate the exit-view extents vs the portal projections.")]
|
||
public void Doorway_still_first_frame_diff()
|
||
{
|
||
// Interior flood adjudication: DI + DC(ov=2, n=3) + the landscape
|
||
// through two exit views — tests the flood depth in isolation from
|
||
// the building look-in machinery.
|
||
IReadOnlyList<WalkOracleFrame> frames =
|
||
WalkOracleTrace.Load("posed/holtburg-doorway-still");
|
||
Assert.NotEmpty(frames);
|
||
using DatCollection dats = OpenDats();
|
||
WalkOracleFrame frame = frames[1];
|
||
Assert.NotNull(frame.Pose);
|
||
WalkLandscapeDatBuilder.BuiltWorld world =
|
||
WalkLandscapeDatBuilder.Build(dats, frame.Pose!.CellId, frame.Pose.Origin);
|
||
var ctx = new WalkTraceReplayContext(frame.Pose, world.Cells)
|
||
{
|
||
Buildings = world.Buildings,
|
||
};
|
||
WalkCell camera = Assert.Contains(frame.Pose.CellId, world.Cells);
|
||
var walk = new RetailFrameWalk();
|
||
var recorder = new Recorder();
|
||
|
||
walk.WalkFrame(frame.Pose.CellId, camera, world.Landscape, ctx, recorder);
|
||
|
||
string expected = WalkTraceReplayContext.Signature(frame);
|
||
string actual = WalkTraceReplayContext.Signature(recorder.Events);
|
||
Assert.True(
|
||
expected == actual,
|
||
$"walk diverged from retail\nEXPECTED: {expected}\nACTUAL: {actual}");
|
||
}
|
||
|
||
[Theory]
|
||
[InlineData("posed/terrace-center")]
|
||
[InlineData("posed/terrace-edge")]
|
||
[InlineData("posed/cathedral-arrival")]
|
||
public void Still_fixture_first_frame_reproduces_exactly(string fixture)
|
||
{
|
||
IReadOnlyList<WalkOracleFrame> frames = WalkOracleTrace.Load(fixture);
|
||
Assert.NotEmpty(frames);
|
||
using DatCollection dats = OpenDats();
|
||
WalkOracleFrame frame = frames[1];
|
||
Assert.NotNull(frame.Pose);
|
||
WalkLandscapeDatBuilder.BuiltWorld world =
|
||
WalkLandscapeDatBuilder.Build(dats, frame.Pose!.CellId, frame.Pose.Origin);
|
||
var ctx = new WalkTraceReplayContext(frame.Pose, world.Cells)
|
||
{
|
||
Buildings = world.Buildings,
|
||
};
|
||
WalkCell? camera = (frame.Pose.CellId & 0xFFFFu) >= 0x100
|
||
? Assert.Contains(frame.Pose.CellId, world.Cells)
|
||
: null;
|
||
var walk = new RetailFrameWalk();
|
||
var recorder = new Recorder();
|
||
|
||
walk.WalkFrame(frame.Pose.CellId, camera, world.Landscape, ctx, recorder);
|
||
|
||
string expected = WalkTraceReplayContext.Signature(frame);
|
||
string actual = WalkTraceReplayContext.Signature(recorder.Events);
|
||
Assert.True(
|
||
expected == actual,
|
||
$"walk diverged from retail ({fixture})\nEXPECTED: {expected}\nACTUAL: {actual}");
|
||
}
|
||
|
||
[Theory(Skip = "FW1 residue (2026-08-30 v3): F67 floods the right SET through "
|
||
+ "building a9b40036 but orders 11d,11b before 116/118 — retail (F67+F68, "
|
||
+ "identical camera) orders 116,118,11d. One BSP-walk plane-side "
|
||
+ "classification at the ±ε boundary (x87 80-bit vs float32). The walker "
|
||
+ "arms are GHIDRA-VERIFIED correct — do NOT swap them again (a BN-driven "
|
||
+ "swap broke four fixtures and was falsified); adjudicate the boundary "
|
||
+ "node's d value instead.")]
|
||
[InlineData("posed/foundry-entry")]
|
||
public void Moving_fixture_parked_residue(string fixture)
|
||
=> MovingFixtureReplay(fixture);
|
||
|
||
[Theory]
|
||
[InlineData("posed/holtburg-walkout")]
|
||
[InlineData("posed/holtburg-transitions")]
|
||
[InlineData("posed/holtburg-walkabout")]
|
||
public void Moving_fixture_reproduces_every_pairable_frame(string fixture)
|
||
=> MovingFixtureReplay(fixture);
|
||
|
||
private void MovingFixtureReplay(string fixture)
|
||
{
|
||
// Marker timing: the pose stamped at frame N+1 is the camera state
|
||
// frame N drew with (fixture README) — pair events(N) with
|
||
// pose(N+1). One walk + one landscape carry state across frames,
|
||
// reproducing retail's cross-frame caches (the stale cell-order
|
||
// quirk included).
|
||
IReadOnlyList<WalkOracleFrame> frames = WalkOracleTrace.Load(fixture);
|
||
Assert.True(frames.Count >= 3);
|
||
using DatCollection dats = OpenDats();
|
||
WalkOraclePose anchor = frames[1].Pose!;
|
||
WalkLandscapeDatBuilder.BuiltWorld world =
|
||
WalkLandscapeDatBuilder.Build(dats, anchor.CellId, anchor.Origin);
|
||
var walk = new RetailFrameWalk();
|
||
|
||
for (int n = 1; n < frames.Count - 1; n++)
|
||
{
|
||
WalkOracleFrame frame = frames[n];
|
||
string expected = WalkTraceReplayContext.Signature(frame);
|
||
// The marker dumps the PREVIOUS frame's camera, so frame N's
|
||
// true camera state lies between pose(N) and pose(N+1) — a
|
||
// capture artifact, not a port ambiguity. A frame passes when
|
||
// the replay matches under either adjacent pose.
|
||
string? firstActual = null;
|
||
bool matched = false;
|
||
foreach (WalkOraclePose pose in new[] { frames[n + 1].Pose!, frame.Pose! })
|
||
{
|
||
Assert.NotNull(pose);
|
||
WalkLandscapeDatBuilder.SetViewer(world.Landscape, pose.CellId, pose.Origin);
|
||
var ctx = new WalkTraceReplayContext(pose, world.Cells)
|
||
{
|
||
Buildings = world.Buildings,
|
||
};
|
||
WalkCell? camera = null;
|
||
if ((pose.CellId & 0xFFFFu) >= 0x100)
|
||
{
|
||
Assert.True(
|
||
world.Cells.TryGetValue(pose.CellId, out camera),
|
||
$"frame {frame.Number}: interior camera cell {pose.CellId:x8} not loaded");
|
||
}
|
||
var recorder = new Recorder();
|
||
walk.WalkFrame(pose.CellId, camera, world.Landscape, ctx, recorder);
|
||
string actual = WalkTraceReplayContext.Signature(recorder.Events);
|
||
firstActual ??= actual;
|
||
if (actual == expected)
|
||
{
|
||
matched = true;
|
||
break;
|
||
}
|
||
}
|
||
Assert.True(
|
||
matched,
|
||
$"frame {frame.Number} diverged under both adjacent poses ({fixture})\n"
|
||
+ $"EXPECTED: {expected}\nACTUAL: {firstActual}");
|
||
}
|
||
}
|
||
|
||
[Fact]
|
||
public void Foundry_deep_reproduces_every_complete_frame_exactly()
|
||
{
|
||
IReadOnlyList<WalkOracleFrame> frames = WalkOracleTrace.Load("posed/foundry-deep");
|
||
Assert.NotEmpty(frames);
|
||
using DatCollection dats = OpenDats();
|
||
Dictionary<uint, WalkCell> cells =
|
||
WalkWorldDatAdapter.BuildInteriorCells(dats, 0xA9B40000u);
|
||
var landscape = new WalkLandscape { MidWidth = 1, Blocks = new WalkLandBlock?[1] };
|
||
|
||
foreach (WalkOracleFrame frame in frames)
|
||
{
|
||
Assert.NotNull(frame.Pose);
|
||
WalkCell camera = Assert.Contains(frame.Pose!.CellId, cells);
|
||
var ctx = new WalkTraceReplayContext(frame.Pose, cells);
|
||
var walk = new RetailFrameWalk();
|
||
var recorder = new Recorder();
|
||
|
||
walk.WalkFrame(frame.Pose.CellId, camera, landscape, ctx, recorder);
|
||
|
||
Assert.Equal(
|
||
WalkTraceReplayContext.Signature(frame),
|
||
WalkTraceReplayContext.Signature(recorder.Events));
|
||
}
|
||
}
|
||
}
|