Unit-Stärken präzisiert: Stat-Proxy-Multiplikatoren + gelernte Unit-Gewichte

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
team3
2026-07-23 08:09:33 +02:00
parent 20fd8c15f3
commit 7c72761190
6 changed files with 97 additions and 6 deletions

View File

@@ -45,6 +45,7 @@ def build(static: dict, learned: dict | None = None, extra_meta: dict | None = N
"augments": static["augments"],
},
"roles": roles,
"stat_mults": baseline.compute_stat_mults(static["units"]),
"item_pool": craftable_items(static["items"]),
"learned": learned or {},
}

View File

@@ -6,11 +6,49 @@ def unit_value(cost: int, stars: int) -> float:
def stat_proxies(stats: dict) -> dict:
ehp = stats["hp"] * (1 + (stats["armor"] + stats["magicResist"]) / 2 / 100)
auto_dps = stats["damage"] * stats["attackSpeed"]
mana_gap = max(stats["mana"] - stats["initialMana"], 1)
cast_rate = stats["attackSpeed"] * 10 / mana_gap
return {"ehp": ehp, "auto_dps": auto_dps, "cast_rate": cast_rate}
# cdragon liefert für manche Units null-Werte — als 0 behandeln.
hp = stats.get("hp") or 0
resists = ((stats.get("armor") or 0) + (stats.get("magicResist") or 0)) / 2
ad = stats.get("damage") or 0
aspd = stats.get("attackSpeed") or 0
mana_gap = max((stats.get("mana") or 0) - (stats.get("initialMana") or 0), 1)
return {
"ehp": hp * (1 + resists / 100),
"auto_dps": ad * aspd,
"cast_rate": aspd * 10 / mana_gap,
}
STAT_MULT_RANGE = (0.9, 1.1)
def compute_stat_mults(static_units: dict) -> dict:
"""Stat-Stärke relativ zur eigenen Kostenstufe: (eHP + Offense) / 2, gekappt.
Offense = Maximum aus Auto-DPS und Cast-Rate (je normalisiert), damit
Caster nicht gegen Auto-Attacker abfallen.
"""
proxies = {api: stat_proxies(u["stats"]) for api, u in static_units.items()}
by_cost: dict[int, list[str]] = {}
for api, u in static_units.items():
by_cost.setdefault(u["cost"], []).append(api)
mults = {}
for apis in by_cost.values():
n = len(apis)
avg = {
key: sum(proxies[a][key] for a in apis) / n or 1.0
for key in ("ehp", "auto_dps", "cast_rate")
}
for a in apis:
ehp_norm = proxies[a]["ehp"] / avg["ehp"]
offense = max(
proxies[a]["auto_dps"] / avg["auto_dps"],
proxies[a]["cast_rate"] / avg["cast_rate"],
)
raw = (ehp_norm + offense) / 2
mults[a] = round(min(max(raw, STAT_MULT_RANGE[0]), STAT_MULT_RANGE[1]), 4)
return mults
def classify_role(unit: dict) -> str:

View File

@@ -28,6 +28,7 @@ def learn(rows: list[tuple]) -> dict:
trait_placements = defaultdict(list)
item_placements = defaultdict(list)
augment_placements = defaultdict(list)
unit_placements = defaultdict(list)
unit_top4 = defaultdict(int)
pair_top4 = defaultdict(int)
n_boards = 0
@@ -40,6 +41,8 @@ def learn(rows: list[tuple]) -> dict:
units = json.loads(units_json)
names = sorted({u["character_id"] for u in units})
for name in names:
unit_placements[name].append(placement)
for u in units:
for item in u.get("itemNames", []):
item_placements[item].append(placement)
@@ -69,6 +72,7 @@ def learn(rows: list[tuple]) -> dict:
pairs[f"{a}|{b}"] = round(lift, 4)
return {
"units": {k: round(_multiplier(v), 4) for k, v in unit_placements.items()},
"traits": {k: round(_multiplier(v), 4) for k, v in trait_placements.items()},
"items": {k: round(_multiplier(v), 4) for k, v in item_placements.items()},
"augments": {k: round(_multiplier(v), 4) for k, v in augment_placements.items()},

View File

@@ -32,18 +32,24 @@ def score_board(board_units: list[dict], augments: list[str], artifact: dict) ->
static_units = artifact["static"]["units"]
static_traits = artifact["static"]["traits"]
learned = artifact.get("learned", {})
unit_mults = learned.get("units", {})
item_mults = learned.get("items", {})
trait_mults = learned.get("traits", {})
augment_mults = learned.get("augments", {})
pair_lifts = learned.get("pairs", {})
roles = artifact.get("roles", {})
stat_mults = artifact.get("stat_mults", {})
total = 0.0
for u in board_units:
unit = static_units.get(u["api_name"])
if not unit:
continue
value = baseline.unit_value(unit["cost"], u["stars"])
value = (
baseline.unit_value(unit["cost"], u["stars"])
* stat_mults.get(u["api_name"], 1.0)
* unit_mults.get(u["api_name"], 1.0)
)
role = roles.get(u["api_name"]) or baseline.classify_role(unit)
for item in u.get("items", []):
mult = item_mults.get(item, baseline.DEFAULT_ITEM_MULT)