def test_1_prince_victory(): """ player1 has a countess, player0 uses a prince to discard himself and get the princess and win. """ game_round = start_round_from_player_cards( [cards.Prince(), cards.Handmaid(), cards.Guard()], [cards.Countess(), cards.Priest()], set_aside=cards.Princess(), first_player=0, ) player0, player1 = game_round.players assert tuple(map(CardType, player0.hand)) == (CardType.PRINCE, CardType.HANDMAID) assert tuple(map(CardType, player1.hand)) == (CardType.COUNTESS, ) assert max(game_round.players, key=lambda p: p.hand.card.value) is player1 (immunity, ) = play_with_choices(player0, CardType.HANDMAID) assert immunity.player is player0 results = play_with_choices(player1, CardType.PRIEST, mv.OpponentChoice.NO_TARGET) assert results == () discarded, dealt = play_with_choices(player0, CardType.PRINCE, player0) assert CardType(discarded.discarded) == CardType.GUARD assert CardType(dealt.dealt) == CardType.PRINCESS end = game_round.state assert end.type == RoundState.Type.ROUND_END assert max(game_round.players, key=lambda p: p.hand.card.value) is player0 assert end.winner is player0
def _requests_are_equivalent(local: move.ChoiceStep, remote: move.ChoiceStep): return ( remote.player == local.player # can only check type due to a serialization/deserialization defect # (the card instances won't be the same object) and CardType(remote.card_played) == CardType(local.card_played) )
async def handle(e: mv.ChooseOneCard): num_drawn = len(e.options) - 1 names = [CardType(c).name.title() for c in e.options] options_members = {CardType(c).name: c for c in e.options} print(f"You draw {num_drawn} {pluralize('card', num_drawn)}; " f"you now have these cards in your hand: {', '.join(names)}") choices = enum.Enum("CardOption", names=options_members) choice = await async_ask_valid_input("Choose one card:", choices=choices) e.choice = choice.value return e
def play_type(self, card_type: CardType) -> MoveStepGenerator: """Shortcut to play the first card in the hand of a given type""" with valid8.validation("card_type", card_type, help_msg="Not in hand"): for card in self.hand: if CardType(card) == card_type: return self.play_card(card) else: raise LookupError(card_type)
def play_card(player: RoundPlayer, card: cards.Card, autofill=None, skip_if_disallowed=True): from test_loveletter.unit.test_cards_cases import DISCARD_TYPES if autofill is None: autofill = CardType(card) in DISCARD_TYPES if (skip_if_disallowed and not isinstance(card, Mock) and CardType.COUNTESS in map(CardType, player.hand) and CardType(card) in {CardType.PRINCE, CardType.KING}): pytest.skip(f"Playing {card} with Countess in hand will raise") give_card(player, card) move_ = player.play_card(card) if autofill: return autofill_move(move_, close=True) else: return move_
def autofill_step(step: loveletter.round.ChooseCardToPlay): hand = step.player.hand card_types = tuple(map(CardType, hand)) if CardType.COUNTESS in card_types and (CardType.PRINCE in card_types or CardType.KING in card_types): # we're forced to play the Countess step.choice = next(c for c in hand if CardType(c) == CardType.COUNTESS) else: step.choice = random.choice(list(hand)) return step
def _validate_choice(self, value): from loveletter.cards import CardType card_type = CardType(value) valid8.validate( "card_type", card_type, custom=lambda t: t != CardType.GUARD, # TODO: use minilambda help_msg="You can't guess a Guard", )
def test_guard_incorrectGuess_doesNotEliminateOpponent(started_round: Round): player = started_round.current_player for other in set(started_round.players) - {player}: assert other.alive wrong_guesses = set(CardType) - { CardType(type(other.hand.card)), CardType.GUARD, } for guess in wrong_guesses: move = play_card(player, cards.Guard()) target_step = next(move) target_step.choice = other guess_step = move.send(target_step) guess_step.choice = guess results = send_gracious(move, guess_step) assert tuple(map(type, results)) == (mv.WrongCardGuess, ) assert results[0].guess == CardType(guess) assert other.alive # artificially start new turn with same player restart_turn(started_round)
def test_guard_correctGuess_eliminatesOpponent(started_round: Round): player = started_round.current_player for other in set(started_round.players) - {player}: assert other.alive move = play_card(player, cards.Guard()) target_step = move.send(None) target_step.choice = other guess_step = move.send(target_step) guess_step.choice = guess = type(other.hand.card) results = send_gracious(move, guess_step) assert tuple(map(type, results)) == (mv.CorrectCardGuess, mv.PlayerEliminated) assert results[0].guess == CardType(guess) assert results[1].eliminated == other assert not other.alive # artificially start new turn with same player restart_turn(started_round)
def test_4_princess_suicide(): """ player1 holds a Princess but has to eliminate themselves because they draw a Prince and the opponent is immune. """ game_round = start_round_from_player_cards( [cards.Handmaid(), cards.Baron(), cards.Guard()], [cards.Princess(), cards.Prince(), cards.Countess()], first_player=0, ) player0, player1 = game_round.players play_with_choices(player0, CardType.HANDMAID) play_random_move(player1) assert game_round.ended assert game_round.state.winner is player0 assert CardType( game_round.deck.take()) == CardType.GUARD # assert no card dealt
def test_prince_againstPrincess_kills(started_round: Round): player = started_round.current_player victim = started_round.next_player(player) give_card(victim, cards.Princess(), replace=True) victim_card = victim.hand.card deck_before = list(started_round.deck) move = play_card(player, cards.Prince()) target_step = next(move) target_step.choice = victim results = send_gracious(move, target_step) assert tuple(map(type, results)) == ( mv.CardDiscarded, mv.PlayerEliminated, ) assert results[0].target is victim assert results[0].discarded is victim_card assert results[1].eliminated is victim assert not victim.alive assert CardType(victim.discarded_cards[-1]) == CardType.PRINCESS assert list(started_round.deck) == deck_before
def card_sprite(card: Card, size=DEFAULT_CARD_SIZE) -> np.array: """Make a face-up card sprite for a given card object.""" arr = _empty_card(size) width = arr.shape[1] write_string(arr, f"({card.value})", row=2, align="<", margin=3) write_string(arr, CardType(card).name, row=2, align="^") min_description_start = 4 bottom_margin = 2 description_margin = 4 description_end = size - bottom_margin lines = textwrap.wrap( card.description, width=width - 2 * description_margin, max_lines=description_end - min_description_start, ) description_start = description_end - len(lines) for i, line in enumerate(lines, start=description_start): write_string(arr, line, row=i, align="^", margin=description_margin) return arr
def choice(self, value): from loveletter.cards import CardType super(CardGuess, type(self)).choice.fset(self, CardType(value))
def test_cardType_fromIntValue_raises(value): with pytest.raises(ValueError): CardType(value)
def test_cardType_fromSubclass_works(card_type): class DummySubclass(card_type.card_class): pass assert CardType(DummySubclass) == card_type
def test_cardType_fromIdenticalSubclass_works(card_type): assert CardType(card_type.card_class) == card_type
def test_cardTypeOrder_increasingPair_asExpected(card1, card2): assert CardType(card1) < CardType(card2)
def card_from_card_type(card_type: CardType): return card_type.card_class()
def from_serializable(self, value: Serializable) -> None: from loveletter.cards import CardType return CardType(value)
right.play.assert_called_once_with(dummy_player) left.play.assert_not_called() assert dummy_player.hand.card is left assert right not in dummy_player.hand dummy_player.round.discard_pile.place.assert_called_once_with(right) assert dummy_player.discarded_cards[-1] == right @pytest_cases.parametrize_with_cases("player", DummyPlayerCases.case_empty_hand) def test_playType_present_works(player: RoundPlayer): give_card(player, cards.Prince()) give_card(player, cards.Prince()) autofill_move(player.play_type(card_type := CardType.PRINCE)) assert len(player.hand) == 1 assert CardType(player.discarded_cards[-1]) == card_type @pytest_cases.parametrize_with_cases("player", DummyPlayerCases.case_empty_hand) def test_playType_notPresent_raises(player: RoundPlayer): give_card(player, cards.Guard()) give_card(player, cards.Princess()) with pytest.raises(valid8.ValidationError): send_gracious(player.play_type(CardType.PRINCE), None) @pytest_cases.parametrize_with_cases("player", cases=PlayerCases) def test_eliminate_discardsCards(player: RoundPlayer): game_round = player.round card = player.hand.card