"""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)