From 0ece224b0b6a1eab071a9f0efdbb5e70a85e01fe Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Fri, 25 Sep 2026 03:21:40 +0200 Subject: [PATCH] Cut 12.2 Soul fix batch 2, S8: an L-shaped aimed item, not just a square Only a 2x2 (BigMarker) aimed item was ever tested -- a square's centroid and its bounding-box centre are the same point, so N11 (aim point = bounding-box centre instead of the true cell-average centroid) survived. AimingAtAnLShaped ItemCentresOnItsCentroidNotItsBoundingBox equips a 3-cell L-tromino (Build's new equipLMarker fixture), fires from a diagonal facing so the shape's true asymmetry projects fully onto the lateral axis, and asserts the empirical mean lateral offset tracks the item's own true centroid (computed by the test) and not its bounding-box centre. Manually verified against N11's own patch: it now fails (empirical -2.139, tracking the wrong bounding-box prediction -2.121, against the true centroid -1.886); the existing BigMarker test is unaffected since a square's two candidate points coincide. Co-Authored-By: Claude Opus 5.5 --- .../FireControlCut12Tests.cs | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/tests/Aetheria.Shared.Tests/FireControlCut12Tests.cs b/tests/Aetheria.Shared.Tests/FireControlCut12Tests.cs index 829dd0304..6160ad02f 100644 --- a/tests/Aetheria.Shared.Tests/FireControlCut12Tests.cs +++ b/tests/Aetheria.Shared.Tests/FireControlCut12Tests.cs @@ -533,6 +533,42 @@ public sealed class FireControlCut12Tests : IDisposable $"empirical mean lateral offset {empiricalMeanLateral:F3} does not track the centroid's own lane {expectedA:F3}"); } + // S8 fix batch (Hands, 2026-09-25): the BigMarker test above only ever exercises a 2x2 (square) footprint, + // whose true centroid and bounding-box centre are the same point -- N11 (aim point = bounding-box centre + // instead of the cell average) survived because nothing could tell the two apart. An L-tromino (3 of a + // 2x2's 4 cells) makes them different points: this fixture pins the aim point against the exact centroid + // the test computes itself, and separately checks it against the wrong (bounding-box) candidate to prove + // the fixture actually distinguishes them. + [Fact] + public void AimingAtAnLShapedItemCentresOnItsCentroidNotItsBoundingBox() + { + // A diagonal facing (not axis-aligned) puts the L-shape's actual asymmetry -- its centroid sits off + // the bounding-box centre along the diagonal, not along either grid axis -- fully onto the lateral + // axis, instead of losing most of it to the axis a purely axis-aligned facing would leave unmeasured. + var e = Build(TestSettings(), SolidShape(9, 9), precision: 1f, targetFacing: normalize(float2(1, 1)), equipLMarker: true); + Assert.True(e.Shooter.TrySelectTargetItem(e.LMarker)); + + var travelDirection = FireControl.TravelDirection(e.Weapon, e.Shooter, e.Target); + var bearing = normalize(e.Target.ToSchematic(travelDirection)); + var ell = float2(-bearing.y, bearing.x); + var centroid = e.LMarkerCells.Aggregate(float2.zero, (t, c) => t + (float2) c) / e.LMarkerCells.Length; + var boundingBoxCentre = (float2(e.LMarkerCells.Min(c => c.x), e.LMarkerCells.Min(c => c.y)) + + float2(e.LMarkerCells.Max(c => c.x), e.LMarkerCells.Max(c => c.y))) / 2f; + Assert.True(lengthsq(centroid - boundingBoxCentre) > .01f, "fixture: the L-shape's centroid and bounding-box centre must actually differ"); + + var expectedA = dot(centroid, ell); + var wrongA = dot(boundingBoxCentre, ell); + Assert.True(Math.Abs(expectedA - wrongA) > .15f, "fixture: the two candidate aim points must project to distinguishably different lateral values"); + + var outcomes = FireMany(e, 3000); + var hits = outcomes.Where(o => o.Hit).ToList(); + Assert.True(hits.Count > 300, $"expected a healthy number of hits, got {hits.Count}"); + + var empiricalMeanLateral = hits.Average(o => (double) o.Lateral); + Assert.True(Math.Abs(empiricalMeanLateral - expectedA) < .1, + $"empirical mean lateral offset {empiricalMeanLateral:F3} (bounding-box centre would predict {wrongA:F3}) does not track the true centroid's own lane {expectedA:F3}"); + } + // BroadsideIsEasierThanHeadOn: kills the bearing being ignored and the old max(W,H) bound. A 2x12 hull is // much easier to hit broadside (its long axis facing the shot) than head-on (its short axis facing it). [Fact]