LuneMap spiegelsymmetrisch mit 16 Türmen und 2 Ancients, LuneCombat mit reihenfolgeunabhängigem Zweiphasenschritt und Gebäudesperren für jede Schadensquelle, LuneMatch mit Planungsentwürfen, atomarer Übernahme, Einmal-Belohnung und Kernsieg/Unentschieden, endlose LuneWaves, Bots über dieselbe Befehlsschnittstelle. 87 Kernprüfungen bestanden. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
48 lines
2.7 KiB
GDScript
48 lines
2.7 KiB
GDScript
extends Node
|
|
## Headless six-bot matches for balance and acceptance. A QA round limit is reported as
|
|
## "Testlimit erreicht / Partie nicht abgeschlossen", never as a result.
|
|
## Run: godot --headless --path . res://tools/luneraria/sim_match.tscn -- --seeds=1,2,3 --limit=60
|
|
func _ready() -> void:
|
|
call_deferred("run")
|
|
func run() -> void:
|
|
var seeds: Array = [1]
|
|
var limit: int = 60
|
|
var out: String = ""
|
|
for arg: String in OS.get_cmdline_user_args():
|
|
if arg.begins_with("--seeds="):
|
|
seeds = Array(arg.substr(8).split(",")).map(func(s: String) -> int: return int(s))
|
|
elif arg.begins_with("--limit="):
|
|
limit = int(arg.substr(8))
|
|
elif arg.begins_with("--out="):
|
|
out = arg.substr(6)
|
|
var reports: Array = []
|
|
for seed: int in seeds:
|
|
var started: int = Time.get_ticks_msec()
|
|
var match_state := LuneMatch.new()
|
|
match_state.setup({"seed": seed, "human_slot": -1})
|
|
while match_state.phase != LuneMatch.Phase.OVER and match_state.round_number <= limit:
|
|
for slot: int in range(6):
|
|
LuneBot.plan(match_state, slot)
|
|
match_state.begin_combat()
|
|
var combat: LuneCombat = match_state.combat
|
|
match_state.finish_combat()
|
|
if combat.end_reason == "timeout" and "--diagnose" in OS.get_cmdline_user_args():
|
|
for unit: LuneUnit in combat.units:
|
|
if unit.alive and unit.is_mobile():
|
|
print(" STUCK t%d %s %s %s pos=%s prog=%.1f base=%s target=%s hp=%.0f/%.0f" % [unit.team, unit.lane, unit.kind, unit.def.get("id", ""), str(unit.position.snapped(Vector2(.1, .1))), unit.progress, unit.reached_base, (unit.target.def.get("id", "") if unit.target != null else "-"), unit.hp, unit.max_hp])
|
|
var h: Dictionary = match_state.history[-1]
|
|
print("R%02d %s %.1fs buildings %s lost %s gold %s" % [h.round, h.end_reason, h.combat_time, str(h.buildings), str(h.lost), str(h.gold)])
|
|
var result: String = "Testlimit erreicht / Partie nicht abgeschlossen"
|
|
if match_state.phase == LuneMatch.Phase.OVER:
|
|
result = "Unentschieden" if match_state.winner == LuneMatch.DRAW else ("Sieg Blau" if match_state.winner == 0 else "Sieg Rot")
|
|
var report := {"seed": seed, "result": result, "rounds": match_state.history.size(), "note": match_state.end_note,
|
|
"profiles": range(6).map(func(s: int) -> String: return LuneBot.profile_for(match_state, s)),
|
|
"bunds": match_state.players.map(func(p: Dictionary) -> String: return p.bund),
|
|
"wall_ms": Time.get_ticks_msec() - started, "history": match_state.history}
|
|
reports.append(report)
|
|
print("SIM_MATCH seed=%d %s after %d rounds (%s) in %d ms" % [seed, result, report.rounds, report.note, report.wall_ms])
|
|
if not out.is_empty():
|
|
var file := FileAccess.open(out, FileAccess.WRITE)
|
|
file.store_string(JSON.stringify(reports, "\t"))
|
|
file.close()
|
|
get_tree().quit()
|