Initial commit, with rules 0.5 and basic bots
This commit is contained in:
@@ -0,0 +1,670 @@
|
||||
"""Rules engine behaviour, one RULES.md rule per test where possible.
|
||||
|
||||
Positions refer to the fixture board (tests/fixtures/quadrant_valid.txt). In quadrant 0:
|
||||
Allenamento (0,5); Nebbia (1,4); tunnel (1,6)<->(3,4); Fatica (3,6); Passo Falso (4,1);
|
||||
Sfida (5,2); Dojo entrance (5,7)->(6,7). Player 1's Safe Room starts at (0,13).
|
||||
"""
|
||||
|
||||
import pytest
|
||||
from scripted import make_game, put, turn
|
||||
|
||||
from dojo.board import Pos
|
||||
from dojo.engine import Game, RandomSource, seat_corners
|
||||
from dojo.events import (
|
||||
CardDrawn,
|
||||
ExamTaken,
|
||||
FightKind,
|
||||
GameOver,
|
||||
KumiteFought,
|
||||
Moved,
|
||||
SentToStart,
|
||||
Source,
|
||||
TrackChanged,
|
||||
TurnSkipped,
|
||||
)
|
||||
from dojo.rules import VARIANT_A, VARIANT_B, Card
|
||||
|
||||
START_0 = Pos(0, 0)
|
||||
START_1 = Pos(0, 13)
|
||||
NEBBIA = Pos(1, 4)
|
||||
|
||||
|
||||
def events_of[T](game: Game, kind: type[T]) -> list[T]:
|
||||
return [e for e in game.events if isinstance(e, kind)]
|
||||
|
||||
|
||||
def only_fight(game: Game) -> KumiteFought:
|
||||
fights = events_of(game, KumiteFought)
|
||||
assert len(fights) == 1
|
||||
return fights[0]
|
||||
|
||||
|
||||
# Setup ------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
@pytest.mark.parametrize("players", range(2, 9))
|
||||
def test_corner_mates_never_play_consecutively(players: int) -> None:
|
||||
corners = seat_corners(players)
|
||||
|
||||
assert len(corners) == players
|
||||
assert all(corners[i] != corners[(i + 1) % players] for i in range(players))
|
||||
assert sorted(set(corners)) == list(range(min(players, 4)))
|
||||
|
||||
|
||||
def test_seats_up_to_four_take_corners_clockwise() -> None:
|
||||
assert seat_corners(4) == (0, 1, 2, 3)
|
||||
|
||||
|
||||
def test_players_start_in_their_own_safe_room() -> None:
|
||||
from dataclasses import replace
|
||||
|
||||
game, _, _ = make_game(players=5, config=replace(VARIANT_A, start_position=3))
|
||||
|
||||
for player in game.players:
|
||||
assert player.position in game.board.safe_room(player.corner)
|
||||
assert player.track == 3
|
||||
|
||||
|
||||
def test_illegal_agent_choice_is_rejected() -> None:
|
||||
game, agents, _ = make_game(dice=[1, 1])
|
||||
agents[0].ends.append(Pos(9, 9))
|
||||
|
||||
with pytest.raises(AssertionError, match="no offered path"):
|
||||
game.play_turn()
|
||||
|
||||
|
||||
# Kumite -----------------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_senshu_tie_goes_to_the_attacker_and_equal_belts_swap_a_belt() -> None:
|
||||
game, agents, dice = make_game(dice=[1, 1, 3, 3])
|
||||
put(game, 0, Pos(4, 3), track=3)
|
||||
put(game, 1, Pos(4, 5), track=3)
|
||||
|
||||
turn(game, agents, 0, Pos(4, 5))
|
||||
|
||||
fight = only_fight(game)
|
||||
assert fight.attacker_won and fight.kind is FightKind.EQUAL
|
||||
assert (game.players[0].track, game.players[1].track) == (6, 0)
|
||||
assert game.players[0].position == Pos(4, 5)
|
||||
assert game.players[1].position == START_1
|
||||
assert not dice.rolls
|
||||
|
||||
|
||||
def test_upset_swaps_a_belt() -> None:
|
||||
game, agents, _ = make_game(dice=[1, 1, 6, 3])
|
||||
put(game, 0, Pos(4, 3), track=0)
|
||||
put(game, 1, Pos(4, 5), track=6)
|
||||
|
||||
turn(game, agents, 0, Pos(4, 5))
|
||||
|
||||
fight = only_fight(game)
|
||||
assert fight.kind is FightKind.UPSET
|
||||
assert (fight.attacker_total, fight.defender_total) == (6, 5)
|
||||
assert (game.players[0].track, game.players[1].track) == (3, 3)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(("config", "gain"), [(VARIANT_A, 0), (VARIANT_B, 1)])
|
||||
def test_higher_belt_beating_lower_gains_the_variant_amount(config, gain: int) -> None:
|
||||
game, agents, _ = make_game(config=config, dice=[1, 1, 1, 2])
|
||||
put(game, 0, Pos(4, 3), track=6)
|
||||
put(game, 1, Pos(4, 5), track=0)
|
||||
|
||||
turn(game, agents, 0, Pos(4, 5))
|
||||
|
||||
assert only_fight(game).kind is FightKind.EXPECTED
|
||||
assert game.players[0].track == 6 + gain
|
||||
assert game.players[1].track == 0
|
||||
assert game.players[1].position == START_1
|
||||
|
||||
|
||||
def test_belt_bonus_is_capped_at_two() -> None:
|
||||
game, agents, _ = make_game(dice=[1, 1, 1, 4])
|
||||
put(game, 0, Pos(4, 3), track=15)
|
||||
put(game, 1, Pos(4, 5), track=0)
|
||||
|
||||
turn(game, agents, 0, Pos(4, 5))
|
||||
|
||||
fight = only_fight(game)
|
||||
assert (fight.attacker_total, fight.defender_total) == (3, 4)
|
||||
assert not fight.attacker_won and fight.kind is FightKind.UPSET
|
||||
assert (game.players[0].track, game.players[1].track) == (12, 3)
|
||||
assert game.players[0].position == START_0
|
||||
|
||||
|
||||
def test_losing_attacker_goes_to_start_and_skips_the_square_effect() -> None:
|
||||
game, agents, _ = make_game(dice=[1, 1, 1, 2])
|
||||
put(game, 0, Pos(0, 3), track=3)
|
||||
put(game, 1, Pos(0, 5), track=3)
|
||||
|
||||
turn(game, agents, 0, Pos(0, 5))
|
||||
|
||||
assert game.players[0].position == START_0
|
||||
assert (game.players[0].track, game.players[1].track) == (0, 6)
|
||||
assert game.players[1].position == Pos(0, 5)
|
||||
assert not [e for e in events_of(game, TrackChanged) if e.source is Source.SQUARE]
|
||||
|
||||
|
||||
def test_winning_attacker_continues_its_move() -> None:
|
||||
game, agents, _ = make_game(dice=[1, 2, 6, 1])
|
||||
put(game, 0, Pos(4, 2), track=3)
|
||||
put(game, 1, Pos(4, 3), track=3)
|
||||
|
||||
turn(game, agents, 0, Pos(4, 5), Pos(4, 5))
|
||||
|
||||
assert game.players[0].position == Pos(4, 5)
|
||||
assert game.players[1].position == START_1
|
||||
assert [e.path for e in events_of(game, Moved)] == [
|
||||
(Pos(4, 3),),
|
||||
(Pos(4, 4), Pos(4, 5)),
|
||||
]
|
||||
|
||||
|
||||
def test_losing_attacker_stops_even_with_steps_left() -> None:
|
||||
game, agents, dice = make_game(dice=[1, 2, 1, 6])
|
||||
put(game, 0, Pos(4, 2), track=3)
|
||||
put(game, 1, Pos(4, 3), track=3)
|
||||
|
||||
turn(game, agents, 0, Pos(4, 5))
|
||||
|
||||
assert game.players[0].position == START_0
|
||||
assert game.players[1].position == Pos(4, 3)
|
||||
assert not dice.rolls
|
||||
|
||||
|
||||
def test_no_kumite_inside_a_safe_room() -> None:
|
||||
game, agents, _ = make_game(dice=[1, 1])
|
||||
put(game, 1, Pos(1, 1))
|
||||
put(game, 0, Pos(1, 3))
|
||||
|
||||
turn(game, agents, 0, Pos(1, 1))
|
||||
|
||||
assert not events_of(game, KumiteFought)
|
||||
assert game.players[0].position == game.players[1].position == Pos(1, 1)
|
||||
|
||||
|
||||
# Tecniche ---------------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_kiai_adds_to_the_total() -> None:
|
||||
game, agents, _ = make_game(dice=[1, 1, 2, 3])
|
||||
put(game, 0, Pos(4, 3), track=3)
|
||||
put(game, 1, Pos(4, 5), track=3)
|
||||
game.players[0].tecnica = Card.KIAI
|
||||
agents[0].plays = frozenset({Card.KIAI})
|
||||
|
||||
turn(game, agents, 0, Pos(4, 5))
|
||||
|
||||
fight = only_fight(game)
|
||||
assert (fight.attacker_total, fight.defender_total) == (4, 3)
|
||||
assert fight.cards == ((0, Card.KIAI),)
|
||||
assert agents[0].fight_views[0].attacker_total == 2
|
||||
assert game.players[0].tecnica is None
|
||||
|
||||
|
||||
def test_kaeshi_rerolls_the_die() -> None:
|
||||
game, agents, dice = make_game(dice=[1, 1, 1, 4, 5])
|
||||
put(game, 0, Pos(4, 3), track=3)
|
||||
put(game, 1, Pos(4, 5), track=3)
|
||||
game.players[0].tecnica = Card.KAESHI
|
||||
agents[0].plays = frozenset({Card.KAESHI})
|
||||
|
||||
turn(game, agents, 0, Pos(4, 5))
|
||||
|
||||
assert only_fight(game).attacker_total == 5
|
||||
assert only_fight(game).attacker_won
|
||||
assert not dice.rolls
|
||||
|
||||
|
||||
def test_defender_sees_the_attackers_card_before_deciding() -> None:
|
||||
game, agents, _ = make_game(dice=[1, 1, 3, 3])
|
||||
put(game, 0, Pos(4, 3), track=3)
|
||||
put(game, 1, Pos(4, 5), track=3)
|
||||
game.players[0].tecnica = Card.KIAI
|
||||
game.players[1].tecnica = Card.KIAI
|
||||
agents[0].plays = agents[1].plays = frozenset({Card.KIAI})
|
||||
|
||||
turn(game, agents, 0, Pos(4, 5))
|
||||
|
||||
assert agents[1].fight_views[0].played == ((0, Card.KIAI),)
|
||||
assert agents[1].fight_views[0].attacker_total == 5
|
||||
assert only_fight(game).cards == ((0, Card.KIAI), (1, Card.KIAI))
|
||||
|
||||
|
||||
def test_uke_losing_defender_stays_and_the_attacker_steps_on() -> None:
|
||||
game, agents, _ = make_game(dice=[1, 1, 5, 1])
|
||||
put(game, 0, Pos(4, 3), track=3)
|
||||
put(game, 1, Pos(4, 5), track=3)
|
||||
game.players[1].tecnica = Card.UKE
|
||||
agents[1].plays = frozenset({Card.UKE})
|
||||
|
||||
turn(game, agents, 0, Pos(4, 5), Pos(4, 6))
|
||||
|
||||
assert game.players[1].position == Pos(4, 5)
|
||||
assert game.players[1].track == 0
|
||||
assert game.players[0].position == Pos(4, 6)
|
||||
assert not events_of(game, SentToStart)
|
||||
|
||||
|
||||
def test_uke_losing_attacker_returns_to_the_previous_square() -> None:
|
||||
game, agents, _ = make_game(dice=[1, 1, 1, 6])
|
||||
put(game, 0, Pos(4, 3), track=3)
|
||||
put(game, 1, Pos(4, 5), track=3)
|
||||
game.players[0].tecnica = Card.UKE
|
||||
agents[0].plays = frozenset({Card.UKE})
|
||||
|
||||
turn(game, agents, 0, Pos(4, 5))
|
||||
|
||||
assert game.players[0].position == Pos(4, 4)
|
||||
assert (game.players[0].track, game.players[1].track) == (0, 6)
|
||||
|
||||
|
||||
# Meditazione ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_meditating_pawn_is_passed_through_without_a_fight() -> None:
|
||||
game, agents, _ = make_game(dice=[1, 1])
|
||||
put(game, 0, Pos(4, 3))
|
||||
put(game, 1, Pos(4, 4))
|
||||
game.players[1].meditating = True
|
||||
|
||||
turn(game, agents, 0, Pos(4, 5))
|
||||
|
||||
assert not events_of(game, KumiteFought)
|
||||
assert game.players[0].position == Pos(4, 5)
|
||||
|
||||
|
||||
def test_ending_on_a_meditating_pawn_takes_one_extra_step() -> None:
|
||||
game, agents, _ = make_game(dice=[1, 1])
|
||||
put(game, 0, Pos(4, 2))
|
||||
put(game, 1, Pos(4, 4))
|
||||
game.players[1].meditating = True
|
||||
|
||||
turn(game, agents, 0, Pos(4, 4), Pos(4, 5))
|
||||
|
||||
assert game.players[0].position == Pos(4, 5)
|
||||
extra_step_options = {path[-1] for path in agents[0].offered_paths[1]}
|
||||
assert extra_step_options == {Pos(3, 4), Pos(4, 5), Pos(5, 4)}
|
||||
|
||||
|
||||
def test_meditating_players_turn_is_skipped_once() -> None:
|
||||
game, agents, _ = make_game()
|
||||
game.players[1].meditating = True
|
||||
|
||||
turn(game, agents, 1)
|
||||
|
||||
assert events_of(game, TurnSkipped) == [TurnSkipped(round=1, player=1)]
|
||||
assert not game.players[1].meditating
|
||||
|
||||
|
||||
# Squares ----------------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_allenamento_and_fatica() -> None:
|
||||
game, agents, _ = make_game(dice=[1, 1, 1, 1])
|
||||
put(game, 0, Pos(0, 3), track=3)
|
||||
turn(game, agents, 0, Pos(0, 5))
|
||||
assert game.players[0].track == 4
|
||||
|
||||
put(game, 0, Pos(4, 5))
|
||||
turn(game, agents, 0, Pos(3, 6))
|
||||
assert game.players[0].track == 3
|
||||
|
||||
|
||||
@pytest.mark.parametrize(("before", "after"), [(14, 14), (13, 14), (15, 16), (17, 17)])
|
||||
def test_variant_b_squares_cannot_make_a_player_brown(before: int, after: int) -> None:
|
||||
game, agents, _ = make_game(config=VARIANT_B, dice=[1, 1])
|
||||
put(game, 0, Pos(0, 3), track=before)
|
||||
|
||||
turn(game, agents, 0, Pos(0, 5))
|
||||
|
||||
assert game.players[0].track == after
|
||||
|
||||
|
||||
def test_variant_a_squares_can_make_a_player_brown() -> None:
|
||||
game, agents, _ = make_game(config=VARIANT_A, dice=[1, 1])
|
||||
put(game, 0, Pos(0, 3), track=14)
|
||||
|
||||
turn(game, agents, 0, Pos(0, 5))
|
||||
|
||||
assert game.players[0].track == 15
|
||||
|
||||
|
||||
def test_variant_b_kumite_can_make_a_player_brown() -> None:
|
||||
game, agents, _ = make_game(config=VARIANT_B, dice=[1, 1, 3, 3])
|
||||
put(game, 0, Pos(4, 3), track=14)
|
||||
put(game, 1, Pos(4, 5), track=12)
|
||||
|
||||
turn(game, agents, 0, Pos(4, 5))
|
||||
|
||||
assert game.players[0].track == 17
|
||||
|
||||
|
||||
def test_passo_falso_returns_to_the_move_start_and_fights_stand() -> None:
|
||||
game, agents, _ = make_game(dice=[1, 1, 6, 1])
|
||||
put(game, 0, Pos(4, 3), track=3)
|
||||
put(game, 1, Pos(4, 2), track=3)
|
||||
|
||||
turn(game, agents, 0, Pos(4, 1), Pos(4, 1))
|
||||
|
||||
assert game.players[0].position == Pos(4, 3)
|
||||
assert game.players[0].track == 6
|
||||
assert game.players[1].position == START_1
|
||||
|
||||
|
||||
def test_sfida_square_fights_a_chosen_opponent_and_clamps_at_zero() -> None:
|
||||
game, agents, _ = make_game(dice=[1, 1, 4, 4])
|
||||
put(game, 0, Pos(5, 4), track=0)
|
||||
put(game, 1, Pos(10, 10), track=0)
|
||||
agents[0].sfida_target = 1
|
||||
|
||||
turn(game, agents, 0, Pos(5, 2))
|
||||
|
||||
assert only_fight(game).attacker_won
|
||||
assert (game.players[0].track, game.players[1].track) == (3, 0)
|
||||
assert game.players[0].position == Pos(5, 2)
|
||||
assert game.players[1].position == START_1
|
||||
|
||||
|
||||
@pytest.mark.parametrize("meditating", [False, True])
|
||||
def test_sfida_cannot_target_safe_rooms_or_meditating_players(meditating: bool) -> None:
|
||||
game, agents, _ = make_game(dice=[1, 1])
|
||||
put(game, 0, Pos(5, 4))
|
||||
if meditating:
|
||||
put(game, 1, Pos(10, 10))
|
||||
game.players[1].meditating = True
|
||||
agents[0].sfida_target = 1
|
||||
|
||||
turn(game, agents, 0, Pos(5, 2))
|
||||
|
||||
assert not events_of(game, KumiteFought)
|
||||
|
||||
|
||||
def test_tunnel_is_optional_and_ends_the_move() -> None:
|
||||
game, agents, _ = make_game(dice=[1, 1, 1, 1])
|
||||
put(game, 0, NEBBIA)
|
||||
turn(game, agents, 0, Pos(1, 6))
|
||||
assert game.players[0].position == Pos(1, 6)
|
||||
|
||||
put(game, 0, NEBBIA)
|
||||
agents[0].use_tunnels = True
|
||||
turn(game, agents, 0, Pos(1, 6))
|
||||
assert game.players[0].position == Pos(3, 4)
|
||||
|
||||
|
||||
def test_occupied_tunnel_exit_starts_a_kumite() -> None:
|
||||
game, agents, _ = make_game(dice=[1, 1, 3, 3])
|
||||
put(game, 0, NEBBIA, track=3)
|
||||
put(game, 1, Pos(3, 4), track=3)
|
||||
agents[0].use_tunnels = True
|
||||
|
||||
turn(game, agents, 0, Pos(1, 6))
|
||||
|
||||
assert only_fight(game).attacker_won
|
||||
assert game.players[0].position == Pos(3, 4)
|
||||
assert game.players[1].position == START_1
|
||||
|
||||
|
||||
def test_tunnel_exit_with_a_meditating_pawn_cannot_be_used() -> None:
|
||||
game, agents, _ = make_game(dice=[1, 1])
|
||||
put(game, 0, NEBBIA)
|
||||
put(game, 1, Pos(3, 4))
|
||||
game.players[1].meditating = True
|
||||
agents[0].use_tunnels = True
|
||||
|
||||
turn(game, agents, 0, Pos(1, 6))
|
||||
|
||||
assert game.players[0].position == Pos(1, 6)
|
||||
|
||||
|
||||
# Nebbia del Dojo --------------------------------------------------------------------------
|
||||
|
||||
|
||||
def draw_on_nebbia(
|
||||
card: Card, players: int = 2, dice: tuple[int, ...] = (1, 1), **tracks: int
|
||||
) -> tuple[Game, list]:
|
||||
game, agents, _ = make_game(players=players, dice=dice, deck=((card, 1),))
|
||||
put(game, 0, Pos(0, 3))
|
||||
for name, track in tracks.items():
|
||||
game.players[int(name[1:])].track = track
|
||||
return game, agents
|
||||
|
||||
|
||||
def test_card_is_drawn_when_stopping_on_nebbia() -> None:
|
||||
game, agents = draw_on_nebbia(Card.ALLENAMENTO_INTENSIVO)
|
||||
|
||||
turn(game, agents, 0, NEBBIA)
|
||||
|
||||
assert events_of(game, CardDrawn) == [CardDrawn(0, Card.ALLENAMENTO_INTENSIVO)]
|
||||
assert game.players[0].track == 1
|
||||
|
||||
|
||||
def test_discard_pile_is_reshuffled_when_the_deck_runs_out() -> None:
|
||||
game, agents = draw_on_nebbia(Card.ALLENAMENTO_INTENSIVO, dice=(1, 1, 1, 1))
|
||||
turn(game, agents, 0, NEBBIA)
|
||||
put(game, 0, Pos(0, 3))
|
||||
|
||||
turn(game, agents, 0, NEBBIA)
|
||||
|
||||
assert game.players[0].track == 2
|
||||
|
||||
|
||||
def test_colpo_proibito() -> None:
|
||||
game, agents = draw_on_nebbia(Card.COLPO_PROIBITO, p0=3)
|
||||
|
||||
turn(game, agents, 0, NEBBIA)
|
||||
|
||||
assert game.players[0].track == 2
|
||||
|
||||
|
||||
def test_sensei_osserva_helps_every_lowest_player() -> None:
|
||||
game, agents = draw_on_nebbia(Card.SENSEI_OSSERVA, players=3, p0=3, p1=0, p2=0)
|
||||
|
||||
turn(game, agents, 0, NEBBIA)
|
||||
|
||||
assert [p.track for p in game.players] == [3, 2, 2]
|
||||
|
||||
|
||||
def test_peso_della_cintura_hits_every_highest_player() -> None:
|
||||
game, agents = draw_on_nebbia(Card.PESO_DELLA_CINTURA, players=3, p0=3, p1=6, p2=6)
|
||||
|
||||
turn(game, agents, 0, NEBBIA)
|
||||
|
||||
assert [p.track for p in game.players] == [3, 5, 5]
|
||||
|
||||
|
||||
def test_meditazione_card() -> None:
|
||||
game, agents = draw_on_nebbia(Card.MEDITAZIONE)
|
||||
|
||||
turn(game, agents, 0, NEBBIA)
|
||||
|
||||
assert game.players[0].meditating
|
||||
|
||||
|
||||
def test_passo_falso_card() -> None:
|
||||
game, agents = draw_on_nebbia(Card.PASSO_FALSO)
|
||||
|
||||
turn(game, agents, 0, NEBBIA)
|
||||
|
||||
assert game.players[0].position == Pos(0, 3)
|
||||
|
||||
|
||||
def test_irimi_swaps_places_without_a_fight() -> None:
|
||||
game, agents = draw_on_nebbia(Card.IRIMI)
|
||||
put(game, 1, Pos(10, 10))
|
||||
agents[0].irimi_target = 1
|
||||
|
||||
turn(game, agents, 0, NEBBIA)
|
||||
|
||||
assert game.players[0].position == Pos(10, 10)
|
||||
assert game.players[1].position == NEBBIA
|
||||
assert not events_of(game, KumiteFought)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("card", [Card.SFIDA, Card.IRIMI])
|
||||
def test_sfida_and_irimi_cards_are_mandatory(card: Card) -> None:
|
||||
game, agents = draw_on_nebbia(card, dice=(1, 1, 3, 3))
|
||||
put(game, 1, Pos(10, 10))
|
||||
|
||||
turn(game, agents, 0, NEBBIA)
|
||||
|
||||
if card is Card.SFIDA:
|
||||
assert only_fight(game).defender == 1
|
||||
else:
|
||||
assert game.players[0].position == Pos(10, 10)
|
||||
|
||||
|
||||
def test_tunnel_segreto_teleports_to_a_chosen_tunnel() -> None:
|
||||
game, agents = draw_on_nebbia(Card.TUNNEL_SEGRETO)
|
||||
agents[0].tunnel_destination = Pos(3, 4)
|
||||
|
||||
turn(game, agents, 0, NEBBIA)
|
||||
|
||||
assert game.players[0].position == Pos(3, 4)
|
||||
|
||||
|
||||
def test_tsugi_ashi_makes_another_full_move() -> None:
|
||||
game, agents = draw_on_nebbia(Card.TSUGI_ASHI, dice=(1, 1, 1, 1))
|
||||
|
||||
turn(game, agents, 0, NEBBIA, Pos(2, 5))
|
||||
|
||||
assert game.players[0].position == Pos(2, 5)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(("keep_drawn", "kept"), [(True, Card.UKE), (False, Card.KIAI)])
|
||||
def test_only_one_tecnica_can_be_held(keep_drawn: bool, kept: Card) -> None:
|
||||
game, agents = draw_on_nebbia(Card.UKE)
|
||||
game.players[0].tecnica = Card.KIAI
|
||||
agents[0].keep_drawn = keep_drawn
|
||||
|
||||
turn(game, agents, 0, NEBBIA)
|
||||
|
||||
assert game.players[0].tecnica is kept
|
||||
|
||||
|
||||
# Central Dojo and Final Exam --------------------------------------------------------------
|
||||
|
||||
|
||||
def dojo_game(dice: list[int], players: int = 3, **tracks: int) -> tuple[Game, list]:
|
||||
game, agents, _ = make_game(players=players, dice=dice)
|
||||
put(game, 0, Pos(4, 7), track=15)
|
||||
for name, track in tracks.items():
|
||||
game.players[int(name[1:])].track = track
|
||||
return game, agents
|
||||
|
||||
|
||||
def test_only_brown_players_may_enter_the_dojo() -> None:
|
||||
game, agents = dojo_game([1, 1])
|
||||
game.players[0].track = 14
|
||||
|
||||
turn(game, agents, 0)
|
||||
|
||||
offered = agents[0].offered_paths[0]
|
||||
assert not any(game.board.dojo & set(path) for path in offered)
|
||||
|
||||
|
||||
def test_passing_the_exam_wins_the_game() -> None:
|
||||
game, agents = dojo_game([1, 1, 4, 1], p1=9, p2=12)
|
||||
|
||||
turn(game, agents, 0, Pos(6, 7))
|
||||
|
||||
assert events_of(game, ExamTaken) == [ExamTaken(challenger=0, defender=2, passed=True)]
|
||||
assert game.winner == 0
|
||||
assert game.finished
|
||||
assert isinstance(game.events[-1], GameOver)
|
||||
|
||||
|
||||
def test_failing_the_exam_costs_a_belt_and_the_defender_gains_nothing() -> None:
|
||||
game, agents = dojo_game([1, 1, 1, 6], p1=9, p2=12)
|
||||
|
||||
turn(game, agents, 0, Pos(6, 7))
|
||||
|
||||
assert game.winner is None
|
||||
assert game.players[0].track == 12
|
||||
assert game.players[0].position == START_0
|
||||
assert game.players[2].track == 12
|
||||
|
||||
|
||||
def test_uke_in_the_exam_returns_to_the_entrance() -> None:
|
||||
game, agents = dojo_game([1, 1, 1, 6], p1=9, p2=12)
|
||||
game.players[0].tecnica = Card.UKE
|
||||
agents[0].plays = frozenset({Card.UKE})
|
||||
|
||||
turn(game, agents, 0, Pos(6, 7))
|
||||
|
||||
assert game.players[0].position == Pos(5, 7)
|
||||
assert game.players[0].track == 12
|
||||
|
||||
|
||||
def test_challenger_picks_among_tied_defenders() -> None:
|
||||
game, agents = dojo_game([1, 1, 4, 1], p1=12, p2=12)
|
||||
agents[0].exam_defender = 1
|
||||
|
||||
turn(game, agents, 0, Pos(6, 7))
|
||||
|
||||
assert events_of(game, ExamTaken)[0].defender == 1
|
||||
|
||||
|
||||
def test_meditating_players_cannot_defend() -> None:
|
||||
game, agents = dojo_game([1, 1, 4, 1], p1=9, p2=12)
|
||||
game.players[2].meditating = True
|
||||
|
||||
turn(game, agents, 0, Pos(6, 7))
|
||||
|
||||
assert events_of(game, ExamTaken)[0].defender == 1
|
||||
|
||||
|
||||
def test_kumite_won_mid_move_can_open_the_dojo() -> None:
|
||||
game, agents, dice = make_game(dice=[1, 1, 3, 3, 6, 1])
|
||||
put(game, 0, Pos(4, 7), track=14)
|
||||
put(game, 1, Pos(5, 7), track=12)
|
||||
|
||||
turn(game, agents, 0, (Pos(5, 7), Pos(5, 6)), Pos(6, 7))
|
||||
|
||||
assert game.players[0].track == 17
|
||||
assert events_of(game, ExamTaken) == [ExamTaken(challenger=0, defender=1, passed=True)]
|
||||
assert game.winner == 0
|
||||
assert not dice.rolls
|
||||
|
||||
|
||||
# Whole game -------------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_a_game_runs_to_completion() -> None:
|
||||
from dataclasses import replace
|
||||
|
||||
from scripted import ScriptedAgent, fixture_board
|
||||
|
||||
config = replace(VARIANT_A, max_rounds=30)
|
||||
game = Game(fixture_board(), config, [ScriptedAgent() for _ in range(4)], RandomSource(7))
|
||||
|
||||
game.run()
|
||||
|
||||
assert game.finished
|
||||
assert isinstance(game.events[-1], GameOver)
|
||||
with pytest.raises(RuntimeError):
|
||||
game.play_turn()
|
||||
|
||||
|
||||
def test_views_hide_which_tecnica_an_opponent_holds() -> None:
|
||||
game, _, _ = make_game()
|
||||
game.players[0].tecnica = Card.KIAI
|
||||
|
||||
theirs, mine = game.view(1), game.view(0)
|
||||
|
||||
assert theirs.tecnica is None
|
||||
assert theirs.players[0].holds_tecnica
|
||||
assert not theirs.players[1].holds_tecnica
|
||||
assert mine.tecnica is Card.KIAI
|
||||
|
||||
|
||||
def test_agents_decide_on_their_own_current_view() -> None:
|
||||
game, agents, _ = make_game(dice=[1, 1])
|
||||
put(game, 1, Pos(4, 4), track=7)
|
||||
|
||||
turn(game, agents, 1)
|
||||
|
||||
view = agents[1].path_views[0]
|
||||
assert view.me == 1
|
||||
assert view.players[1].position == Pos(4, 4)
|
||||
assert view.belt(1) == 2
|
||||
Reference in New Issue
Block a user