Files
tft/backend/tests/test_arena.py
2026-09-07 07:44:09 +02:00

104 lines
4.2 KiB
Python

"""Arena: Boards auf das Kampffeld, eigene Positionen, leere Seiten."""
import random
from tft.sim.arena import BOARD_COLS
from tft.sim import arena, hexgrid
STATS = {"hp": 800, "armor": 20, "magicResist": 20, "damage": 50,
"damageByStar": [0, 50, 100, 200], "attackSpeed": 1.0, "range": 1,
"mana": 0, "initialMana": 0, "critChance": 0.0, "critMultiplier": 1.4}
ARTIFACT = {"static": {
"units": {"A": {"traits": [], "stats": STATS},
"B": {"traits": [], "stats": {**STATS, "range": 4}}},
"items": {"HP": {"effects": {"Health": 500}}},
"traits": {},
}}
def test_front_row_of_both_sides_faces_the_other():
"""Reihe 0 des Spielers ist die vorderste — sonst laufen alle aneinander vorbei."""
a = arena.from_hex(3, "a")
b = arena.from_hex(3, "b")
assert hexgrid.distance((a["col"], a["row"]), (b["col"], b["row"])) == 1
def test_back_row_is_furthest_away():
a = arena.from_hex(3 + 3 * BOARD_COLS, "a")
b = arena.from_hex(3 + 3 * BOARD_COLS, "b")
assert hexgrid.distance((a["col"], a["row"]), (b["col"], b["row"])) == 7
def test_own_positions_are_used():
board = [{"api_name": "A", "stars": 1, "hex": 3}]
out = arena.placed(board, "a", ARTIFACT["static"]["units"])
assert (out[0]["col"], out[0]["row"]) == (3, 3)
def test_boards_without_positions_fall_back_to_the_lineup():
"""Endboards aus der Riot-API tragen keine Felder."""
board = [{"api_name": "A", "stars": 1}, {"api_name": "B", "stars": 1}]
out = arena.placed(board, "a", ARTIFACT["static"]["units"])
assert all("col" in e and "row" in e for e in out)
# Nahkämpfer vorne, Fernkämpfer hinten.
front = next(e for e in out if e["api_name"] == "A")
back = next(e for e in out if e["api_name"] == "B")
assert front["row"] > back["row"]
def test_two_units_never_share_a_cell():
board = [{"api_name": "A", "stars": 1, "hex": 3},
{"api_name": "A", "stars": 1, "hex": 3}]
out = arena.placed(board, "a", ARTIFACT["static"]["units"])
assert len({(e["col"], e["row"]) for e in out}) == 2
def test_an_empty_board_loses_at_once():
board = [{"api_name": "A", "stars": 1, "hex": 0}]
res, log = arena.fight([], board, ARTIFACT, random.Random(1))
assert res.winner == "b" and res.frames == 0 and log is None
res, _ = arena.fight(board, [], ARTIFACT, random.Random(1))
assert res.winner == "a"
assert arena.fight([], [], ARTIFACT, random.Random(1))[0].winner is None
def test_items_reach_the_fighters():
board = [{"api_name": "A", "stars": 1, "hex": 0, "items": ["HP"]}]
plain = arena.side([{"api_name": "A", "stars": 1, "hex": 0}], "a", ARTIFACT)
armed = arena.side(board, "a", ARTIFACT)
assert armed[0].max_hp == plain[0].max_hp + 500
def test_the_bigger_board_wins():
big = [{"api_name": "A", "stars": 1, "hex": h} for h in range(4)]
small = [{"api_name": "A", "stars": 1, "hex": 0}]
assert arena.fight(big, small, ARTIFACT, random.Random(1))[0].winner == "a"
def test_replay_returns_a_playable_log():
"""Feldmaße, Kämpfer und Ereignisse — mehr braucht die Anzeige nicht."""
a = [{"api_name": "A", "stars": 1, "hex": h} for h in range(3)]
b = [{"api_name": "A", "stars": 1, "hex": h} for h in range(2)]
_, log = arena.fight(a, b, ARTIFACT, random.Random(1), record=True)
assert log["cols"] == hexgrid.COLS and log["rows"] == hexgrid.ROWS
assert len(log["units"]) == 5
assert {u["slot"] for u in log["units"]} == set(range(5))
kinds = {e["e"] for e in log["events"]}
assert "spawn" in kinds and "hit" in kinds
assert log["winner"] == "a"
def test_replay_is_none_without_a_fight():
_, log = arena.fight([], [{"api_name": "A", "stars": 1, "hex": 0}],
ARTIFACT, random.Random(1), record=True)
assert log is None
def test_replay_matches_the_fight():
"""Sonst zeigt die Anzeige etwas anderes als das Spiel gewertet hat."""
a = [{"api_name": "A", "stars": 1, "hex": h} for h in range(3)]
b = [{"api_name": "B", "stars": 1, "hex": h} for h in range(3)]
res, log = arena.fight(a, b, ARTIFACT, random.Random(3), record=True)
plain, _ = arena.fight(a, b, ARTIFACT, random.Random(3))
assert log["winner"] == res.winner == plain.winner