Kalibrierung: Holdout pro Match, Kanarienvogel ignoriert Summons/Sonder-Items, Baseline-Vergleich

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
team3
2026-07-23 08:01:43 +02:00
parent ca934ed942
commit 20fd8c15f3
5 changed files with 30 additions and 11 deletions

View File

@@ -31,6 +31,7 @@ FAKE_MATCH = {
}
STATIC = {
"meta": {"set": 17},
"units": {"TFT17_Rammus": {}},
"traits": {"TFT17_ResistTank": {}},
"items": {"TFT_Item_InfinityEdge": {}},
@@ -61,7 +62,7 @@ def test_validate_ids_clean():
def test_validate_ids_flags_unknown():
rows = extract_match(FAKE_MATCH)
static = {"units": {}, "traits": {}, "items": {}, "augments": {}}
static = {"meta": {"set": 17}, "units": {}, "traits": {}, "items": {}, "augments": {}}
unknown = validate_ids(rows, static)
assert unknown["unit:TFT17_Rammus"] == 8
assert unknown["item:TFT_Item_InfinityEdge"] == 8

View File

@@ -82,13 +82,17 @@ def main() -> None:
from tft import db
from tft.model import artifact as artifact_mod
from tft.model.calibrate import calibrate
from tft.staticdata.fetch import load_static
conn = db.connect()
art = artifact_mod.load(current_set())
result = calibrate(conn, art, holdout_only=True)
learned_art = artifact_mod.load(current_set())
baseline_art = artifact_mod.build(load_static(), {})
r_base = calibrate(conn, baseline_art, holdout_only=True)
r_learned = calibrate(conn, learned_art, holdout_only=True)
conn.close()
print(f"holdout matches: {result['matches']}")
print(f"mean spearman (score vs placement): {result['mean_spearman']:.3f}")
print(f"holdout matches: {r_learned['matches']}")
print(f"spearman baseline: {r_base['mean_spearman']:.3f}")
print(f"spearman learned: {r_learned['mean_spearman']:.3f}")
elif args.command == "autoplay":
from tft.constants.loader import load_constants

View File

@@ -39,16 +39,29 @@ def extract_match(match: dict) -> list[tuple]:
return rows
BENIGN_UNIT_HINTS = ("minion", "summon", "pve", "follower")
def validate_ids(rows: list[tuple], static: dict) -> Counter:
"""Count ids in endboards that are unknown to the static data (patch-drift canary)."""
"""Count ids in endboards that are unknown to the static data (patch-drift canary).
Summons/PvE-Units und Sonder-Items (Radiant, Ornn, ...) sind erwartbar
unbekannt und werden ignoriert.
"""
unknown: Counter = Counter()
known_items = set(static["items"]) | set(static["augments"])
set_prefix = f"TFT{static['meta']['set']}_"
for row in rows:
for u in json.loads(row[8]):
if u["character_id"] not in static["units"]:
unknown[f"unit:{u['character_id']}"] += 1
api = u["character_id"]
if api not in static["units"]:
if not any(h in api.lower() for h in BENIGN_UNIT_HINTS):
unknown[f"unit:{api}"] += 1
for item in u.get("itemNames", []):
if item not in known_items:
if item in known_items:
continue
# Nur Items des aktuellen Sets oder generische zählen als Drift.
if item.startswith("TFT_Item_") or item.startswith(set_prefix):
unknown[f"item:{item}"] += 1
for t in json.loads(row[9]):
if t["name"] not in static["traits"]:

View File

@@ -49,7 +49,8 @@ def calibrate(conn, artifact: dict, holdout_only: bool = False) -> dict:
set_number = artifact["meta"]["set"]
where = "WHERE set_number = ?"
if holdout_only:
where += " AND rowid % 5 = 0"
# Holdout pro MATCH (nicht pro Board), sonst gibt es keine vollständigen 8er.
where += " AND substr(match_id, -1) IN ('0', '5')"
rows = conn.execute(
f"SELECT match_id, placement, units, augments FROM endboards {where}",
(set_number,),

View File

@@ -80,7 +80,7 @@ def learn(rows: list[tuple]) -> dict:
def learn_from_db(conn, set_number: int, exclude_holdout: bool = True) -> dict:
where = "WHERE set_number = ?"
if exclude_holdout:
where += " AND rowid % 5 != 0"
where += " AND substr(match_id, -1) NOT IN ('0', '5')"
rows = conn.execute(
f"SELECT placement, units, traits, augments FROM endboards {where}",
(set_number,),