"""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, series_probability, win_probability, ) from dojo.engine import Agent, FightView, Game, RandomSource from dojo.rules import DEFAULT_RULES, 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) assert win_probability(0, 0, senshu=False) == pytest.approx(15 / 36) def test_series_probability_is_a_best_of_n() -> None: assert series_probability(0.3, 1) == pytest.approx(0.3) assert series_probability(0.5, 3) == pytest.approx(0.5) assert series_probability(0.6, 3) == pytest.approx(0.6**2 * (3 - 2 * 0.6)) def test_kiai_that_only_ties_the_exam_is_kept_when_ties_go_to_the_defender() -> None: game, _, _ = make_game(config=RulesConfig(exam_ties_to_defender=True)) game.players[0].tecnica = Card.KIAI exam = FightView(0, 1, True, 3, 5, played=()) assert not HeuristicAgent().play_tecnica(game.view(0), exam) assert HeuristicAgent().play_tecnica(game.view(0), fight(3, 5)) 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", [ DEFAULT_RULES, RulesConfig(exam_ties_to_defender=True, exam_defender_black=True, exam_rounds=3), ], ) 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