Trump card: draw from the stack, no trump for wizards and jesters
- Deal the hands from the shuffled stack and draw the trump card from it, so the trump card is in nobody's hand and cannot be played - A wizard or jester as trump card means no trump suit (previously a wizard let the dealer choose); remove the trump selection phase, the ChooseTrump action, the trump picker UI and the AI trump actions - Completed tricks carry their round's trump card, so the table keeps showing the matching trump card while the last trick of a round is displayed - Card corners are no longer rotated, so 6 and 9 cannot be confused - Trump label explains why there is no trump (wizard, jester, last round) - Tests: trump card never dealt, special cards give no trump, tricks keep their trump card Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -19,8 +19,8 @@ import copy
|
||||
import random
|
||||
|
||||
from . import rules
|
||||
from .actions import Action, Bid, ChooseTrump, PlayCard
|
||||
from .cards import SUITS, Card, new_shuffled_deck
|
||||
from .actions import Action, Bid, PlayCard
|
||||
from .cards import Card, new_shuffled_deck
|
||||
from .observation import Observation
|
||||
from .state import (
|
||||
Event,
|
||||
@@ -99,9 +99,6 @@ class WizardGame:
|
||||
if player is None or player != self.current_player:
|
||||
return []
|
||||
|
||||
if self.phase is Phase.TRUMP_SELECTION:
|
||||
return [ChooseTrump(suit) for suit in SUITS]
|
||||
|
||||
if self.phase is Phase.BIDDING:
|
||||
return [Bid(value) for value in self._allowed_bids(player)]
|
||||
|
||||
@@ -127,8 +124,6 @@ class WizardGame:
|
||||
if not self.is_legal(player, action):
|
||||
raise IllegalActionError(f"Illegal action {action} for player {player}")
|
||||
|
||||
if isinstance(action, ChooseTrump):
|
||||
return self._choose_trump(action)
|
||||
if isinstance(action, Bid):
|
||||
return self._bid(player, action)
|
||||
if isinstance(action, PlayCard):
|
||||
@@ -183,39 +178,32 @@ class WizardGame:
|
||||
return (player + 1) % self.num_players
|
||||
|
||||
def _deal(self, round_number: int, dealer: int) -> RoundState:
|
||||
deck = new_shuffled_deck(self._rng)
|
||||
stack = new_shuffled_deck(self._rng)
|
||||
hands: list[list[Card]] = [[] for _ in range(self.num_players)]
|
||||
# Deal one card at a time starting left of the dealer.
|
||||
# Deal one card at a time from the top of the stack, starting left of
|
||||
# the dealer.
|
||||
for i in range(round_number * self.num_players):
|
||||
hands[(dealer + 1 + i) % self.num_players].append(deck[i])
|
||||
remaining = deck[round_number * self.num_players:]
|
||||
hands[(dealer + 1 + i) % self.num_players].append(stack.pop())
|
||||
|
||||
# The next card defines the trump suit. In the last round no cards
|
||||
# are left, so there is no trump.
|
||||
trump_card = remaining[0] if remaining else None
|
||||
# The next card of the stack is turned up as trump card. It is taken
|
||||
# off the stack and belongs to nobody's hand. In the last round the
|
||||
# stack is empty, so there is no trump card.
|
||||
trump_card = stack.pop() if stack else None
|
||||
starter = self._next(dealer)
|
||||
|
||||
rnd = RoundState(
|
||||
self.phase = Phase.BIDDING
|
||||
self.current_player = starter
|
||||
return RoundState(
|
||||
round_number=round_number,
|
||||
dealer=dealer,
|
||||
hands=hands,
|
||||
trump_card=trump_card,
|
||||
trump_suit=trump_card.suit if trump_card and trump_card.is_number else None,
|
||||
trump_suit=rules.trump_suit(trump_card),
|
||||
bids=[None] * self.num_players,
|
||||
tricks_won=[0] * self.num_players,
|
||||
trick_leader=starter,
|
||||
)
|
||||
|
||||
if trump_card is not None and trump_card.is_wizard:
|
||||
# A wizard as trump card: the dealer chooses the trump suit.
|
||||
self.phase = Phase.TRUMP_SELECTION
|
||||
self.current_player = dealer
|
||||
else:
|
||||
# No trump card or a jester: the round is played without trump.
|
||||
self.phase = Phase.BIDDING
|
||||
self.current_player = starter
|
||||
return rnd
|
||||
|
||||
def _allowed_bids(self, player: int) -> list[int]:
|
||||
"""Bids 0..cards in hand, except that the last bidder may not make the
|
||||
sum of all bids equal the number of cards each player holds this round.
|
||||
@@ -229,12 +217,6 @@ class WizardGame:
|
||||
allowed = [bid for bid in allowed if bid != forbidden]
|
||||
return allowed
|
||||
|
||||
def _choose_trump(self, action: ChooseTrump) -> list[Event]:
|
||||
self.round.trump_suit = action.suit
|
||||
self.phase = Phase.BIDDING
|
||||
self.current_player = self._next(self.round.dealer)
|
||||
return []
|
||||
|
||||
def _bid(self, player: int, action: Bid) -> list[Event]:
|
||||
rnd = self.round
|
||||
rnd.bids[player] = action.value
|
||||
@@ -263,7 +245,9 @@ class WizardGame:
|
||||
winner = rnd.current_trick[rules.trick_winner(cards, rnd.trump_suit)].player
|
||||
rnd.tricks_won[winner] += 1
|
||||
|
||||
result = TrickResult(rnd.round_number, rnd.trick_number, tuple(rnd.current_trick), winner)
|
||||
result = TrickResult(
|
||||
rnd.round_number, rnd.trick_number, tuple(rnd.current_trick), winner, rnd.trump_card
|
||||
)
|
||||
self.last_trick = result
|
||||
events: list[Event] = [TrickCompleted(result)]
|
||||
|
||||
|
||||
Reference in New Issue
Block a user