Files
tft/backend/tft/model/score.py
2026-07-23 00:42:03 +02:00

73 lines
2.5 KiB
Python

"""The one board-scoring entry point. Sim, bots, calibration, and UI all call this."""
from tft.model import baseline
def active_trait_tiers(board_units: list[dict], static_traits: dict, static_units: dict) -> dict:
"""trait api_name -> reached breakpoint ordinal (1-based), only active traits."""
counts: dict[str, int] = {}
for u in board_units:
unit = static_units.get(u["api_name"])
if not unit:
continue
for trait in set(unit["traits"]):
counts[trait] = counts.get(trait, 0) + 1
tiers = {}
for trait, n in counts.items():
info = static_traits.get(trait)
if not info:
continue
ordinal = 0
for i, bp in enumerate(info["breakpoints"], start=1):
if n >= bp["min_units"]:
ordinal = i
if ordinal:
tiers[trait] = ordinal
return tiers
def score_board(board_units: list[dict], augments: list[str], artifact: dict) -> float:
"""board_units: [{api_name, stars, items: [item api names]}]."""
static_units = artifact["static"]["units"]
static_traits = artifact["static"]["traits"]
learned = artifact.get("learned", {})
item_mults = learned.get("items", {})
trait_mults = learned.get("traits", {})
augment_mults = learned.get("augments", {})
pair_lifts = learned.get("pairs", {})
roles = artifact.get("roles", {})
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"])
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)
if baseline.item_fits_role(item, role):
mult *= baseline.ROLE_FIT_BONUS
value *= mult
total += value
for trait, ordinal in active_trait_tiers(board_units, static_traits, static_units).items():
default = baseline.DEFAULT_TRAIT_TIER_MULT[
min(ordinal, len(baseline.DEFAULT_TRAIT_TIER_MULT) - 1)
]
total *= trait_mults.get(f"{trait}@{ordinal}", default)
for augment in augments:
total *= augment_mults.get(augment, 1.0)
names = sorted({u["api_name"] for u in board_units})
lift_sum = sum(
pair_lifts.get(f"{a}|{b}", 0.0)
for i, a in enumerate(names)
for b in names[i + 1 :]
)
total *= 1 + min(max(lift_sum * 0.01, -0.10), 0.10)
return total