From ec78beb84321fd9a08a1cb90b8f6bd0fc5cbbf9f Mon Sep 17 00:00:00 2001 From: Erik Date: Wed, 3 Jun 2026 14:21:44 +0200 Subject: [PATCH] =?UTF-8?q?test(p0):=20find=5Fcell=5Flist=20golden=20confo?= =?UTF-8?q?rmance=20=E2=80=94=20unambiguous=20interior=20picks?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit P0 Task 4. FindCellList resolves a sphere deep inside room 0171 -> 0171, deep inside vestibule 0170 -> 0170, and re-picks 0170 from a stale 0171 seed (membership re-picks by containment, not the seed). Retail-faithful by construction (candidate cells loaded from the real dats). The subtle doorway-threshold pick is the trace-backed golden (Task 6). Co-Authored-By: Claude Opus 4.8 (1M context) --- .../FindCellListConformanceTests.cs | 87 +++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 tests/AcDream.Core.Tests/Conformance/FindCellListConformanceTests.cs diff --git a/tests/AcDream.Core.Tests/Conformance/FindCellListConformanceTests.cs b/tests/AcDream.Core.Tests/Conformance/FindCellListConformanceTests.cs new file mode 100644 index 0000000..8d09c0c --- /dev/null +++ b/tests/AcDream.Core.Tests/Conformance/FindCellListConformanceTests.cs @@ -0,0 +1,87 @@ +using System.Numerics; +using AcDream.Core.Physics; +using DatReaderWriter; +using DatReaderWriter.Options; +using Xunit; + +namespace AcDream.Core.Tests.Conformance; + +/// +/// P0 Task 4 — golden conformance for the membership pick +/// (retail CObjCell::find_cell_list +/// @ 0x52b4e0 pc:308742). The unambiguous cases are retail-faithful by +/// construction: the candidate cells are loaded from the same dats retail +/// loads, and a sphere clearly inside one room can only resolve to that +/// room. The subtle doorway-threshold pick (the 0031↔0170↔0171 ping-pong) +/// is the trace-backed golden in FindCellListConformanceTests (Task 6). +/// +public class FindCellListConformanceTests +{ + private const float FootRadius = 0.4f; // player foot-sphere radius + + /// Cache the whole threshold building (016F..0175) so portal expansion is faithful. + private static PhysicsDataCache LoadThresholdBuilding(DatCollection dats) + { + var cache = new PhysicsDataCache(); + for (uint low = 0x016Fu; low <= 0x0175u; low++) + ConformanceDats.LoadEnvCell(dats, cache, ConformanceDats.HoltburgLandblock | low); + return cache; + } + + [Fact] + public void FindCellList_DeepInsideRoom0171_Returns0171() + { + var datDir = ConformanceDats.ResolveDatDir(); + if (datDir is null) return; + using var dats = new DatCollection(datDir, DatAccessType.Read); + var cache = LoadThresholdBuilding(dats); + + var world = Vector3.Transform( + CottageDoorwayCharacterizationTests.Interior0171Local, + cache.GetCellStruct(CottageDoorwayCharacterizationTests.Room0171)!.WorldTransform); + + uint picked = CellTransit.FindCellList(cache, world, FootRadius, + CottageDoorwayCharacterizationTests.Room0171); + Assert.Equal(CottageDoorwayCharacterizationTests.Room0171, picked); + } + + [Fact] + public void FindCellList_DeepInsideVestibule0170_Returns0170() + { + var datDir = ConformanceDats.ResolveDatDir(); + if (datDir is null) return; + using var dats = new DatCollection(datDir, DatAccessType.Read); + var cache = LoadThresholdBuilding(dats); + + var world = Vector3.Transform( + CottageDoorwayCharacterizationTests.Interior0170Local, + cache.GetCellStruct(CottageDoorwayCharacterizationTests.Vestibule0170)!.WorldTransform); + + uint picked = CellTransit.FindCellList(cache, world, FootRadius, + CottageDoorwayCharacterizationTests.Vestibule0170); + Assert.Equal(CottageDoorwayCharacterizationTests.Vestibule0170, picked); + } + + /// + /// Seeding from the WRONG current cell (room 0171) while standing deep inside + /// the vestibule (0170) must still resolve to 0170 — find_cell_list re-picks by + /// containment, it does not trust the stale seed. This is the membership-stability + /// property P1 must preserve. + /// + [Fact] + public void FindCellList_InVestibule_SeededFromRoom_Returns0170() + { + var datDir = ConformanceDats.ResolveDatDir(); + if (datDir is null) return; + using var dats = new DatCollection(datDir, DatAccessType.Read); + var cache = LoadThresholdBuilding(dats); + + var world = Vector3.Transform( + CottageDoorwayCharacterizationTests.Interior0170Local, + cache.GetCellStruct(CottageDoorwayCharacterizationTests.Vestibule0170)!.WorldTransform); + + uint picked = CellTransit.FindCellList(cache, world, FootRadius, + CottageDoorwayCharacterizationTests.Room0171); // stale/wrong seed + Assert.Equal(CottageDoorwayCharacterizationTests.Vestibule0170, picked); + } +}