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()