114 lines
4.1 KiB
Python
114 lines
4.1 KiB
Python
"""Gruppen: Bindung aus den Pro-Comps, Stärke aus den Boards."""
|
|
|
|
from tft.model import pairs
|
|
from tft.staticdata import procomps
|
|
|
|
UNITS = {a: {"cost": 1, "traits": ["T"], "asset_names": [f"DA_{a}"]}
|
|
for a in "ABCDEF"}
|
|
|
|
|
|
def board(place, *units):
|
|
return {"place": place, "cell": (9, 3),
|
|
"entities": [(u, 2, 0) for u in units]}
|
|
|
|
|
|
def cell(b):
|
|
return b["cell"]
|
|
|
|
|
|
def test_bond_asks_about_the_rarer_unit():
|
|
"""Nicht "wie oft zusammen", sondern "wie oft zusammen, wenn möglich".
|
|
|
|
B steht nie ohne A. C steht halb so oft bei A. Rohe Zählung sähe C
|
|
vorne, weil C öfter vorkommt.
|
|
"""
|
|
groups = [["A", "B", "X", "Y"]] * 3 + [["A", "C", "X", "Y"]] * 2 \
|
|
+ [["C", "D", "X", "Y"]] * 2
|
|
w = pairs.weights(groups, pseudo=0)
|
|
assert w[("A", "B")] == 1.0
|
|
assert w[("A", "C")] == 0.5
|
|
|
|
|
|
def test_pseudo_counts_hold_back_single_sightings():
|
|
"""Ein Paar mit einer Nennung darf nicht wie eine Regel aussehen."""
|
|
groups = [["A", "B", "C", "D"]]
|
|
assert pairs.weights(groups, pseudo=0)[("A", "B")] == 1.0
|
|
assert pairs.weights(groups, pseudo=3)[("A", "B")] == 0.25
|
|
|
|
|
|
def test_every_pair_in_a_group_must_clear_the_threshold():
|
|
"""Ein Dritter darf nicht an nur einem der beiden hängen.
|
|
|
|
C bindet stark an A, aber gar nicht an B. Also kein Trio.
|
|
"""
|
|
bond = {("A", "B"): 0.9, ("A", "C"): 0.9, ("B", "C"): 0.1}
|
|
out = pairs.candidates(bond, UNITS, min_bond=0.4)
|
|
assert ("A", "B") in out and ("A", "C") in out
|
|
assert ("A", "B", "C") not in out
|
|
|
|
|
|
def test_a_group_needs_a_shared_trait():
|
|
"""Ohne das kommt Unsinn heraus: eine Comp hat acht bis neun Units,
|
|
zwei davon stehen nebeneinander, ohne etwas miteinander zu tun.
|
|
"""
|
|
units = {"A": {"traits": ["T"]}, "B": {"traits": ["T"]},
|
|
"C": {"traits": ["U"]}}
|
|
bond = {("A", "B"): 0.9, ("A", "C"): 0.9, ("B", "C"): 0.9}
|
|
out = pairs.candidates(bond, units, min_bond=0.4)
|
|
assert ("A", "B") in out
|
|
assert ("A", "C") not in out and ("A", "B", "C") not in out
|
|
|
|
|
|
def test_strength_is_measured_against_comparable_boards():
|
|
boards = [board(1, "A", "B", "C") for _ in range(400)] \
|
|
+ [board(7, "D", "E", "F") for _ in range(400)]
|
|
out = pairs.strength(boards, {(9, 3): 4.0}, cell,
|
|
[("A", "B"), ("D", "E")], min_boards=10)
|
|
assert out[("A", "B")]["edge"] > 0 > out[("D", "E")]["edge"]
|
|
assert out[("A", "B")]["boards"] == 400
|
|
|
|
|
|
def test_rare_groups_are_dropped():
|
|
boards = [board(1, "A", "B") for _ in range(5)]
|
|
assert pairs.strength(boards, {(9, 3): 4.0}, cell, [("A", "B")],
|
|
min_boards=10) == {}
|
|
|
|
|
|
def test_select_drops_overlapping_and_negative_groups():
|
|
"""Sonst steht dieselbe Gruppe zwanzigmal leicht abgewandelt da."""
|
|
scored = {("A", "B", "C"): {"edge": 0.5}, ("A", "B"): {"edge": 0.4},
|
|
("D", "E"): {"edge": 0.2}, ("A", "F"): {"edge": -0.1}}
|
|
assert pairs.select(scored) == [("A", "B", "C"), ("D", "E")]
|
|
|
|
|
|
def test_build_is_empty_without_input():
|
|
assert pairs.build([], [board(1, "A", "B")], {}, cell, UNITS) == {}
|
|
assert pairs.build([["A", "B", "C", "D"]], [], {}, cell, UNITS) == {}
|
|
|
|
|
|
def test_comps_reads_every_stage():
|
|
"""Level 4 bis 9 sind eigene Stufen — jede zählt als Beobachtung."""
|
|
doc = {
|
|
"content": {"comps": [{"data": {"$ref": "#/comp/x"}, "tier": "S"}]},
|
|
"refs": [{
|
|
"content_id": "x",
|
|
"metadata": {"title": "Test"},
|
|
"content": {
|
|
"unit_positions": {str(i): {"apiName": f"DA_{a}"}
|
|
for i, a in enumerate("ABCD")},
|
|
"variations": [
|
|
{"type": "comp",
|
|
"unit_positions": {str(i): {"apiName": f"DA_{a}"}
|
|
for i, a in enumerate("ABCDE")}},
|
|
{"type": "tierlist"},
|
|
# zu klein, fliegt raus
|
|
{"type": "comp",
|
|
"unit_positions": {"0": {"apiName": "DA_A"}}},
|
|
],
|
|
},
|
|
}],
|
|
}
|
|
out = procomps.comps(doc, UNITS)
|
|
assert [len(c["units"]) for c in out] == [4, 5]
|
|
assert all(c["tier"] == "S" for c in out)
|