champions-of-luneraria/Assets/Luneraria/Tests/Core/Golden/scenarios.json
Martin Stickelbroeck 4d6ed17dca U1: Regelkern in C#, verhaltensgleich zur Godot-Referenz
Vollständiger, engineunabhängiger Regelkern (Luneraria.Core, ohne
UnityEngine): Partie mit sechs Plätzen und zwei Teams, private Planung mit
atomarer Übernahme, Gold, Käufe, Aufwertungen, Formation, drei Lanes mit
zwei Laneturmen und zwei Basistürmen je Team, Ancients, unbegrenzte Wellen,
Helden mit Stufen, Items und Talenten, Angriffschampions, Bossgrotte mit
Anspruch, Entsendung, Kopfgeld und dreirundiger Sperre, Bots und
Siegbedingung (nur Ancient-Fall). Serverautoritative Befehlsschnittstelle.

- Deterministisch: fester Takt, geordnete Maps, fortlaufende IDs, kein
  Zufall, keine Uhr, keine Threads
- Tabellen einmal aus Godot erzeugt (tools/port/gen_content.py)
- Golden-Referenzen aus Godot (tools/godot-golden): 19 Szenarien,
  120 Snapshots, 51.221 Werte, Abweichung 0 (Toleranzen dokumentiert)
- Tests: content 75, core 91, m2 73, golden 157, determinism 6 = 402/402
- Leistung (M5): Simulationsschritt 0,259 ms statt 1,587 ms in GDScript
- Referenzfehler R1 im Bot-Gedächtnis bewusst übernommen und dokumentiert
- Geplante Helden (Lucy, Ghaliga, Siegfried, Feena) im Datenmodell, inaktiv
- Doku unter docs/port (U1-RULES, PORT-STATUS, PORT-QA, U2 bis U5)

Unity-Editor-Lauf offen: Die Unity-Lizenz ist noch nicht aktiviert; gebaut
und getestet mit dem .NET 8 SDK auf Unitys Sprachstand (C# 9, .NET
Standard 2.1).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 14:31:18 +02:00

226 lines
12 KiB
JSON

