from dataclasses import replace import pytest from dojo.board import Pos from dojo.events import ( Event, ExamTaken, FightKind, GameOver, KumiteFought, Moved, Source, TrackChanged, TurnStarted, ) from dojo.metrics import KUMITE_SECONDS, TURN_SECONDS, GameMetrics, measure, summarize from dojo.rules import VARIANT_A CORNERS = (0, 1) BOTS = ("balanced", "random") HOME_0 = Pos(0, 1) # quadrant 0 HOME_1 = Pos(0, 14) # quadrant 1 def fought(attacker: int, defender: int, kind: FightKind, *, exam: bool = False) -> KumiteFought: return KumiteFought(attacker, defender, 0, 0, 4, 3, True, kind, exam, cards=()) def run(events: list[Event]) -> GameMetrics: return measure(events, VARIANT_A, CORNERS, BOTS, scenario="test", seed=3) def test_measure_counts_fights_income_and_leads() -> None: metrics = run( [ TurnStarted(1, 0, 7), Moved(0, (HOME_0,)), TrackChanged(0, 0, 1, Source.SQUARE), TurnStarted(1, 1, 5), Moved(1, (HOME_0,)), TrackChanged(1, 0, 1, Source.SQUARE), fought(1, 0, FightKind.EQUAL), TrackChanged(1, 1, 4, Source.KUMITE), TrackChanged(0, 1, 0, Source.KUMITE), fought(1, 0, FightKind.UPSET), TurnStarted(2, 0, 6), fought(1, 0, FightKind.EXPECTED), # Player 1 defends: not part of a chain. TrackChanged(0, 0, 5, Source.CARD), fought(0, 1, FightKind.EQUAL, exam=True), ExamTaken(0, 1, passed=True), GameOver(0, 2), ] ) assert (metrics.kumite, metrics.upsets, metrics.expected_wins) == (3, 1, 1) assert metrics.max_chain == 2 assert (metrics.exam_attempts, metrics.exam_passes) == (1, 1) assert (metrics.income_square, metrics.income_kumite, metrics.income_card) == (2, 2, 5) assert (metrics.square_income_home, metrics.square_income_away) == (1, 1) # Leader: 0 (1-0), tie (1-1) keeps it, 1 (0-4), 0 (5-4). assert metrics.lead_changes == 2 assert metrics.final_spread == 1 assert (metrics.winner_seat, metrics.winner_bot, metrics.rounds) == (0, "balanced", 2) assert metrics.minutes == pytest.approx(round((3 * TURN_SECONDS + 4 * KUMITE_SECONDS) / 60, 1)) def test_measure_tracks_turns_started_on_14() -> None: metrics = run( [ TrackChanged(0, 0, 14, Source.CARD), TurnStarted(1, 0, 7), TurnStarted(1, 1, 7), TurnStarted(2, 0, 7), TrackChanged(0, 14, 15, Source.KUMITE), TurnStarted(2, 1, 7), TurnStarted(3, 0, 7), TrackChanged(0, 15, 14, Source.SQUARE), TurnStarted(4, 0, 7), GameOver(None, 4), ] ) assert metrics.turns_on_14 == 3 assert metrics.longest_14_streak == 2 assert metrics.winner_seat is None def test_summarize_splits_wins_by_seat_and_bot() -> None: base = run([GameOver(None, 10)]) rows = [ replace(base, winner_seat=0, winner_bot="balanced"), replace(base, lineup="random,balanced", winner_seat=1, winner_bot="balanced"), replace(base, lineup="random,balanced", winner_seat=0, winner_bot="random"), base, ] summary = summarize("test", rows) assert summary.finished == 0.75 assert summary.win_rate_by_seat == (0.5, 0.25) assert summary.win_rate_by_bot == {"balanced": 0.5, "random": 0.25} assert summary.rounds.median == 10 def test_summarize_needs_games() -> None: with pytest.raises(ValueError, match="no games"): summarize("test", [])