80 lines
3.0 KiB
Python
80 lines
3.0 KiB
Python
"""Boardbewertung: Merkmale, Baumdurchlauf, Grenzfälle."""
|
|
|
|
import pytest
|
|
|
|
from tft.model import rating
|
|
|
|
STATIC = {
|
|
"units": {
|
|
"U_TANK": {"cost": 1, "traits": ["T_BRAWL"], "name": "Tank"},
|
|
"U_CARRY": {"cost": 4, "traits": ["T_BRAWL", "T_SHOT"], "name": "Carry"},
|
|
"U_FIVE": {"cost": 5, "traits": ["T_SHOT"], "name": "Five"},
|
|
},
|
|
"traits": {
|
|
"T_BRAWL": {"breakpoints": [{"min_units": 2}, {"min_units": 4}]},
|
|
"T_SHOT": {"breakpoints": [{"min_units": 2}]},
|
|
},
|
|
}
|
|
|
|
|
|
def board(*specs):
|
|
return [{"api_name": a, "stars": s, "items": list(i)} for a, s, i in specs]
|
|
|
|
|
|
@pytest.fixture
|
|
def art():
|
|
return {"static": STATIC, "component_pool": ["C_SWORD"], "item_pool": ["I_BLADE"],
|
|
"rating": {"tables": {}}}
|
|
|
|
|
|
def test_trait_counts_and_tiers():
|
|
b = board(("U_TANK", 1, []), ("U_CARRY", 1, []), ("U_FIVE", 1, []))
|
|
counts = rating.trait_counts(b, STATIC["units"])
|
|
assert counts == {"T_BRAWL": 2, "T_SHOT": 2}
|
|
assert rating.trait_tiers(counts, STATIC["traits"]) == {"T_BRAWL": 1, "T_SHOT": 1}
|
|
# Eine Unit unter dem Breakpoint aktiviert nichts.
|
|
single = rating.trait_counts(board(("U_TANK", 1, [])), STATIC["units"])
|
|
assert rating.trait_tiers(single, STATIC["traits"]) == {}
|
|
|
|
|
|
def test_features_are_stable_in_length(art):
|
|
empty = rating.features([], 1, art)
|
|
full = rating.features(board(("U_CARRY", 3, ["I_BLADE", "C_SWORD"])), 9, art)
|
|
assert len(empty) == len(full)
|
|
assert all(isinstance(x, (int, float)) for x in empty + full)
|
|
|
|
|
|
def test_unknown_units_are_ignored(art):
|
|
known = rating.features(board(("U_TANK", 1, [])), 5, art)
|
|
mixed = rating.features(board(("U_TANK", 1, []), ("NOPE", 3, [])), 5, art)
|
|
assert known == mixed
|
|
|
|
|
|
def test_no_model_means_no_prediction(art):
|
|
assert rating.predict_placement(board(("U_TANK", 1, [])), 5, art) is None
|
|
assert rating.board_rating(board(("U_TANK", 1, [])), 5, art) is None
|
|
|
|
|
|
def test_tree_walk_and_clamping(art):
|
|
# Ein Baum, der nur auf die Unit-Zahl schaut (Merkmal 4).
|
|
art["rating"] = {"tables": {}, "base": 4.0, "lr": 1.0,
|
|
"trees": [[4, 1.5, 2.0, -2.0]]}
|
|
few = rating.predict_placement(board(("U_TANK", 1, [])), 3, art)
|
|
many = rating.predict_placement(
|
|
board(("U_TANK", 1, []), ("U_CARRY", 1, []), ("U_FIVE", 1, [])), 9, art)
|
|
assert few == 6.0 and many == 2.0
|
|
assert rating.board_rating(board(("U_TANK", 1, [])), 3, art) == pytest.approx(-1.5)
|
|
|
|
# Ausreißer werden auf 1 bis 8 gestutzt — es gibt keinen Platz 0.
|
|
art["rating"]["trees"] = [[4, 1.5, 99.0, -99.0]]
|
|
assert rating.predict_placement(board(("U_TANK", 1, [])), 3, art) == 8.0
|
|
assert rating.predict_placement(
|
|
board(("U_TANK", 1, []), ("U_CARRY", 1, [])), 9, art) == 1.0
|
|
|
|
|
|
def test_learned_tables_reach_the_features(art):
|
|
art["rating"]["tables"] = {"unit": {"U_CARRY": 1.5}}
|
|
plain = rating.features(board(("U_TANK", 1, [])), 5, art)
|
|
good = rating.features(board(("U_CARRY", 1, [])), 5, art)
|
|
assert good != plain
|