Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 49 additions & 12 deletions Assets/Tests/EditMode/Gameplay/CanonicalMatchSetupTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand Down Expand Up @@ -204,7 +210,8 @@ private sealed class SlotLayout

/// <summary>
/// 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.
Expand Down Expand Up @@ -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)));
Expand All @@ -424,7 +437,7 @@ public void MatchBootstrap_PlacesTheCanonicalOpeningGeometry()
}

[Test]
public void ReferenceOpeningPosition_RegistersFiveFiniteFields()
public void ReferenceOpeningPosition_RegistersElevenFiniteFields()
{
ReferenceHost host = BuildReferenceHost(CanonicalSeed);
ApplyOpeningPosition(host);
Expand All @@ -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]
Expand Down
67 changes: 45 additions & 22 deletions Assets/_Project/Scripts/Gameplay/Match/MatchBootstrap.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
/// <see cref="MatchRunner.InitializeMatch"/>'s default) — plus the five
/// finite canonical Aetherium fields (two starts, two expansions and the
/// contested centre). The Refinery is NO longer
/// <see cref="MatchRunner.InitializeMatch"/>'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.
Expand Down Expand Up @@ -150,22 +151,42 @@ private struct FieldLayout
}

/// <summary>
/// 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) -&gt; (124 - x, 124 - y); a 3x3 footprint's lower-left
/// origin therefore mirrors as (x, y) -&gt; (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) -&gt; (124 - x, 124 - y); a 3x3
/// footprint's lower-left origin therefore mirrors as
/// (x, y) -&gt; (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).
/// </summary>
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 },
};

/// <summary>maxHealth stamped by SpawnUnit when no definition stats are applied.</summary>
Expand Down Expand Up @@ -275,9 +296,11 @@ public bool TryGetFieldInitialReserve(ushort fieldId, out long reserveAE)
public Vector2Int EnemyFieldCell => new Vector2Int(EnemyPlayerLayout.FieldX, EnemyPlayerLayout.FieldY);

/// <summary>
/// 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.
/// </summary>
public Vector2Int[] AllFieldCells
{
Expand Down Expand Up @@ -933,9 +956,9 @@ private void OnDestroy()
}

/// <summary>
/// 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.
/// </summary>
private void SetupFields()
{
Expand Down
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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**,
Expand Down
27 changes: 18 additions & 9 deletions tools/Nova.SimRunner.Tests/BuildZoneCapacityTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
/// <see cref="ConstructionSystem.BuildInfluenceRadiusCells"/>. All five
/// canonical fields (D-102/D-107 layout) are registered, though only the
/// start field at (7,7) intersects the zone.
/// <see cref="ConstructionSystem.BuildInfluenceRadiusCells"/>. 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.
/// </para>
/// <para>
/// Two lanes keep the number honest. The REAL lane drives the untouched
Expand All @@ -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;
Expand All @@ -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;
Expand Down
Loading
Loading