Bots planen Items/Champions vor der Armee und helfen deterministisch, damit Spiegelmatches fair bleiben. Boss und Profile neu abgestimmt; Balance-, Stress- und Volks-Werkzeuge sowie Kartenprops ergänzt.
55 lines
3.3 KiB
Python
55 lines
3.3 KiB
Python
#!/usr/bin/env python3
|
|
"""Summarise a balance_run JSON: side fairness, profile and Bund strength, match length."""
|
|
import json, sys
|
|
from collections import Counter, defaultdict
|
|
rows = json.load(open(sys.argv[1]))
|
|
side = Counter(); lengths = []; limit = 0; timeouts = 0; consistent = 0
|
|
prof_w = Counter(); prof_n = Counter(); bund_w = Counter(); bund_n = Counter(); boss = Counter()
|
|
max_combat = 0.0; hero_levels = []
|
|
for row in rows:
|
|
results = []
|
|
for key, flip in (("normal", False), ("swapped", True)):
|
|
game = row[key]
|
|
profiles = row["profiles"][3:] + row["profiles"][:3] if flip else row["profiles"]
|
|
bunds = row["bunds"][3:] + row["bunds"][:3] if flip else row["bunds"]
|
|
side[game["result"]] += 1
|
|
lengths.append(game["rounds"]); timeouts += game["timeouts"]; max_combat = max(max_combat, game["max_combat"])
|
|
hero_levels += [l for l in game["hero_levels"] if l > 0]
|
|
for e in game["boss"]:
|
|
boss[e.split(":")[1]] += 1
|
|
if game["result"] == "testlimit":
|
|
limit += 1
|
|
continue
|
|
winner = 0 if game["result"] == "blau" else 1 if game["result"] == "rot" else -1
|
|
# Winning composition: which original half won (0 = first three slots of the row).
|
|
results.append(-1 if winner < 0 else (winner if not flip else 1 - winner))
|
|
for slot in range(6):
|
|
prof_n[profiles[slot]] += 1; bund_n[bunds[slot]] += 1
|
|
if slot // 3 == winner:
|
|
prof_w[profiles[slot]] += 1; bund_w[bunds[slot]] += 1
|
|
if len(results) == 2 and results[0] == results[1] and results[0] >= 0:
|
|
consistent += 1
|
|
games = sum(side.values())
|
|
print(f"Partien {games} · Ergebnisse {dict(side)} · Testlimit {limit} · Zeitfenster-Kämpfe {timeouts} · längster Kampf {max_combat:.0f} s")
|
|
print(f"Wellen bis Ende: min {min(lengths)} · median {sorted(lengths)[len(lengths)//2]} · max {max(lengths)}")
|
|
print(f"Seitentausch: dieselbe Zusammensetzung gewinnt beide Male in {consistent}/{len(rows)} Paaren")
|
|
print("Profile (Siege/Einsätze):", {p: f"{prof_w[p]}/{prof_n[p]} ({100*prof_w[p]/max(1,prof_n[p]):.0f} %)" for p in sorted(prof_n)})
|
|
print("Bünde:", {b: f"{bund_w[b]}/{bund_n[b]} ({100*bund_w[b]/max(1,bund_n[b]):.0f} %)" for b in sorted(bund_n)})
|
|
print("Boss:", dict(boss), "· Heldenstufen am Ende (Median):", sorted(hero_levels)[len(hero_levels)//2] if hero_levels else "-")
|
|
|
|
if rows and all(row.get("matchup") for row in rows):
|
|
# Pure profile duels: wins of the row's first profile over its second, both sides.
|
|
print("Duelle (Siege Zeile gegen Spalte, je 2 Partien mit Seitentausch):")
|
|
names = sorted({row["profiles"][0] for row in rows} | {row["profiles"][3] for row in rows})
|
|
wins = defaultdict(int)
|
|
for row in rows:
|
|
a, b = row["profiles"][0], row["profiles"][3]
|
|
for key, flip in (("normal", False), ("swapped", True)):
|
|
r = row[key]["result"]
|
|
if r in ("blau", "rot"):
|
|
first_won = (r == "blau") != flip
|
|
wins[(a, b) if first_won else (b, a)] += 1
|
|
print(" " + " ".join(f"{n[:6]:>7}" for n in names) + " gesamt")
|
|
for n in names:
|
|
total = sum(wins[(n, m)] for m in names)
|
|
print(f"{n[:12]:<12}" + " ".join(f"{('-' if n == m else wins[(n, m)]):>7}" for m in names) + f" {total}/{2 * (len(names) - 1)}")
|