From 9d8d0cd4a4512471d39285d3bf2381dbff367c7d Mon Sep 17 00:00:00 2001 From: Dennis Westermann Date: Sat, 29 Aug 2026 12:00:53 +0200 Subject: [PATCH] feat(map): die Karte traegt 11 Vorkommen statt 5 (21.6, #93) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fuenf Aetheriumfelder auf 128x128, zwei als Startbasis gebunden, liessen fuer zwei Spieler nur drei umkaempfte Felder uebrig — ein Wettlauf zum jeweils naechsten, keine Wahl zwischen Alternativen. Neu: vier natuerliche Erweiterungen a 9.000 AE und vier umkaempfte Flanken a 12.000 AE, Mitte unveraendert bei einem Feld a 15.000 (wird erst in 21.7 zum Gebiet). Punktsymmetrie nach D-107 fuer jedes Paar nachgerechnet und testgepinnt. Die Feldlage stand an sechs statt der in R-1 angenommenen vier Stellen literal im Repo; fuenf sind mitgezogen, die sechste liegt im Editor-Ordner und hat keinen Laufzeitkonsumenten. Erarbeitet von Kimi K3 als delegiertem Worker, Bericht unter reports/v8.6.0/sprint-21/. --- .../Gameplay/CanonicalMatchSetupTests.cs | 61 +++++++++++++---- .../Scripts/Gameplay/Match/MatchBootstrap.cs | 67 +++++++++++++------ CHANGELOG.md | 16 +++++ .../BuildZoneCapacityTests.cs | 27 +++++--- .../CanonicalMatchSetupTests.cs | 53 ++++++++++++--- .../Determinism10000Scenario.cs | 40 +++++++---- 6 files changed, 196 insertions(+), 68 deletions(-) diff --git a/Assets/Tests/EditMode/Gameplay/CanonicalMatchSetupTests.cs b/Assets/Tests/EditMode/Gameplay/CanonicalMatchSetupTests.cs index f76af33..6a09a0c 100644 --- a/Assets/Tests/EditMode/Gameplay/CanonicalMatchSetupTests.cs +++ b/Assets/Tests/EditMode/Gameplay/CanonicalMatchSetupTests.cs @@ -106,11 +106,17 @@ private struct FieldLayout private static readonly FieldLayout[] FieldLayouts = { - new FieldLayout { Id = 1, X = 7, Y = 7, ReserveAE = 9000L }, - new FieldLayout { Id = 2, X = 117, Y = 117, ReserveAE = 9000L }, - new FieldLayout { Id = 3, X = 24, Y = 40, ReserveAE = 9000L }, - new FieldLayout { Id = 4, X = 100, Y = 84, ReserveAE = 9000L }, - new FieldLayout { Id = 5, X = 62, Y = 62, ReserveAE = 15000L }, + new FieldLayout { Id = 1, X = 7, Y = 7, ReserveAE = 9000L }, + new FieldLayout { Id = 2, X = 117, Y = 117, ReserveAE = 9000L }, + new FieldLayout { Id = 3, X = 24, Y = 40, ReserveAE = 9000L }, + new FieldLayout { Id = 4, X = 100, Y = 84, ReserveAE = 9000L }, + new FieldLayout { Id = 5, X = 62, Y = 62, ReserveAE = 15000L }, + new FieldLayout { Id = 6, X = 44, Y = 24, ReserveAE = 9000L }, + new FieldLayout { Id = 7, X = 80, Y = 100, ReserveAE = 9000L }, + new FieldLayout { Id = 8, X = 44, Y = 80, ReserveAE = 12000L }, + new FieldLayout { Id = 9, X = 80, Y = 44, ReserveAE = 12000L }, + new FieldLayout { Id = 10, X = 24, Y = 96, ReserveAE = 12000L }, + new FieldLayout { Id = 11, X = 100, Y = 28, ReserveAE = 12000L }, }; // Faction-resolved opening placement ids (SimDefinitions id rule): // slot 0 Alliance (role value), slot 1 Legion (role value + 17). @@ -204,7 +210,8 @@ private sealed class SlotLayout /// /// Byte-exact mirror of Determinism10000Scenario.SetupMatch (D-077): - /// five finite Aetherium fields in canonical id order, then per slot a + /// eleven finite Aetherium fields (21.6, #93) in canonical id order, + /// then per slot a /// completed HQ and ONE Builder — nothing else. Entity spawn order is /// load-bearing: EntityManager hands out ids from a deterministic free /// list, so any reordering shifts every id and therefore every hash. @@ -401,7 +408,13 @@ public void MatchBootstrap_PlacesTheCanonicalOpeningGeometry() new Vector2Int(24, 40), new Vector2Int(100, 84), new Vector2Int(62, 62), - })); + new Vector2Int(44, 24), + new Vector2Int(80, 100), + new Vector2Int(44, 80), + new Vector2Int(80, 44), + new Vector2Int(24, 96), + new Vector2Int(100, 28), + }), "the eleven canonical fields (21.6, #93) in id order"); Assert.That(bootstrap.LocalHqOrigin, Is.EqualTo(new Vector2Int(4, 4))); Assert.That(bootstrap.EnemyHqOrigin, Is.EqualTo(new Vector2Int(118, 118))); Assert.That(bootstrap.MapSize, Is.EqualTo(new Vector2Int(MapWidth, MapHeight))); @@ -424,7 +437,7 @@ public void MatchBootstrap_PlacesTheCanonicalOpeningGeometry() } [Test] - public void ReferenceOpeningPosition_RegistersFiveFiniteFields() + public void ReferenceOpeningPosition_RegistersElevenFiniteFields() { ReferenceHost host = BuildReferenceHost(CanonicalSeed); ApplyOpeningPosition(host); @@ -449,12 +462,36 @@ public void CanonicalOpeningGeometry_UsesD107PointAndFootprintMirrors() Assert.That(Slot0Layout.BuilderX + Slot1Layout.BuilderX, Is.EqualTo(124), "point coordinates mirror as p -> 124-p"); Assert.That(Slot0Layout.BuilderY + Slot1Layout.BuilderY, Is.EqualTo(124)); - Assert.That(FieldLayouts[0].X + FieldLayouts[1].X, Is.EqualTo(124)); - Assert.That(FieldLayouts[0].Y + FieldLayouts[1].Y, Is.EqualTo(124)); - Assert.That(FieldLayouts[2].X + FieldLayouts[3].X, Is.EqualTo(124)); - Assert.That(FieldLayouts[2].Y + FieldLayouts[3].Y, Is.EqualTo(124)); + // The eleven-field layout (21.6, #93): five mirrored pairs plus the + // self-mirroring centre. Every pair sums to 124 per axis. + AssertMirrorPair(0, 1, "start fields"); + AssertMirrorPair(2, 3, "natural expansions"); + AssertMirrorPair(5, 6, "second expansions"); + AssertMirrorPair(7, 8, "near contested flanks"); + AssertMirrorPair(9, 10, "far contested flanks"); Assert.That(FieldLayouts[4].X, Is.EqualTo(62)); Assert.That(FieldLayouts[4].Y, Is.EqualTo(62)); + // Mirrored fields carry mirrored reserves, or the mirror is only + // geometric and the balance statement is false. + AssertMirroredReserves(0, 1); + AssertMirroredReserves(2, 3); + AssertMirroredReserves(5, 6); + AssertMirroredReserves(7, 8); + AssertMirroredReserves(9, 10); + } + + private static void AssertMirrorPair(int first, int second, string label) + { + Assert.That(FieldLayouts[first].X + FieldLayouts[second].X, Is.EqualTo(124), + $"D-107: {label} must mirror as (x, y) -> (124 - x, 124 - y)"); + Assert.That(FieldLayouts[first].Y + FieldLayouts[second].Y, Is.EqualTo(124), + $"D-107: {label} must mirror as (x, y) -> (124 - x, 124 - y)"); + } + + private static void AssertMirroredReserves(int first, int second) + { + Assert.That(FieldLayouts[first].ReserveAE, Is.EqualTo(FieldLayouts[second].ReserveAE), + "D-107 symmetry covers the reserve, not only the cell"); } [Test] diff --git a/Assets/_Project/Scripts/Gameplay/Match/MatchBootstrap.cs b/Assets/_Project/Scripts/Gameplay/Match/MatchBootstrap.cs index cf29520..a32b6f6 100644 --- a/Assets/_Project/Scripts/Gameplay/Match/MatchBootstrap.cs +++ b/Assets/_Project/Scripts/Gameplay/Match/MatchBootstrap.cs @@ -85,9 +85,10 @@ public NetworkJoinStatus( /// quality/content/mvp-v1.json startStatePerPlayer): per slot ONLY a /// completed HQ, ONE Builder and 3.000 AE starting credits /// (EconomySystem.CanonicalMatchStartingCreditsAE, plumbed in by - /// 's default) — plus the five - /// finite canonical Aetherium fields (two starts, two expansions and the - /// contested centre). The Refinery is NO longer + /// 's default) — plus the + /// eleven finite canonical Aetherium fields (two starts, four natural + /// expansions, four contested flanks and the contested centre; 21.6, + /// #93). The Refinery is NO longer /// pre-placed: the player builds it (it has no Power-plant prerequisite /// since D-077), and the completed Refinery — not the HQ — produces the /// Harvesters. @@ -150,22 +151,42 @@ private struct FieldLayout } /// - /// The five canonical fields (16.7, C1 — MVPContentManifest section 5, - /// mirror of Determinism10000Scenario.FieldLayouts): two start fields - /// and two natural expansions at 9.000 AE each, one contested centre - /// at 15.000. Symmetry is binding: point coordinates mirror as - /// (x, y) -> (124 - x, 124 - y); a 3x3 footprint's lower-left - /// origin therefore mirrors as (x, y) -> (122 - x, 122 - y). - /// D-107 maps HQ (4,4) to HQ (118,118), so both starts are equally - /// far from their expansion (34 cells) and from the centre (56). + /// The eleven canonical fields (21.6, #93 — the T-01 report's "at + /// least roughly twice as many", up from the five of 16.7/C1): two + /// start fields and TWO natural expansions per side at 9.000 AE + /// each, four contested flank fields at 12.000 and the contested + /// centre at 15.000. The centre deliberately stays a SINGLE field + /// here — 21.7 (D-109) turns it into the grouped zone; this package + /// only grows the count. Symmetry is binding (D-107): point + /// coordinates mirror as (x, y) -> (124 - x, 124 - y); a 3x3 + /// footprint's lower-left origin therefore mirrors as + /// (x, y) -> (122 - x, 122 - y). The pairs 1/2, 3/4, 6/7, 8/9 + /// and 10/11 are exact mirrors; the centre (62,62) mirrors onto + /// itself. Distances are Chebyshev from the HQ footprint's + /// centre-facing corner cell ((6,6) against (118,118) — the + /// convention behind the canonical 34/56): each start field sits 1 + /// from its HQ, the naturals lie 34 and 38 out (two per side, so + /// the opening is a CHOICE, not a race), the near flank pair is + /// exactly equidistant (74/74), the far flank pair leans four + /// cells toward its side (90/94), the centre stays at 56. + /// Reserves: naturals match the start field (the 21.3 longevity + /// measurement), the flanks pay +33 % for the contested walk, the + /// centre keeps its premium. Total on the map: 117.000 AE (was + /// 51.000). /// private static readonly FieldLayout[] FieldLayouts = { - new FieldLayout { Id = 1, X = 7, Y = 7, ReserveAE = 9000L }, - new FieldLayout { Id = 2, X = 117, Y = 117, ReserveAE = 9000L }, - new FieldLayout { Id = 3, X = 24, Y = 40, ReserveAE = 9000L }, - new FieldLayout { Id = 4, X = 100, Y = 84, ReserveAE = 9000L }, - new FieldLayout { Id = 5, X = 62, Y = 62, ReserveAE = 15000L }, + new FieldLayout { Id = 1, X = 7, Y = 7, ReserveAE = 9000L }, + new FieldLayout { Id = 2, X = 117, Y = 117, ReserveAE = 9000L }, + new FieldLayout { Id = 3, X = 24, Y = 40, ReserveAE = 9000L }, + new FieldLayout { Id = 4, X = 100, Y = 84, ReserveAE = 9000L }, + new FieldLayout { Id = 5, X = 62, Y = 62, ReserveAE = 15000L }, + new FieldLayout { Id = 6, X = 44, Y = 24, ReserveAE = 9000L }, + new FieldLayout { Id = 7, X = 80, Y = 100, ReserveAE = 9000L }, + new FieldLayout { Id = 8, X = 44, Y = 80, ReserveAE = 12000L }, + new FieldLayout { Id = 9, X = 80, Y = 44, ReserveAE = 12000L }, + new FieldLayout { Id = 10, X = 24, Y = 96, ReserveAE = 12000L }, + new FieldLayout { Id = 11, X = 100, Y = 28, ReserveAE = 12000L }, }; /// maxHealth stamped by SpawnUnit when no definition stats are applied. @@ -275,9 +296,11 @@ public bool TryGetFieldInitialReserve(ushort fieldId, out long reserveAE) public Vector2Int EnemyFieldCell => new Vector2Int(EnemyPlayerLayout.FieldX, EnemyPlayerLayout.FieldY); /// - /// All five registered field cells in canonical id order: start 0/1, - /// expansion 0/1, contested centre. Presentation iterates this list so - /// marker and scatter geometry cannot silently omit a field. + /// All eleven registered field cells in canonical id order: start + /// 0/1, expansion 0/1, contested centre, second expansion 0/1, + /// contested near flank 0/1, contested far flank 0/1. Presentation + /// iterates this list so marker and scatter geometry cannot + /// silently omit a field. /// public Vector2Int[] AllFieldCells { @@ -933,9 +956,9 @@ private void OnDestroy() } /// - /// Registers all five canonical fields in ascending id order. This is - /// the exact field pass mirrored by Determinism10000Scenario and both - /// CanonicalMatchSetupTests lanes. + /// Registers all eleven canonical fields in ascending id order. This + /// is the exact field pass mirrored by Determinism10000Scenario and + /// both CanonicalMatchSetupTests lanes. /// private void SetupFields() { diff --git a/CHANGELOG.md b/CHANGELOG.md index 9437bc8..87d58e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -189,6 +189,22 @@ die Versionierung folgt (in der aktuellen Doku-Phase) dem Dokumentationsstand de sie belegt keine Verbesserung. ### Geändert +- **Die Karte trägt 11 Vorkommen statt 5 (Paket 21.6, #93).** Bisher lagen fünf + Aetheriumfelder auf 128×128, zwei davon als Startbasis gebunden — für zwei + Spieler blieben **drei** umkämpfte Felder, und daraus entsteht keine + Entscheidung, sondern ein Wettlauf zum jeweils nächstgelegenen. Neu sind vier + natürliche Erweiterungen à 9.000 AE und vier umkämpfte Flanken à 12.000 AE; + die Mitte bleibt in diesem Paket **ein** Feld à 15.000 (sie wird erst in 21.7 + zum Gebiet). Gesamtreserve der Karte 117.000 AE, umkämpfte Felder für zwei + Spieler: neun statt drei. Die bindende Punktsymmetrie aus D-107 + (`(x, y) → (124 − x, 124 − y)`) ist für jedes Paar nachgerechnet und als Test + gepinnt — Asymmetrie wäre hier kein Geschmacks-, sondern ein Balancefehler. + Nebenbefund beim Umsetzen: die kanonische Feldlage stand nicht wie in R-1 + angenommen an **vier** Stellen literal im Repo, sondern an **sechs**; die + fünfte (`tools/Nova.SimRunner.Tests/BuildZoneCapacityTests.cs`) ist + mitgezogen, die sechste (`Assets/_Project/Editor/BootstrapSceneGenerator.cs`, + backt die alte Lage in `MapDefinitionSO`) liegt außerhalb der Schreibhoheit + dieses Pakets und ist heute folgenlos, weil kein Laufzeitkonsument sie liest - **Die Skirmish-KI benennt, was eine Einheit vorhat — und verteidigt damit ihre Basis (`GoalKind`, `r7` → `r8`)** — zwei Schritte, die zusammengehören und deshalb zusammen kommen: erst bekommt die Entscheidung eine **Form**, diff --git a/tools/Nova.SimRunner.Tests/BuildZoneCapacityTests.cs b/tools/Nova.SimRunner.Tests/BuildZoneCapacityTests.cs index 2cffd1b..31400ef 100644 --- a/tools/Nova.SimRunner.Tests/BuildZoneCapacityTests.cs +++ b/tools/Nova.SimRunner.Tests/BuildZoneCapacityTests.cs @@ -19,9 +19,11 @@ namespace Nova.SimRunner.Tests /// The start zone is the build-influence area of the canonical player-0 /// HQ anchor alone (footprint origin (4,4), D-107): every 3x3 footprint /// whose footprint-aware Chebyshev distance to the HQ rectangle is at most - /// . All five - /// canonical fields (D-102/D-107 layout) are registered, though only the - /// start field at (7,7) intersects the zone. + /// . All eleven + /// canonical fields (21.6/#93 layout) are registered, though only the + /// start field at (7,7) intersects the zone — the next-nearest field sits + /// at (44,24), thirty cells past the zone boundary, so the pinned numbers + /// below are deliberately insensitive to the outfield layout. /// /// /// Two lanes keep the number honest. The REAL lane drives the untouched @@ -48,7 +50,8 @@ public sealed class BuildZoneCapacityTests { // Canonical player-0 start (D-107): HQ footprint origin (4,4), start // field point (7,7) with 9.000 AE. The remaining canonical fields - // (D-102/D-107) complete the field set the spacing rule iterates. + // (21.6/#93 layout, mirrored from MatchBootstrap.FieldLayouts like the + // four R-1 copies) complete the field set the spacing rule iterates. private const int HqOriginX = 4; private const int HqOriginY = 4; private const ushort DefHQAlliance = 3; @@ -57,11 +60,17 @@ public sealed class BuildZoneCapacityTests private static readonly (ushort Id, int X, int Y, long ReserveAE)[] CanonicalFields = { - (1, 7, 7, 9000L), - (2, 117, 117, 9000L), - (3, 24, 40, 9000L), - (4, 100, 84, 9000L), - (5, 62, 62, 15000L), + (1, 7, 7, 9000L), + (2, 117, 117, 9000L), + (3, 24, 40, 9000L), + (4, 100, 84, 9000L), + (5, 62, 62, 15000L), + (6, 44, 24, 9000L), + (7, 80, 100, 9000L), + (8, 44, 80, 12000L), + (9, 80, 44, 12000L), + (10, 24, 96, 12000L), + (11, 100, 28, 12000L), }; private static int F => SimDefinitions.BuildingFootprintCells; diff --git a/tools/Nova.SimRunner.Tests/CanonicalMatchSetupTests.cs b/tools/Nova.SimRunner.Tests/CanonicalMatchSetupTests.cs index 321e141..9c112b5 100644 --- a/tools/Nova.SimRunner.Tests/CanonicalMatchSetupTests.cs +++ b/tools/Nova.SimRunner.Tests/CanonicalMatchSetupTests.cs @@ -78,11 +78,17 @@ private struct FieldLayout private static readonly FieldLayout[] FieldLayouts = { - new FieldLayout { Id = 1, X = 7, Y = 7, ReserveAE = 9000L }, - new FieldLayout { Id = 2, X = 117, Y = 117, ReserveAE = 9000L }, - new FieldLayout { Id = 3, X = 24, Y = 40, ReserveAE = 9000L }, - new FieldLayout { Id = 4, X = 100, Y = 84, ReserveAE = 9000L }, - new FieldLayout { Id = 5, X = 62, Y = 62, ReserveAE = 15000L }, + new FieldLayout { Id = 1, X = 7, Y = 7, ReserveAE = 9000L }, + new FieldLayout { Id = 2, X = 117, Y = 117, ReserveAE = 9000L }, + new FieldLayout { Id = 3, X = 24, Y = 40, ReserveAE = 9000L }, + new FieldLayout { Id = 4, X = 100, Y = 84, ReserveAE = 9000L }, + new FieldLayout { Id = 5, X = 62, Y = 62, ReserveAE = 15000L }, + new FieldLayout { Id = 6, X = 44, Y = 24, ReserveAE = 9000L }, + new FieldLayout { Id = 7, X = 80, Y = 100, ReserveAE = 9000L }, + new FieldLayout { Id = 8, X = 44, Y = 80, ReserveAE = 12000L }, + new FieldLayout { Id = 9, X = 80, Y = 44, ReserveAE = 12000L }, + new FieldLayout { Id = 10, X = 24, Y = 96, ReserveAE = 12000L }, + new FieldLayout { Id = 11, X = 100, Y = 28, ReserveAE = 12000L }, }; // Faction-resolved opening placement ids (SimDefinitions id rule): // slot 0 Alliance (role value), slot 1 Legion (role value + 17). @@ -176,7 +182,8 @@ private sealed class SlotLayout /// /// Byte-exact mirror of Determinism10000Scenario.SetupMatch (D-077): - /// five finite Aetherium fields in canonical id order, then per slot a + /// eleven finite Aetherium fields (21.6, #93) in canonical id order, + /// then per slot a /// completed HQ and ONE Builder — nothing else. Entity spawn order is /// load-bearing: EntityManager hands out ids from a deterministic free /// list, so any reordering shifts every id and therefore every hash. @@ -294,7 +301,7 @@ public void ReferenceOpeningPosition_MatchesDeterminism10000ScenarioSetupMatch() } [Test] - public void ReferenceOpeningPosition_RegistersFiveFiniteFields() + public void ReferenceOpeningPosition_RegistersElevenFiniteFields() { ReferenceHost host = BuildReferenceHost(CanonicalSeed); ApplyOpeningPosition(host); @@ -319,12 +326,36 @@ public void CanonicalOpeningGeometry_UsesD107PointAndFootprintMirrors() Assert.That(Slot0Layout.BuilderX + Slot1Layout.BuilderX, Is.EqualTo(124), "point coordinates mirror as p -> 124-p"); Assert.That(Slot0Layout.BuilderY + Slot1Layout.BuilderY, Is.EqualTo(124)); - Assert.That(FieldLayouts[0].X + FieldLayouts[1].X, Is.EqualTo(124)); - Assert.That(FieldLayouts[0].Y + FieldLayouts[1].Y, Is.EqualTo(124)); - Assert.That(FieldLayouts[2].X + FieldLayouts[3].X, Is.EqualTo(124)); - Assert.That(FieldLayouts[2].Y + FieldLayouts[3].Y, Is.EqualTo(124)); + // The eleven-field layout (21.6, #93): five mirrored pairs plus the + // self-mirroring centre. Every pair sums to 124 per axis. + AssertMirrorPair(0, 1, "start fields"); + AssertMirrorPair(2, 3, "natural expansions"); + AssertMirrorPair(5, 6, "second expansions"); + AssertMirrorPair(7, 8, "near contested flanks"); + AssertMirrorPair(9, 10, "far contested flanks"); Assert.That(FieldLayouts[4].X, Is.EqualTo(62)); Assert.That(FieldLayouts[4].Y, Is.EqualTo(62)); + // Mirrored fields carry mirrored reserves, or the mirror is only + // geometric and the balance statement is false. + AssertMirroredReserves(0, 1); + AssertMirroredReserves(2, 3); + AssertMirroredReserves(5, 6); + AssertMirroredReserves(7, 8); + AssertMirroredReserves(9, 10); + } + + private static void AssertMirrorPair(int first, int second, string label) + { + Assert.That(FieldLayouts[first].X + FieldLayouts[second].X, Is.EqualTo(124), + $"D-107: {label} must mirror as (x, y) -> (124 - x, 124 - y)"); + Assert.That(FieldLayouts[first].Y + FieldLayouts[second].Y, Is.EqualTo(124), + $"D-107: {label} must mirror as (x, y) -> (124 - x, 124 - y)"); + } + + private static void AssertMirroredReserves(int first, int second) + { + Assert.That(FieldLayouts[first].ReserveAE, Is.EqualTo(FieldLayouts[second].ReserveAE), + "D-107 symmetry covers the reserve, not only the cell"); } [Test] diff --git a/tools/Nova.SimRunner/Determinism10000Scenario.cs b/tools/Nova.SimRunner/Determinism10000Scenario.cs index 3956e2d..54041bc 100644 --- a/tools/Nova.SimRunner/Determinism10000Scenario.cs +++ b/tools/Nova.SimRunner/Determinism10000Scenario.cs @@ -136,8 +136,8 @@ internal sealed class DeterminismComparison /// MS-1 manifest start state of quality/content/mvp-v1.json /// (startStatePerPlayer) per slot — a COMPLETED HQ, ONE Builder and /// 3.000 AE (EconomySystem.CanonicalMatchStartingCreditsAE, wired by - /// ) — plus five finite canonical Aetherium fields. - /// Nothing + /// ) — plus eleven finite canonical Aetherium + /// fields (21.6, #93). Nothing /// else is spawned: the script then drives the opening exactly like a /// player, for BOTH slots — walk the Builder to the future site, place /// the Refinery once it is affordable and the committed grid covers its @@ -194,20 +194,32 @@ private struct FieldLayout } /// - /// The five canonical fields (16.7, C1 — MVPContentManifest section 5): - /// two start fields and two natural expansions at 9.000 AE each, one - /// contested centre at 15.000. Every slot-1 coordinate is the point - /// mirror of slot 0 across the D-102/D-107 Glutrinne layout axis ((x, y) -> - /// (124 - x, 124 - y)). Registration in ascending id order is part of - /// the canonical initial state. + /// The eleven canonical fields (16.7/C1 layout as extended by sprint + /// 21.6, #93 — the T-01 report's "at least roughly twice as many"): + /// two start fields and TWO natural expansions per side at 9.000 AE + /// each, four contested flank fields at 12.000 and the contested + /// centre at 15.000 (a single field until 21.7 groups it). Every + /// slot-1 coordinate is the point mirror of slot 0 across the + /// D-102/D-107 Glutrinne layout axis ((x, y) -> (124 - x, 124 - y)): + /// the pairs 1/2, 3/4, 6/7, 8/9 and 10/11 mirror exactly, the centre + /// (62,62) mirrors onto itself. Registration in ascending id order is + /// part of the canonical initial state. Mirror of + /// MatchBootstrap.FieldLayouts — a drift between the two is caught by + /// the CanonicalMatchSetupTests reference hash chain in BOTH lanes. /// private static readonly FieldLayout[] FieldLayouts = { - new FieldLayout { Id = 1, X = 7, Y = 7, ReserveAE = 9000L }, - new FieldLayout { Id = 2, X = 117, Y = 117, ReserveAE = 9000L }, - new FieldLayout { Id = 3, X = 24, Y = 40, ReserveAE = 9000L }, - new FieldLayout { Id = 4, X = 100, Y = 84, ReserveAE = 9000L }, - new FieldLayout { Id = 5, X = 62, Y = 62, ReserveAE = 15000L }, + new FieldLayout { Id = 1, X = 7, Y = 7, ReserveAE = 9000L }, + new FieldLayout { Id = 2, X = 117, Y = 117, ReserveAE = 9000L }, + new FieldLayout { Id = 3, X = 24, Y = 40, ReserveAE = 9000L }, + new FieldLayout { Id = 4, X = 100, Y = 84, ReserveAE = 9000L }, + new FieldLayout { Id = 5, X = 62, Y = 62, ReserveAE = 15000L }, + new FieldLayout { Id = 6, X = 44, Y = 24, ReserveAE = 9000L }, + new FieldLayout { Id = 7, X = 80, Y = 100, ReserveAE = 9000L }, + new FieldLayout { Id = 8, X = 44, Y = 80, ReserveAE = 12000L }, + new FieldLayout { Id = 9, X = 80, Y = 44, ReserveAE = 12000L }, + new FieldLayout { Id = 10, X = 24, Y = 96, ReserveAE = 12000L }, + new FieldLayout { Id = 11, X = 100, Y = 28, ReserveAE = 12000L }, }; /// @@ -636,7 +648,7 @@ private static void IssueSlotCommands( }; /// - /// Applies the deterministic match setup to a fresh host: the five + /// Applies the deterministic match setup to a fresh host: the eleven /// canonical fields in ascending id order, then per slot the D-077 /// start state of quality/content/mvp-v1.json /// (startStatePerPlayer) — a COMPLETED HQ, ONE Builder and the 3.000