{
"about": "Golden scenarios for the rules port. The Godot reference (tools/godot-golden/golden_scenarios.gd) and the C# core (GoldenSuite) interpret the same operations; Godot writes golden.json, C# compares its own snapshots with it (docs/port/U1-RULES.md). Uid references: $last:<slot> last roster entry, $roster:<slot>:<index>, $hero:<slot>, $booking:<slot>:<index> (new bookings of the draft).",
"scenarios": [
{"id": "start_three_lanes", "title": "Normaler Drei-Lane-Start: sechs Bots, Aufmarsch, erste Gefechte, Abrechnung",
"ops": [
{"op": "setup", "seed": 4, "bunds": ["wurzelbund", "wurzelbund", "glutpakt", "glutpakt", "wurzelbund", "glutpakt"]},
{"op": "bots"},
{"op": "snapshot", "label": "planned"},
{"op": "begin"},
{"op": "snapshot", "label": "spawned", "units": true, "wave": true},
{"op": "steps", "n": 200},
{"op": "snapshot", "label": "t10", "units": true},
{"op": "steps", "n": 400},
{"op": "snapshot", "label": "t30", "units": true},
{"op": "finish"},
{"op": "snapshot", "label": "settled", "events": true}
]},
{"id": "lane_outer_lost", "title": "Lane verliert äußeren Turm: Bastion wird angreifbar, Marschgrenze rückt vor",
"ops": [
{"op": "setup", "seed": 5},
{"op": "building", "id": "blau_top_outer", "hp": 0},
{"op": "round", "value": 6},
{"op": "gold", "slot": 3, "add": 900},
{"op": "bots"},
{"op": "begin"},
{"op": "steps", "n": 500},
{"op": "snapshot", "label": "t25", "units": true},
{"op": "finish"},
{"op": "snapshot", "label": "settled", "events": true}
]},
{"id": "lane_both_lost", "title": "Lane verliert beide Türme: Basistürme angreifbar",
"ops": [
{"op": "setup", "seed": 6},
{"op": "building", "id": "blau_bot_outer", "hp": 0},
{"op": "building", "id": "blau_bot_inner", "hp": 0},
{"op": "round", "value": 8},
{"op": "gold", "slot": 5, "add": 1500},
{"op": "bots"},
{"op": "begin"},
{"op": "steps", "n": 700},
{"op": "snapshot", "label": "t35", "units": true},
{"op": "finish"},
{"op": "snapshot", "label": "settled", "events": true}
]},
{"id": "base_tower_attack", "title": "Angriff auf Basistürme durch eine starke Rot-Armee",
"ops": [
{"op": "setup", "seed": 7},
{"op": "building", "id": "blau_mid_outer", "hp": 0},
{"op": "building", "id": "blau_mid_inner", "hp": 0},
{"op": "round", "value": 12},
{"op": "gold", "slot": 3, "add": 6000},
{"op": "gold", "slot": 4, "add": 6000},
{"op": "gold", "slot": 5, "add": 6000},
{"op": "bots"},
{"op": "begin"},
{"op": "steps", "n": 1200},
{"op": "snapshot", "label": "t60", "units": true},
{"op": "finish"},
{"op": "snapshot", "label": "settled", "events": true}
]},
{"id": "ancient_destruction", "title": "Ancient-Zerstörung beendet die Partie",
"ops": [
{"op": "setup", "seed": 8},
{"op": "building", "id": "rot_top_outer", "hp": 0},
{"op": "building", "id": "rot_top_inner", "hp": 0},
{"op": "building", "id": "rot_base_1", "hp": 0},
{"op": "building", "id": "rot_base_2", "hp": 0},
{"op": "building", "id": "rot_ancient", "hp": 900},
{"op": "round", "value": 12},
{"op": "gold", "slot": 0, "add": 8000},
{"op": "gold", "slot": 1, "add": 8000},
{"op": "gold", "slot": 2, "add": 8000},
{"op": "bots"},
{"op": "begin"},
{"op": "finish"},
{"op": "snapshot", "label": "over", "events": true}
]},
{"id": "high_wave_21", "title": "Hohe Welle über 20: normale Welle, stärkere Monster",
"ops": [
{"op": "setup", "seed": 9},
{"op": "round", "value": 21},
{"op": "gold_all", "add": 4000},
{"op": "bots"},
{"op": "begin"},
{"op": "snapshot", "label": "spawned", "units": true, "wave": true},
{"op": "steps", "n": 400},
{"op": "snapshot", "label": "t20", "units": true},
{"op": "finish"},
{"op": "snapshot", "label": "settled", "events": true}
]},
{"id": "high_wave_137", "title": "Hohe Welle über 100: Werte, Belohnungen und Kampf bleiben endlich",
"ops": [
{"op": "setup", "seed": 10},
{"op": "round", "value": 137},
{"op": "gold_all", "add": 40000},
{"op": "bots"},
{"op": "begin"},
{"op": "snapshot", "label": "spawned", "wave": true},
{"op": "finish"},
{"op": "snapshot", "label": "settled", "events": true}
]},
{"id": "hero_levels", "title": "Heldenentwicklung über Runden: Anwerben, Erfahrung, Stufen, Talente, Items (Bots)",
"ops": [
{"op": "setup", "seed": 12},
{"op": "play", "rounds": 9}
]},
{"id": "item_purchase", "title": "Itemkauf: Plätze, Einzigartigkeit, Stufe, Rückgabe, Normale Einheiten ohne Items",
"ops": [
{"op": "setup", "seed": 21, "bunds": ["glutpakt", "wurzelbund", "glutpakt", "wurzelbund", "glutpakt", "wurzelbund"]},
{"op": "gold", "slot": 0, "add": 5000},
{"op": "cmd", "slot": 0, "command": {"type": "hero_buy", "hero_id": "held_ringborke"}},
{"op": "cmd", "slot": 0, "command": {"type": "buy", "type_id": "g01"}},
{"op": "cmd", "slot": 0, "command": {"type": "item_buy", "item_id": "glutklinge", "uid": "$last:0"}},
{"op": "cmd", "slot": 0, "command": {"type": "item_buy", "item_id": "glutklinge", "uid": "$hero:0"}},
{"op": "cmd", "slot": 0, "command": {"type": "item_buy", "item_id": "glutklinge"}},
{"op": "cmd", "slot": 0, "command": {"type": "item_buy", "item_id": "kristallsplitter"}},
{"op": "cmd", "slot": 0, "command": {"type": "item_buy", "item_id": "rankenmantel"}},
{"op": "cmd", "slot": 0, "command": {"type": "item_buy", "item_id": "basaltschild"}},
{"op": "cmd", "slot": 0, "command": {"type": "item_buy", "item_id": "funkenfokus"}},
{"op": "cmd", "slot": 0, "command": {"type": "item_sell", "item_id": "basaltschild"}},
{"op": "cmd", "slot": 0, "command": {"type": "item_buy", "item_id": "basaltschild"}},
{"op": "snapshot", "label": "planned"},
{"op": "begin"},
{"op": "snapshot", "label": "spawned", "units": true},
{"op": "finish"},
{"op": "cmd", "slot": 0, "command": {"type": "item_sell", "item_id": "basaltschild"}},
{"op": "cmd", "slot": 0, "command": {"type": "hero_cancel"}},
{"op": "snapshot", "label": "after_round"}
]},
{"id": "attack_champion", "title": "Angriffschampion: Buchung n, öffentliche Ankündigung, Einsatz n+1, Kopfgeld, Verbrauch",
"ops": [
{"op": "setup", "seed": 24, "bunds": ["glutpakt", "wurzelbund", "glutpakt", "wurzelbund", "glutpakt", "wurzelbund"]},
{"op": "gold", "slot": 0, "add": 1000},
{"op": "cmd", "slot": 0, "command": {"type": "champion_book", "champion_id": "ch_belagerung", "lane": "bot"}},
{"op": "cmd", "slot": 0, "command": {"type": "champion_book", "champion_id": "ch_druck", "lane": "top"}},
{"op": "cmd", "slot": 0, "command": {"type": "champion_book", "champion_id": "ch_schutz", "lane": "top"}},
{"op": "cmd", "slot": 0, "command": {"type": "champion_cancel", "uid": "$booking:0:1"}},
{"op": "begin"},
{"op": "finish"},
{"op": "snapshot", "label": "announced"},
{"op": "cmd", "slot": 0, "command": {"type": "champion_cancel", "uid": "c0-1"}},
{"op": "begin"},
{"op": "snapshot", "label": "deployed", "units": true},
{"op": "steps", "n": 600},
{"op": "snapshot", "label": "t30", "units": true},
{"op": "finish"},
{"op": "snapshot", "label": "settled", "events": true},
{"op": "begin"},
{"op": "snapshot", "label": "consumed"},
{"op": "finish"}
]},
{"id": "boss_cycle", "title": "Bossversuch erfolgreich, Anspruch, Entsendung auf gewählter Lane, Boss getötet mit Kopfgeld, dreirundige Sperre, Wiederverfügbarkeit",
"ops": [
{"op": "setup", "seed": 26, "bunds": ["glutpakt", "wurzelbund", "glutpakt", "wurzelbund", "glutpakt", "wurzelbund"]},
{"op": "play", "rounds": 5, "bots": false},
{"op": "expedition", "slot": 1, "types": [1, 1, 1, 0, 2, 2, 2], "hero": "held_ringborke"},
{"op": "begin"},
{"op": "snapshot", "label": "expedition", "units": true},
{"op": "finish"},
{"op": "snapshot", "label": "won", "events": true},
{"op": "settle_boss"},
{"op": "cmd", "slot": 4, "command": {"type": "boss_lane", "lane": "top"}},
{"op": "cmd", "slot": 1, "command": {"type": "boss_lane", "lane": "top"}},
{"op": "cmd", "slot": 1, "command": {"type": "assign", "uid": "$roster:1:0", "lane": "grotto"}},
{"op": "cmd", "slot": 1, "command": {"type": "assign", "uid": "$roster:1:0", "lane": "mid"}},
{"op": "snapshot", "label": "announced"},
{"op": "begin"},
{"op": "snapshot", "label": "deployed", "units": true},
{"op": "steps", "n": 200},
{"op": "snapshot", "label": "marching", "units": true},
{"op": "unit_set", "select": {"kind": "boss", "zone": "lane"}, "hp": 0},
{"op": "steps", "n": 1},
{"op": "finish"},
{"op": "snapshot", "label": "boss_killed", "events": true},
{"op": "buildings_full"},
{"op": "snapshot", "label": "locked_r8"},
{"op": "play", "rounds": 1, "bots": false},
{"op": "buildings_full"},
{"op": "snapshot", "label": "locked_r9"},
{"op": "play", "rounds": 1, "bots": false},
{"op": "snapshot", "label": "open_r10"},
{"op": "begin"},
{"op": "snapshot", "label": "no_second_deployment", "units": true},
{"op": "finish"}
]},
{"id": "boss_failure", "title": "Bossversuch gescheitert: keine Belohnung, keine Sperre, Boss beim nächsten Versuch voll",
"ops": [
{"op": "setup", "seed": 27, "bunds": ["glutpakt", "wurzelbund", "glutpakt", "wurzelbund", "glutpakt", "wurzelbund"]},
{"op": "gold", "slot": 2, "add": 200},
{"op": "cmd", "slot": 2, "command": {"type": "buy", "type_id": "g01"}},
{"op": "cmd", "slot": 2, "command": {"type": "assign", "uid": "$last:2", "lane": "grotto"}},
{"op": "begin"},
{"op": "snapshot", "label": "attempt", "units": true},
{"op": "finish"},
{"op": "snapshot", "label": "failed", "events": true},
{"op": "cmd", "slot": 2, "command": {"type": "assign", "uid": "$roster:2:0", "lane": "grotto"}},
{"op": "begin"},
{"op": "snapshot", "label": "second_attempt", "units": true},
{"op": "finish"},
{"op": "snapshot", "label": "failed_again", "events": true}
]},
{"id": "grotto_contested", "title": "Umkämpfte Grotte: zwei Expeditionen, Boss ruht, erwacht unberührt, Sieger erhält Anspruch",
"ops": [
{"op": "setup", "seed": 25, "bunds": ["glutpakt", "wurzelbund", "glutpakt", "wurzelbund", "glutpakt", "wurzelbund"]},
{"op": "expedition", "slot": 1, "types": [1, 1, 0, 2]},
{"op": "cmd", "slot": 1, "command": {"type": "buy", "type_id": "w01"}},
{"op": "expedition", "slot": 4, "types": [1]},
{"op": "begin"},
{"op": "snapshot", "label": "dormant", "units": true},
{"op": "steps", "n": 300},
{"op": "snapshot", "label": "t15", "units": true},
{"op": "finish"},
{"op": "snapshot", "label": "settled", "events": true}
]},
{"id": "mirror_match", "title": "Spiegelpartie: gleicher Bund und gleiches Profil auf allen Plätzen bleibt exakt symmetrisch",
"ops": [
{"op": "setup", "seed": 3, "bunds": ["glutpakt", "glutpakt", "glutpakt", "glutpakt", "glutpakt", "glutpakt"]},
{"op": "play", "rounds": 8, "profile": "verteidigung"}
]},
{"id": "bot_match_1", "title": "Vollständige Bot-Partie Seed 1 bis zum Ancient-Fall", "ops": [{"op": "setup", "seed": 1}, {"op": "play", "rounds": 60}]},
{"id": "bot_match_2", "title": "Vollständige Bot-Partie Seed 2 bis zum Ancient-Fall", "ops": [{"op": "setup", "seed": 2}, {"op": "play", "rounds": 60}]},
{"id": "bot_match_3", "title": "Vollständige Bot-Partie Seed 3 bis zum Ancient-Fall", "ops": [{"op": "setup", "seed": 3}, {"op": "play", "rounds": 60}]},
{"id": "bot_match_11", "title": "Vollständige Bot-Partie Seed 11 bis zum Ancient-Fall", "ops": [{"op": "setup", "seed": 11}, {"op": "play", "rounds": 60}]},
{"id": "bot_match_28", "title": "Vollständige Bot-Partie Seed 28 (Bots mit Helden, Champions und Grotte)", "ops": [{"op": "setup", "seed": 28, "bunds": ["glutpakt", "wurzelbund", "glutpakt", "wurzelbund", "glutpakt", "wurzelbund"]}, {"op": "play", "rounds": 60}]}
]
}