"""Bot decisions on the fixture board (positions as in test_engine.py).""" import random import pytest from scripted import make_game, put from dojo.board import Board, Pos from dojo.bots import ( AGGRESSIVE, BALANCED, FARMER, PROFILES, HeuristicAgent, Profile, RandomAgent, win_probability, ) from dojo.engine import Agent, FightView, Game, RandomSource from dojo.rules import VARIANT_A, VARIANT_B, Card, RulesConfig ALLENAMENTO = Pos(0, 5) FATICA = Pos(3, 6) TATAMI = Pos(4, 4) DOJO_ENTRANCE = Pos(5, 7) DOJO = Pos(6, 7) def test_win_probability_counts_senshu_and_bonuses() -> None: assert win_probability(0, 0) == pytest.approx(21 / 36) assert win_probability(2, 0) == pytest.approx(30 / 36) assert win_probability(0, 2) == pytest.approx(10 / 36) def test_heuristic_prefers_allenamento_to_fatica() -> None: game, _, _ = make_game() choice = HeuristicAgent().choose_path(game.view(0), [(FATICA,), (ALLENAMENTO,)]) assert choice == (ALLENAMENTO,) def test_brown_heuristic_takes_the_exam_when_it_can() -> None: game, _, _ = make_game() put(game, 0, DOJO_ENTRANCE, track=15) choice = HeuristicAgent().choose_path(game.view(0), [(ALLENAMENTO,), (DOJO,)]) assert choice == (DOJO,) @pytest.mark.parametrize(("profile", "fights"), [(FARMER, False), (AGGRESSIVE, True)]) def test_profiles_differ_on_an_even_fight(profile: Profile, fights: bool) -> None: game, _, _ = make_game() put(game, 0, Pos(4, 3), track=3) put(game, 1, TATAMI, track=3) choice = HeuristicAgent(profile).choose_path(game.view(0), [(Pos(5, 3),), (TATAMI,)]) assert (choice == (TATAMI,)) is fights def fight(attacker_total: int, defender_total: int) -> FightView: return FightView(0, 1, False, attacker_total, defender_total, played=()) @pytest.mark.parametrize( ("me", "card", "totals", "plays"), [ (0, Card.KIAI, (3, 5), True), # 5 ties 5: Senshu gives it to the attacker. (0, Card.KIAI, (2, 5), False), # Kiai cannot turn it: keep it. (0, Card.KIAI, (5, 5), False), # Already winning. (1, Card.KIAI, (5, 4), True), # The defender needs to beat, not tie. (1, Card.KIAI, (6, 4), False), (0, Card.KAESHI, (1, 6), True), # Losing: a reroll can only help. (1, Card.UKE, (4, 4), True), ], ) def test_heuristic_plays_a_tecnica_only_when_losing( me: int, card: Card, totals: tuple[int, int], plays: bool ) -> None: game, _, _ = make_game() game.players[me].tecnica = card assert HeuristicAgent().play_tecnica(game.view(me), fight(*totals)) is plays def test_heuristic_keeps_the_tecnica_its_profile_ranks_first() -> None: game, _, _ = make_game() assert HeuristicAgent(BALANCED).keep_tecnica(game.view(0), Card.UKE, Card.KIAI) is Card.KIAI assert HeuristicAgent(FARMER).keep_tecnica(game.view(0), Card.UKE, Card.KIAI) is Card.UKE def play(board: Board, config: RulesConfig, seed: int) -> Game: rng = random.Random(seed) agents: list[Agent] = [HeuristicAgent(p) for p in PROFILES.values()] agents.append(RandomAgent(rng)) game = Game(board, config, agents, RandomSource(seed)) game.run() return game @pytest.mark.parametrize("config", [VARIANT_A, VARIANT_B]) def test_bots_play_full_games_reproducibly(board: Board, config: RulesConfig) -> None: first, again = play(board, config, seed=7), play(board, config, seed=7) assert first.winner is not None assert first.events == again.events