118 lines
4.7 KiB
Python
118 lines
4.7 KiB
Python
"""Carries: Kontrollen, Zielstufe, Zerlegung."""
|
|
|
|
from tft.model import carries
|
|
|
|
UNITS = {a: {"cost": 3, "traits": ["T"]} for a in ("A", "B", "CARRY", "TANK")}
|
|
UNITS["TEUER"] = {"cost": 5, "traits": ["T"]}
|
|
TRAITS = {"T": {"breakpoints": [{"min_units": 1}, {"min_units": 3}]}}
|
|
|
|
|
|
def board(place, level, items_total, *ents):
|
|
"""ents: (api, sterne, itemzahl)"""
|
|
return {"place": place, "level": level, "items": items_total,
|
|
"entities": list(ents)}
|
|
|
|
|
|
def prepared(boards):
|
|
return carries.prepare(boards, UNITS, TRAITS)
|
|
|
|
|
|
def test_other_three_stars_are_part_of_the_comparison_group():
|
|
"""Wer eine Unit auf 3★ hat, hatte Gold übrig — das Board ist deshalb gut.
|
|
|
|
Die eigenen 3★ zählen dabei nicht mit, sonst würde man den Effekt
|
|
misst, den man erklären will.
|
|
"""
|
|
b = prepared([board(1, 9, 3, ("A", 3, 3), ("B", 3, 0))])[0]
|
|
assert carries.cell(b, 3) == (9, 3, 1) # eigene 3★ abgezogen
|
|
assert carries.cell(b, 2) == (9, 3, 2) # aus Sicht einer 2★-Unit beide
|
|
|
|
|
|
def test_reroll_carries_are_shown_on_three_stars():
|
|
"""Auf 2★ negativ, auf 3★ positiv: so spielt man diesen Champion."""
|
|
boards = prepared([board(6, 9, 3, ("A", 2, 3)) for _ in range(60)]
|
|
+ [board(1, 9, 3, ("A", 3, 3)) for _ in range(60)])
|
|
base = carries.baseline(boards)
|
|
out = carries.values(boards, base, UNITS, min_boards=10)
|
|
assert out["A"]["stars"] == 3 and out["A"]["edge"] > 0
|
|
|
|
|
|
def test_expensive_units_are_never_shown_on_three_stars():
|
|
"""Ein 5-Kosten auf 3★ heißt: das Spiel war schon gewonnen.
|
|
|
|
In den Daten holen 5-Kosten auf 3★ in 98 % der Fälle Platz 1.
|
|
"""
|
|
boards = prepared([board(6, 9, 3, ("TEUER", 2, 3)) for _ in range(60)]
|
|
+ [board(1, 9, 3, ("TEUER", 3, 3)) for _ in range(60)])
|
|
base = carries.baseline(boards)
|
|
out = carries.values(boards, base, UNITS, min_boards=10)
|
|
assert out["TEUER"]["stars"] == 2
|
|
|
|
|
|
def test_units_below_the_rate_are_kept_but_not_carries():
|
|
"""Ein Tank ohne Items bleibt in der Tabelle — er ist der Maßstab.
|
|
|
|
Der Buchstabe im Reiter vergleicht mit allen Units, nicht nur mit
|
|
den Carries.
|
|
"""
|
|
boards = prepared([board(1, 9, 3, ("CARRY", 2, 3), ("TANK", 2, 0))
|
|
for _ in range(60)]
|
|
+ [board(4, 9, 3, ("CARRY", 2, 3), ("TANK", 2, 3))
|
|
for _ in range(20)])
|
|
base = carries.baseline(boards)
|
|
out = carries.values(boards, base, UNITS, min_boards=10)
|
|
assert out["CARRY"]["carry"] and not out["TANK"]["carry"]
|
|
|
|
|
|
def test_rare_units_are_dropped():
|
|
boards = prepared([board(1, 9, 3, ("A", 2, 3)) for _ in range(9)])
|
|
base = carries.baseline(boards)
|
|
assert carries.values(boards, base, UNITS, min_boards=10) == {}
|
|
|
|
|
|
def test_reasons_separate_unit_from_the_rest():
|
|
"""Zwei Carries, gleiche Traits, aber einer steht neben besseren Units.
|
|
|
|
Der Unterschied muss im Topf "rest" landen, nicht in "unit".
|
|
"""
|
|
boards = prepared(
|
|
[board(2, 9, 3, ("A", 2, 3), ("TANK", 2, 0)) for _ in range(400)]
|
|
+ [board(2, 9, 3, ("B", 2, 3), ("CARRY", 2, 0)) for _ in range(400)]
|
|
+ [board(7, 9, 3, ("TANK", 2, 3)) for _ in range(400)]
|
|
+ [board(1, 9, 3, ("CARRY", 2, 3)) for _ in range(400)])
|
|
base = carries.baseline(boards)
|
|
beta, idx, own_of = carries._weights(boards, base, UNITS, carries.RIDGE)
|
|
why = carries.reasons(boards, base, own_of, idx, beta, min_boards=100)
|
|
a, b = why[("A", 2)], why[("B", 2)]
|
|
# B steht neben CARRY (gut), A neben TANK (schlecht).
|
|
assert b["rest"] > a["rest"]
|
|
|
|
|
|
def test_build_is_empty_without_data():
|
|
assert carries.build([], UNITS, TRAITS, min_boards=10) == {}
|
|
|
|
|
|
def test_enabler_measures_the_same_carry_with_and_without():
|
|
"""Stützt die Unit fremde Carries — oder steht sie nur daneben?
|
|
|
|
CARRY läuft mit B besser als ohne. B trägt selbst nie Items, kann den
|
|
Vorsprung also nicht selbst erklären.
|
|
"""
|
|
boards = prepared(
|
|
[board(2, 9, 3, ("CARRY", 2, 3), ("B", 2, 0)) for _ in range(400)]
|
|
+ [board(6, 9, 3, ("CARRY", 2, 3), ("TANK", 2, 0)) for _ in range(400)])
|
|
base = carries.baseline(boards)
|
|
lift = carries.enabler_edge(boards, base, UNITS, min_pair=100, min_total=100)
|
|
assert lift["B"] > 0 > lift["TANK"]
|
|
|
|
|
|
def test_supported_is_relative_to_comparable_boards():
|
|
"""Ein Carry mit starken Stützern liegt über dem Üblichen."""
|
|
boards = prepared(
|
|
[board(2, 9, 3, ("A", 2, 3), ("B", 2, 0)) for _ in range(400)]
|
|
+ [board(6, 9, 3, ("CARRY", 2, 3), ("TANK", 2, 0)) for _ in range(400)])
|
|
base = carries.baseline(boards)
|
|
lift = {"B": 0.4, "TANK": -0.4}
|
|
prop = carries.supported(boards, base, lift, min_boards=100)
|
|
assert prop[("A", 2)] > prop[("CARRY", 2)]
|