extends Node ## Boss strategy comparison from one identical mid-game state (seed replay to round 7). ## Variants in round 8 for Blau (and Rot for the contest); rounds 8-11 are then played by ## the ordinary bots without the boss-hunting profile, so only the scenario differs. ## Run: godot --headless --path . res://tools/luneraria/boss_scenarios.tscn -- --seed=3 const SCENARIOS: Array[String] = ["kein_versuch", "gescheitert", "erfolgreich", "umkaempft", "ueberinvestition"] func _ready() -> void: call_deferred("run") func profile(state: LuneMatch, slot: int) -> String: var name: String = LuneBot.profile_for(state, slot) return "ausgewogen" if name == "bossjagd" else name func replay(seed: int) -> LuneMatch: var state := LuneMatch.new() state.setup({"seed": seed, "human_slot": -1}) while state.round_number < 8 and state.phase != LuneMatch.Phase.OVER: for slot: int in range(6): LuneBot.plan(state, slot, profile(state, slot)) state.begin_combat() state.finish_combat() return state func send(state: LuneMatch, slot: int, count: int, strongest: bool, hero: bool) -> int: var roster: Array = (state.players[slot].draft.roster as Array).duplicate() roster.sort_custom(func(a: Dictionary, b: Dictionary) -> bool: return int(a.seq) < int(b.seq) if int(a.invested) == int(b.invested) else (int(a.invested) > int(b.invested)) == strongest) var value: int = 0 for index: int in range(mini(count, roster.size())): if state.submit(slot, {"type": "assign", "uid": roster[index].uid, "lane": "grotto"}).ok: value += int(roster[index].invested) var champion: Dictionary = state.players[slot].draft.hero if hero and not champion.is_empty(): state.submit(slot, {"type": "assign", "uid": champion.uid, "lane": "grotto"}) return value func building_hp(state: LuneMatch, team: int) -> float: var total: float = 0.0 for id: String in state.building_hp: if id.begins_with("blau" if team == 0 else "rot"): total += float(state.building_hp[id]) return total func team_gold(state: LuneMatch, team: int) -> int: var total: int = 0 for slot: int in range(team * 3, team * 3 + 3): total += int(state.players[slot].gold) return total func run() -> void: var seed: int = 3 for arg: String in OS.get_cmdline_user_args(): if arg.begins_with("--seed="): seed = int(arg.substr(7)) var results: Array = [] for scenario: String in SCENARIOS: var state: LuneMatch = replay(seed) var start := {"blau_hp": building_hp(state, 0), "rot_hp": building_hp(state, 1), "blau_gold": team_gold(state, 0), "rot_gold": team_gold(state, 1)} var row := {"scenario": scenario, "sent_value": 0, "rot_sent_value": 0} for slot: int in range(6): LuneBot.plan(state, slot, profile(state, slot)) match scenario: "gescheitert": row.sent_value = send(state, 1, 3, false, false) "erfolgreich": row.sent_value = send(state, 1, 8, true, true) "umkaempft": row.sent_value = send(state, 1, 8, true, true) row.rot_sent_value = send(state, 4, 8, true, true) "ueberinvestition": row.sent_value = send(state, 1, 12, true, true) row.sent_value += send(state, 0, 4, true, false) + send(state, 2, 4, true, false) var blau_mid_before: int = state.towers_alive(0, "mid") state.begin_combat() state.finish_combat() row.grotto = str(state.last_settlement.boss) row.claim_team = int(state.boss.claim.team) if not (state.boss.claim as Dictionary).is_empty() else -1 row.blau_mid_towers_lost_r8 = blau_mid_before - state.towers_alive(0, "mid") row.blau_hp_after_r8 = building_hp(state, 0) - float(start.blau_hp) var rot_hp_before_r9: float = building_hp(state, 1) var boss_bounty_rot: int = 0 for extra: int in range(3): if state.phase == LuneMatch.Phase.OVER: break for slot: int in range(6): LuneBot.plan(state, slot, profile(state, slot)) state.begin_combat() var deployed: bool = state.combat.units.any(func(u: LuneUnit) -> bool: return u.kind == "boss" and u.zone == "lane") state.finish_combat() if deployed: row.boss_round = state.combat.round_number row.rot_hp_lost_boss_round = rot_hp_before_r9 - building_hp(state, 1) for event: Dictionary in state.combat.events: if event.kind == "boss" and event.zone == "lane": boss_bounty_rot = int(event.bounty) row.boss_killed = boss_bounty_rot > 0 row.boss_bounty_to_rot = boss_bounty_rot row.blau_hp_delta = building_hp(state, 0) - float(start.blau_hp) row.rot_hp_delta = building_hp(state, 1) - float(start.rot_hp) row.blau_gold_delta = team_gold(state, 0) - int(start.blau_gold) row.rot_gold_delta = team_gold(state, 1) - int(start.rot_gold) row.phase = "vorbei" if state.phase == LuneMatch.Phase.OVER else "läuft" row.winner = state.winner results.append(row) print("SCENARIO ", JSON.stringify(row)) var file := FileAccess.open("res://docs/luneraria/logs/m2/boss-scenarios-seed%d.json" % seed, FileAccess.WRITE) file.store_string(JSON.stringify(results, "\t")) file.close() get_tree().quit()