Delete the callerless portal-BFS research graph and spent renderer probe families while retaining the production RetailFrameWalk path, terrain diagnostics, membership invariant, and walk transcript. Mutation evidence (all restored): 1. Restored PortalVisibilityBuilder type -> AppAssembly_ContainsNoSupersededPortalGraphTypes first failed Assert.Empty with AcDream.App.Rendering.PortalVisibilityBuilder. 2. Restored ACDREAM_PROBE_FACILITY_STAIRS -> ProductionSource_ContainsNoDeletedRendererProbe_AndRetainsWalkTranscriptProof first failed Assert.Empty on RenderingDiagnostics.cs. 3. Added a second RetailFrameWalk field -> WalkFrameOwners_AreUnique first failed Assert.Single with _frameWalk and _mutatedSecondFrameWalk. 4. Added OrderBy to OrderedStream -> OrderedWalkStream_HasNoCrossStreamReorder first failed Assert.DoesNotContain on OrderBy(. 5. Added IDatReaderWriter parameter -> FrameTimeWalkOwners_HaveNoRawDatDependency first failed Assert.Empty on RetailFrameWalk.MutatedRawDatParameter.
482 lines
24 KiB
C#
482 lines
24 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 partial class WalkTraceConformanceTests
|
||
{
|
||
/// <summary>S3 chunk 1 fix round 1 (G10): besides the plain
|
||
/// <see cref="WalkEvent"/> list the four-kind
|
||
/// <see cref="WalkTraceReplayContext.Signature(IEnumerable{WalkEvent})"/>
|
||
/// still uses, also builds the full eight-kind token stream — LS/BLD/
|
||
/// DI/DC/LC/SC/EC/OC — AS each hook fires, for
|
||
/// <see cref="WalkTraceReplayContext.Signature8(IReadOnlyList{string})"/>.
|
||
/// <see cref="_lookIn"/> mirrors <c>WalkFrameDriver</c>'s own private
|
||
/// <c>_currentDcStage</c>: null until the frame's first
|
||
/// <see cref="WalkEventKind.DrawInside"/> (the interior root's own
|
||
/// flood defers its EC/OC to <see cref="OnInteriorFloodDrawTurn"/>),
|
||
/// flips true forever once a building's portal pass begins
|
||
/// (<see cref="OnBuildingTurn"/>) — see
|
||
/// <see cref="WalkTraceReplayContext.AppendFloodTurns"/>'s own doc
|
||
/// comment for why this never resets mid-frame. S3 chunk 1 fix round 2
|
||
/// (§11.6 H3): this REPLAY-side derivation is unchanged — only the
|
||
/// ORACLE side (<see cref="WalkTraceReplayContext.Signature8(WalkOracleFrame)"/>)
|
||
/// moved off derivation onto its own literal captured EC/OC lines.
|
||
///
|
||
/// <para>KNOWN LIMIT (S3 review fix round 1, F4a): the production
|
||
/// transcript prints <c>EC</c> once per LIVE VIEW of a flood cell
|
||
/// (retail's <c>setup_view</c>/<c>DrawEnvCell</c> loop,
|
||
/// <c>PView::DrawCells</c> @0x005a4ab1–0x005a4acc), but this replay
|
||
/// side's <see cref="WalkTraceReplayContext.AppendFloodTurns"/> still
|
||
/// derives ONE <c>EC</c> per cell — the sink event carries no per-cell
|
||
/// view count. Every fixture passes only because no captured flood cell
|
||
/// holds more than one live view at DrawCells time. A fixture with a
|
||
/// two-view flood cell would fail here on the second <c>EC</c>; S5's
|
||
/// transcript closeout carries the per-cell view count through the
|
||
/// sink event and removes this note.</para></summary>
|
||
private sealed class Recorder : IWalkEventSink
|
||
{
|
||
public readonly List<WalkEvent> Events = new();
|
||
public readonly List<string> Tokens = new();
|
||
|
||
private bool? _lookIn;
|
||
|
||
public void Emit(in WalkEvent walkEvent)
|
||
{
|
||
Events.Add(walkEvent);
|
||
switch (walkEvent.Kind)
|
||
{
|
||
case WalkEventKind.Landscape:
|
||
Tokens.Add("LS");
|
||
break;
|
||
case WalkEventKind.Building:
|
||
Tokens.Add($"BLD:{walkEvent.CellId:x8}");
|
||
break;
|
||
case WalkEventKind.DrawInside:
|
||
Tokens.Add($"DI:{walkEvent.CellId:x8}");
|
||
_lookIn = false;
|
||
break;
|
||
case WalkEventKind.DrawCells:
|
||
Tokens.Add(
|
||
$"DC:ov={walkEvent.OutsideViewCount}:"
|
||
+ string.Join(',', walkEvent.Cells.Select(c => c.ToString("x8"))));
|
||
// A look-in flood's DC draws its EC/OC immediately
|
||
// (RetailFrameWalk.HandleDrawCellsTurn calls
|
||
// EmitFloodTurns right here for WalkDrawStage.
|
||
// LookInStatic); the interior root's OWN flood (stage
|
||
// CellStatic, _lookIn still false) defers to
|
||
// OnInteriorFloodDrawTurn below — see that method.
|
||
if (_lookIn == true)
|
||
WalkTraceReplayContext.AppendFloodTurns(Tokens, walkEvent.Cells);
|
||
break;
|
||
}
|
||
}
|
||
|
||
public void OnLandCellTurn(uint landblockId, int sideCellCount, int cellIndex)
|
||
=> Tokens.Add(
|
||
$"LC:{WalkTranscriptDump.LodCellId(landblockId, sideCellCount, cellIndex):x8}");
|
||
|
||
public void OnSortCellTurn(uint landblockId, int sideCellCount, int cellIndex)
|
||
=> Tokens.Add(
|
||
$"SC:{WalkTranscriptDump.LodCellId(landblockId, sideCellCount, cellIndex):x8}");
|
||
|
||
public void OnBuildingTurn(WalkBuilding building) => _lookIn = true;
|
||
|
||
public void OnInteriorFloodDrawTurn(IReadOnlyList<uint> cells, int outsideViewCount)
|
||
=> WalkTraceReplayContext.AppendFloodTurns(Tokens, cells);
|
||
|
||
/// <summary>S3 review fix round 1 (F4b): retail's trailing per-frame
|
||
/// weather turn (<c>GameSky::Draw(sky,1)</c> @0x00506ff0, fired by
|
||
/// <c>RetailFrameWalk.DrawLandscape</c> when <c>ctx.WeatherGateOpen</c>
|
||
/// holds) prints its own "OC" line
|
||
/// (<c>WalkFrameDriver.OnWeatherTurn</c>'s production sibling) —
|
||
/// before this fix the interface's default no-op left the replay
|
||
/// side of the eight-kind signature silently blind to it, so
|
||
/// <see cref="WalkTraceReplayContext.Signature8(WalkOracleFrame)"/>
|
||
/// had to drop the SAME line from the oracle side just to keep both
|
||
/// sides comparable (a pin that could never fail on the weather
|
||
/// turn's own placement/value). Recording it here the SAME way
|
||
/// <see cref="Emit"/> records every other "OC" (identical token
|
||
/// shape) lets the oracle side compare literally instead.</summary>
|
||
public void OnWeatherTurn(uint viewerCellId) => Tokens.Add($"OC:{viewerCellId:x8}");
|
||
}
|
||
|
||
private static DatCollection OpenDats()
|
||
{
|
||
string? datDir = InstalledDatTestPath.Resolve();
|
||
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(100f, 0.99f);
|
||
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]
|
||
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);
|
||
// Render::deg_mul is DYNAMIC (auto-tuned by frame load), and this
|
||
// capture ran right after the heavy terrace-edge capture with the
|
||
// multiplier depressed: at mul ≤ 0 the degrade thresholds sit at or
|
||
// below each level's ideal, so 001e (eff 29.3), 0026 (27.1), and
|
||
// 002f (43.4) all select the portless level 1 — retail's zero
|
||
// look-in floods. Every other fixture pins ≈ +0.99 (thresholds at
|
||
// max). The recon session's live dump read −0.99 under the same cdb
|
||
// load. Re-dump deg_mul per capture at the next retail session.
|
||
var walk = new RetailFrameWalk(100f, 0f);
|
||
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}");
|
||
}
|
||
|
||
/// <summary>
|
||
/// PINNED RED 2026-09-03 (S3 chunk 1 fix round 1, G10 — un-skip with the
|
||
/// fix): the eight-kind signature is the FIRST comparison ever run at
|
||
/// LC/SC placement/content against a DAT-built world for this fixture
|
||
/// (the old four-kind Signature filtered LC/SC out entirely — how the
|
||
/// G7 SC-ordering regression shipped green). One real divergence
|
||
/// survives after fixing the LandblockId=0 fixture bug this same round
|
||
/// uncovered (WalkLandscapeDatBuilder.cs — a genuinely separate,
|
||
/// already-fixed defect): at token index 165, the replay draws ONE
|
||
/// EXTRA land-cell pair retail never does —
|
||
/// <c>…SC:a8c90001|LC:a8c90001|SC:a8c90001|</c> retail jumps straight to
|
||
/// <c>LC:95c60001</c>; the replay inserts <c>LC:a9c90001|SC:a9c90001</c>
|
||
/// first. A single spurious block admission at a ring-2/ring-3 LOD
|
||
/// boundary near this pose — a real <c>WalkLandscape.CheckBlocks</c>
|
||
/// visibility question, not a print-site or ordering bug, and out of
|
||
/// this round's print-only scope (§11.4) — needs the retail oracle
|
||
/// (decomp/cdb) to resolve. Filed as docs/ISSUES.md #458.
|
||
/// terrace-edge/cathedral-arrival/foundry-deep all reproduce EXACTLY at
|
||
/// this same eight-kind level; only this row's specific pose hits the
|
||
/// boundary. S3 chunk 1 fix round 2 (§11.6 H3) switched the ORACLE side
|
||
/// of the eight-kind signature from a derived EC/OC to retail's own
|
||
/// LITERAL captured EC/OC lines — re-verified 2026-09-03 that this
|
||
/// divergence stays at the SAME token index 165 (an LC/SC-only mismatch,
|
||
/// upstream of any EC/OC content) under the new comparison; #458's
|
||
/// position is unchanged.
|
||
///
|
||
/// <para>RESOLVED 2026-09-03 (lead, two live cdb captures — see
|
||
/// docs/ISSUES.md #458 and register row AD-118): it is a PRECISION
|
||
/// BOUNDARY, not a structural walk divergence. At this fixture pose the
|
||
/// replay's block test for <c>a9c9</c> (ring 21 north, z slab 75..330)
|
||
/// has three of the four block corners OUTSIDE the doorway's fourth
|
||
/// edge plane and the south-west corner just INSIDE it, so
|
||
/// <c>block_plane_check</c> says PartiallyInside and the block draws;
|
||
/// retail's plane sits about 0.5% away (measured at a nearby pose:
|
||
/// retail's clip heights 300.4/310.2 m vs the replay's 298.8/308.5 m
|
||
/// for the same block, with the SAME sentinel pattern and the SAME
|
||
/// verdicts 1 then 0 in the two views) and puts that last corner
|
||
/// outside too. The row stays KnownFailure on purpose: the fixture is
|
||
/// retail's literal frame, and no bit-exact fix exists short of
|
||
/// reproducing D3D's x87 projection.</para>
|
||
/// </summary>
|
||
[Fact]
|
||
[Trait("Status", "KnownFailure")]
|
||
public void Oh_doorway_still_first_frame_diff()
|
||
{
|
||
// S3 chunk 1 (§11.2 B3): the OH kit pose's own doorway-still capture
|
||
// — DI a9b4013f, DC(ov=2, n=3) per §6b. Mirrors
|
||
// Doorway_still_first_frame_diff's own structure (this pose needs
|
||
// the interior camera cell, unlike the plain outdoor/theory rows).
|
||
// S3 chunk 1 fix round 1 (G6): kept as its own Fact rather than
|
||
// folded into Still_fixture_first_frame_reproduces_exactly's Theory
|
||
// — it and Oh_foundry_deep_... predate that Theory's OH rows and
|
||
// mirror their own FW0 siblings' structure; either shape works, no
|
||
// functional reason blocks merging them later.
|
||
IReadOnlyList<WalkOracleFrame> frames = WalkOracleTrace.Load(
|
||
"docs/research/2026-09-01-overhaul/oh-capture", "holtburg-doorway-still.walk");
|
||
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(100f, 0.99f);
|
||
var recorder = new Recorder();
|
||
|
||
walk.WalkFrame(frame.Pose.CellId, camera, world.Landscape, ctx, recorder);
|
||
|
||
// S3 chunk 1 fix round 1 (G10): the OH kit pose carries LC/SC/EC/OC
|
||
// — compare at the full eight-kind level (see Signature8's own doc
|
||
// comment).
|
||
string expected = WalkTraceReplayContext.Signature8(frame);
|
||
string actual = WalkTraceReplayContext.Signature8(recorder.Tokens);
|
||
Assert.True(
|
||
expected == actual,
|
||
$"walk diverged from retail\nEXPECTED: {expected}\nACTUAL: {actual}");
|
||
}
|
||
|
||
private const string FwOracleRoot = "docs/research/2026-08-30-fw-walk-oracle";
|
||
|
||
/// <summary>S3 chunk 1 (§11.2 B3): the OH capture directory — its OWN
|
||
/// pose-stamped kit-pose captures, a DIFFERENT root than the FW0 still
|
||
/// fixtures above.</summary>
|
||
private const string OhCaptureRoot = "docs/research/2026-09-01-overhaul/oh-capture";
|
||
|
||
[Theory]
|
||
[InlineData(FwOracleRoot, "posed/terrace-center")]
|
||
[InlineData(FwOracleRoot, "posed/terrace-edge")]
|
||
[InlineData(FwOracleRoot, "posed/cathedral-arrival")]
|
||
// S3 chunk 1 (§11.2 B3): the OH kit poses, as NEW rows — the OH
|
||
// cathedral-arrival root is f4180108, NOT FW0's f4180106 (a new pose,
|
||
// not a replacement of the FW0 row above).
|
||
[InlineData(OhCaptureRoot, "terrace-edge.walk")]
|
||
[InlineData(OhCaptureRoot, "cathedral-arrival.walk")]
|
||
public void Still_fixture_first_frame_reproduces_exactly(string root, string fixture)
|
||
{
|
||
IReadOnlyList<WalkOracleFrame> frames = WalkOracleTrace.Load(root, 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(100f, 0.99f);
|
||
var recorder = new Recorder();
|
||
|
||
walk.WalkFrame(frame.Pose.CellId, camera, world.Landscape, ctx, recorder);
|
||
|
||
// S3 chunk 1 fix round 1 (G10): the OH kit-pose rows carry
|
||
// LC/SC/EC/OC — compare THOSE two rows at the full eight-kind level
|
||
// (Signature8's own doc comment); the FW0 rows have none of those
|
||
// lines at all (captured before those breakpoints existed), so they
|
||
// stay on the original four-kind comparison.
|
||
string expected = root == OhCaptureRoot
|
||
? WalkTraceReplayContext.Signature8(frame)
|
||
: WalkTraceReplayContext.Signature(frame);
|
||
string actual = root == OhCaptureRoot
|
||
? WalkTraceReplayContext.Signature8(recorder.Tokens)
|
||
: WalkTraceReplayContext.Signature(recorder.Events);
|
||
Assert.True(
|
||
expected == actual,
|
||
$"walk diverged from retail ({fixture})\nEXPECTED: {expected}\nACTUAL: {actual}");
|
||
}
|
||
|
||
[Fact]
|
||
public void Foundry_entry_reproduces_every_frame_before_the_f67_order_segment()
|
||
{
|
||
// F67–F79 (the fixture's terminal standing segment) diverge in ONE
|
||
// aspect: the flood SET through building a9b40036 is exact, but
|
||
// retail orders its DCs 116,118,11d where the replay orders
|
||
// 11d,116,118. The microscope isolates the flip to the ROOT plane
|
||
// of 0036's drawing BSP (N=(0,0,−1) D=2.8): the replay's
|
||
// building-local eye z is 2.33 (d=+0.47 → NEG-subtree first), while
|
||
// retail behaves as d<0 (POS-first) — a structural ~0.5 m frame
|
||
// question (positionPush(2) / part-scale semantics), only
|
||
// adjudicable by a live cdb dump of FrameCurrent->viewer.viewpoint
|
||
// during 0036's build_draw_portals_only (script:
|
||
// tools/walk-oracle/fw1-f67-viewpoint-probe.cdb). Frames 1–66 must
|
||
// reproduce exactly.
|
||
MovingFixtureReplay("posed/foundry-entry", stopBeforeFrame: 67);
|
||
}
|
||
|
||
[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, int stopBeforeFrame = int.MaxValue)
|
||
{
|
||
// 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(100f, 0.99f);
|
||
|
||
for (int n = 1; n < frames.Count - 1; n++)
|
||
{
|
||
WalkOracleFrame frame = frames[n];
|
||
if (frame.Number >= stopBeforeFrame) break;
|
||
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(100f, 0.99f);
|
||
var recorder = new Recorder();
|
||
|
||
walk.WalkFrame(frame.Pose.CellId, camera, landscape, ctx, recorder);
|
||
|
||
Assert.Equal(
|
||
WalkTraceReplayContext.Signature(frame),
|
||
WalkTraceReplayContext.Signature(recorder.Events));
|
||
}
|
||
}
|
||
|
||
[Fact]
|
||
public void Oh_foundry_deep_reproduces_every_complete_frame_exactly()
|
||
{
|
||
// S3 chunk 1 (§11.2 B3): the OH kit pose's own foundry-deep capture
|
||
// — DI a9b40176, DC(ov=1, n=2), 12 town buildings drawn through the
|
||
// surviving exit chain (§6b). UNLIKE the FW0 sibling above, this
|
||
// capture's own retail transcript shows real BLD content at that
|
||
// depth, so the stub 1x1 landscape (which has no blocks/buildings to
|
||
// walk at all) undershoots it — first divergence, run without the
|
||
// fix below: "DI:a9b40176|DC:ov=1:a9b40176,a9b40177|LS" (nothing
|
||
// after LS) vs retail's "…|LS|BLD:a9b40031|BLD:a9b…" (12 real
|
||
// buildings). The full landscape/building assembler
|
||
// (WalkLandscapeDatBuilder.Build — the SAME one the shared theory
|
||
// and Oh_doorway_still_first_frame_diff use) reproduces them; the
|
||
// camera is stationary across this still pose, so one build serves
|
||
// every frame in the loop, matching the FW0 sibling's "build once"
|
||
// shape.
|
||
IReadOnlyList<WalkOracleFrame> frames = WalkOracleTrace.Load(
|
||
"docs/research/2026-09-01-overhaul/oh-capture", "foundry-deep.walk");
|
||
Assert.NotEmpty(frames);
|
||
using DatCollection dats = OpenDats();
|
||
Assert.NotNull(frames[0].Pose);
|
||
WalkLandscapeDatBuilder.BuiltWorld world = WalkLandscapeDatBuilder.Build(
|
||
dats, frames[0].Pose!.CellId, frames[0].Pose!.Origin);
|
||
|
||
foreach (WalkOracleFrame frame in frames)
|
||
{
|
||
Assert.NotNull(frame.Pose);
|
||
WalkCell camera = Assert.Contains(frame.Pose!.CellId, world.Cells);
|
||
var ctx = new WalkTraceReplayContext(frame.Pose, world.Cells)
|
||
{
|
||
Buildings = world.Buildings,
|
||
};
|
||
var walk = new RetailFrameWalk(100f, 0.99f);
|
||
var recorder = new Recorder();
|
||
|
||
walk.WalkFrame(frame.Pose.CellId, camera, world.Landscape, ctx, recorder);
|
||
|
||
// S3 chunk 1 fix round 1 (G10): full eight-kind comparison —
|
||
// see Signature8's own doc comment.
|
||
Assert.Equal(
|
||
WalkTraceReplayContext.Signature8(frame),
|
||
WalkTraceReplayContext.Signature8(recorder.Tokens));
|
||
}
|
||
}
|
||
}
|