API Reference
State
skaty.game_state.GameState
Contains all the state of a specific game. State should never be directly modified by the user. Instead, state modification is left to the rule set and actions. Actions are applied/undone by directly calling their apply/undo method. The validity of actions and calculation of game score is strictly delegated to the rule set passed.
action_history: list[Action] = []
instance-attribute
List of all actions in chronological order. Should not be directly modified, because apply_action and undo_action manage it.
active_player: PlayerIdx = self._middlehand
instance-attribute
Currently active player.
bid: Optional[int] = None
instance-attribute
Highest bid yet.
current_trick = Trick()
instance-attribute
Current trick.
game_type: Optional[GameType] = None
instance-attribute
Current game type.
hand_available = True
instance-attribute
Has the player looked at the skat?
hands = hands
instance-attribute
List of player hands indexed with PlayerIdx.
phase: GamePhase = GamePhases.BID
instance-attribute
Current game phase.
points: list[int] = [0, 0, 0]
instance-attribute
Points achieved in won tricks.
skat = skat
instance-attribute
List of two card Skat.
trick_history: list[Trick] = []
instance-attribute
List of all tricks in chronological order.
undo_memory: list[dict[str, Any]] = []
instance-attribute
Every action's apply method pushes a dictionary to this list with information about the previous state. This is in turn popped and used in the undo method. Index corresponds to action_history.
__init__(rule_set: AbstractRuleSet[GameState], dealer_idx: PlayerIdx, hands: list[list[Card]], skat: list[Card], log: bool = False)
Initialize a game.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
rule_set
|
AbstractRuleSet[GameState]
|
Ruleset to consider during the game. |
required |
dealer_idx
|
PlayerIdx
|
Index of the player who deals the hands. |
required |
hands
|
list[list[Card]]
|
3 hands of 10 cards, with the primary index corresponding to the player's index. |
required |
skat
|
list[Card]
|
2 cards to be buried in the Skat. |
required |
log
|
bool
|
Whether to log information about game state to stdout. |
False
|
Raises:
| Type | Description |
|---|---|
InvalidGameStateError
|
If hands or skat or dealer_idx are incorrect. |
apply_action(action: Action, check_validity: bool = True) -> None
Optionally check if action is valid, then call its apply method. Appends action to action_history.
calculate_game_score() -> list[int]
Wrapper for AbstractRuleSet.calculate_game_score.
from_random_deal(rule_set: AbstractRuleSet, dealer_idx: PlayerIdx, log: bool = False) -> Self
classmethod
Creates a game with a randomized deck. See init for docs.
get_player_position(player_idx: PlayerIdx) -> PlayerPosition
Returns the PlayerPosition for a given player with player_idx.
get_valid_actions(player_idx: PlayerIdx) -> Iterator[Action]
Wrapper for AbstractRuleSet.get_valid_actions.
undo_action() -> None
Pops the last action from action_history and executes its undo method.
Raises:
| Type | Description |
|---|---|
InvalidGameStateError
|
If action_history contains no action to undo. |
skaty.cards.Suit
Bases: IntEnum
Suits according to ISkO 1.2.1.
CLUBS = 3
class-attribute
instance-attribute
CLUBS
DIAMONDS = 0
class-attribute
instance-attribute
DIAMONDS
HEARTS = 1
class-attribute
instance-attribute
HEARTS
SPADES = 2
class-attribute
instance-attribute
SPADES
skaty.cards.Rank
Bases: IntEnum
Rank values to differentiate the cards value. These are not to be confused with their actual points according to ISkO 1.2.2 which are returned with the points method.
points: int
property
Returns the point value of the card rank in Skat (ISkO 1.2.2).
skaty.cards.Card
Card identified by rank and suit.
rank: Rank
property
Rank of card.
suit: Suit
property
Suit of card.
skaty.cards.create_deck() -> list[Card]
Creates an unshuffled Skat deck.
skaty.cards.shuffle_deck(deck: list[Card]) -> list[Card]
Shuffles a given deck of cards.
skaty.trick.Trick
A single trick of 3 cards.
cards: list[Card]
property
Cards in trick in order.
first_card: Optional[Card]
property
Returns the first card from trick if exists.
len: int
property
Number of cards in trick.
add_card(card: Card)
Appends a card to the trick.
Raises:
| Type | Description |
|---|---|
TrickFinishedError
|
If the trick is already complete. |
get_trick_points() -> int
Returns the sum of all card points. The trick does not have to be completed.
get_winner(rule_set: AbstractRuleSet, game_type: GameType) -> int
Determines the winner of the trick in its order (i.e. 0 if the first card wins the trick...).
Raises:
| Type | Description |
|---|---|
TrickNotFinishedError
|
If the trick does not contain exactly 3 cards. |
is_complete()
Is trick finished?
pop() -> Optional[Card]
Returns the last card played in the trick. Returns None if trick has no cards.
Rules
Information
skaty.rules.TState = TypeVar('TState', bound='GameState')
module-attribute
Any GameState.
skaty.rules.PlayerIdx = Literal[0, 1, 2]
Number to uniquely identify player in a single game.
skaty.rules.PlayerPosition
Bases: IntEnum
Position during bidding or while playing.
skaty.rules.GamePhase = NewType('GamePhase', str)
module-attribute
Differentiate between GamePhases and strings for type checking. GamePhase should have form scope + ':' + IDENTIFIER (e.g. 'core:BID').
skaty.rules.GamePhases
BID = GamePhase('core:BID')
class-attribute
instance-attribute
Bidding phase.
DECLARATION = GamePhase('core:DECLARATION')
class-attribute
instance-attribute
Waiting for a player to declare or use Skat.
GAME_OVER = GamePhase('core:GAME_OVER')
class-attribute
instance-attribute
Game over.
PLAYING = GamePhase('core:PLAYING')
class-attribute
instance-attribute
Playing cards in tricks.
skaty.rules.GameType = NewType('GameType', str)
module-attribute
Differentiate between GameTypes and strings for type checking. GameType should have form scope + ':' + IDENTIFIER (e.g. 'core:PASS')
skaty.rules.GameTypes
PASS = GameType('core:PASS')
class-attribute
instance-attribute
Used if a game is passed during bidding.
Action
skaty.rules.Action
dataclass
Bases: ABC, Generic[TState]
Base class for any action.
player_idx: PlayerIdx
instance-attribute
Player taking the action.
apply(state: TState, rule_set: AbstractRuleSet[TState]) -> None
abstractmethod
Mutate state dependent on action type, state and rules.
is_valid(state: TState, rule_set: AbstractRuleSet[TState]) -> bool
Wrapper for rule_set.is_valid_action(state,self).
undo(state: TState) -> None
abstractmethod
Reverse to state before action was applied. Restores state exactly.
Rule Set
skaty.rules.AbstractRuleSet
Bases: ABC, Generic[TState]
Base class for every rule set over a given type of state. The rule set itself is stateless. All state belongs to TState.
advance_state(state: TState, action: Action) -> None
abstractmethod
Hook for rule set to change state after an action is applied (e.g. change bidding phase).
calculate_game_score(state: TState) -> list[int]
abstractmethod
Attempt to calculate the game score in state. Returns a list with points (positive or negative) for each player with indexes corresponding to the state's players.
Raises:
| Type | Description |
|---|---|
InvalidGameStateError
|
If there is no game score in the current state. |
determine_trick_winner(trick: list[Card], game_type: GameType) -> int
abstractmethod
Calculates index of winning player.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
trick
|
list[Card]
|
Cards played in order of the trick. |
required |
game_type
|
GameType
|
Game type used for trick. |
required |
Returns:
| Type | Description |
|---|---|
PlayerIdx
|
Of the winning player |
Raises:
| Type | Description |
|---|---|
TrickNotFinishedError
|
If trick is not finished. |
InvalidGameTypeError
|
If game type does not allow for tricks. |
get_valid_actions(state: TState, player_idx: PlayerIdx) -> Generator[Action, None, None]
abstractmethod
Yields all valid actions the player can take in state.
initialize_state(state: TState) -> None
abstractmethod
Hook to initialize rule-specific attributes in state.
is_valid_action(state: TState, action: Action) -> bool
abstractmethod
Checks if an action is valid in the current state.
Exceptions
skaty.exceptions
InvalidActionError
Bases: SkatyError
Raised when an illegal action is tried (e.g. acting when not being the active player).
InvalidGameStateError
Bases: SkatyError
Raised if an operation is performed in an invalid game state (e.g. playing a card before a trick starts).
InvalidGameTypeError
Bases: SkatyError
Raised when an invalid game type is passed as an argument (e.g. passing GameType.PASS for is_valid_game_declaration).
NoCardsError
Bases: SkatyError
Raised when no cards are passed where there should be some.
NoHigherBidPossible
Bases: SkatyError
Raised when no higher bid is possible according to the rules.
SkatyError
Bases: Exception
Base exception for all Skaty-related errors.
TrickFinishedError
Bases: InvalidGameStateError
Raised when a card is added to a trick if it is finished.
TrickNotFinishedError
Bases: InvalidGameStateError
Raised when a trick with less than 3 cards is probed for a winner.
ISkO
State
skaty.isko.state.BiddingPhase
Bases: IntEnum
Phase in the bidding process.
skaty.isko.state.ISkOGameTypes
Bases: GameTypes
Extending GameTypes with ISkO game types.
CLUBS = GameType('isko:clubs')
class-attribute
instance-attribute
CLUBS
DIAMONDS = GameType('isko:diamonds')
class-attribute
instance-attribute
DIAMONDS
GRAND = GameType('isko:grand')
class-attribute
instance-attribute
GRAND
HEARTS = GameType('isko:hearts')
class-attribute
instance-attribute
HEARTS
NULL = GameType('isko:null')
class-attribute
instance-attribute
NULL
SPADES = GameType('isko:spades')
class-attribute
instance-attribute
SPADES
skaty.isko.state.GameDeclaration
dataclass
Everything an ISkO game declaration can contain.
game_type: GameType
instance-attribute
Game type to be played.
hand: bool = False
class-attribute
instance-attribute
Has the declarer looked at the Skat?
open: bool = False
class-attribute
instance-attribute
Has the declarer announced open? In case of Null game equivalent to ouvert.
schneider: bool = False
class-attribute
instance-attribute
Has the declarer announced Schneider?
schwarz: bool = False
class-attribute
instance-attribute
Has the declarer announced Schwarz?
skaty.isko.state.ISkOGameState
Bases: GameState
Extending GameState with attributes for ISkO rules.
bid_before: list[bool]
instance-attribute
Has the player with player_idx asserted a positive bid (DeclareBid or Listen) before?
bidding_phase: BiddingPhase
instance-attribute
Current bidding phase.
declaration: Optional[GameDeclaration]
instance-attribute
Declaration made by declarer.
declarer_idx: Optional[PlayerIdx]
instance-attribute
PlayerIdx of the declarer.
last_bid: Optional[DeclareBid | Listen | Pass]
instance-attribute
Last bid action to happen.
passes: list[bool]
instance-attribute
Has the player with player_idx asserted a negative bid/Pass before?
tops: Optional[int]
instance-attribute
Tops in declarers hand and the start of game.
tricks_won: list[int]
instance-attribute
Tricks won by player with player_idx
skaty.isko.state.T_ISkOGameState = TypeVar('T_ISkOGameState', bound=ISkOGameState)
module-attribute
Any GameState based on ISkOGameState.
Actions
skaty.isko.actions.DeclareBid
dataclass
skaty.isko.actions.Listen
dataclass
skaty.isko.actions.Pass
dataclass
skaty.isko.actions.DrawSkat
dataclass
skaty.isko.actions.BurySkat
dataclass
skaty.isko.actions.DeclareGame
dataclass
Bases: Action[T_ISkOGameState]
Declare specific game. Hand is applied automatically dependent on the game state.
skaty.isko.actions.PlayCard
dataclass
Rule Set
skaty.isko.rules.VALID_BIDS = [18, 20, 22, 23, 24, 27, 30, 33, 35, 36, 40, 44, 45, 46, 48, 50, 54, 55, 59, 60, 63, 66, 70, 72, 77, 80, 81, 84, 88, 90, 96, 99, 100, 108, 110, 117, 120, 121, 126, 130, 132, 135, 140, 143, 144, 150, 153, 154, 156, 160, 162, 165, 168, 170, 176, 180, 187, 192, 198, 204, 216, 240, 264]
module-attribute
skaty.isko.rules.ISkO
Bases: AbstractRuleSet[T_ISkOGameState]
advance_bidding(state: T_ISkOGameState, action: DeclareBid | Listen | Pass) -> None
Mutate the state in bidding dependent on action. Might modify:
- state.active_player
- state.bidding_phase
- state.phase
- state.declarer_idx
If bidding continues after action, update:
- state.active_player: to the next player bidding
- state.bidding_phase: if a player passes
If bidding finishes with a declarer after action, update:
- state.active_player: to be index of declarer
- state.phase: to GamePhase.DECLARATION
- state.declarer_idx: to be index of declarer
If bidding finishes with all players passing after action, update:
- state.phase: to GamePhase.GAME_OVER.
advance_playing(state: T_ISkOGameState, action: PlayCard) -> None
Mutate the state as card is played. Might modify:
- state.active_player
- state.phase
- state.current_trick
- state.trick_history
- state.points
- state.tricks_won
advance_state(state: T_ISkOGameState, action: Action) -> None
Advance state with action. Advances bidding/active player, sets game declarations etc.
calculate_game_score(state: T_ISkOGameState) -> list[int]
Score calculation according to ISkO and docstring of base class.
determine_trick_winner(trick: list[Card], game_type: GameType) -> int
Determines the winner of the trick in its order (i.e. 0 if the first card wins the trick...).
Raises:
| Type | Description |
|---|---|
TrickNotFinishedError
|
If trick does not contain exactly 3 cards. |
InvalidGameTypeError
|
If game_type is GameType.PASS. |
get_action_types_for_phase(phase: GamePhase) -> list[type[Action]]
Returns all valid action types for a given phase.
get_card_effective_rank_value(card: Card, game_type: GameType) -> int
Returns a value representing a cards relative strength in a game type.
Raises:
| Type | Description |
|---|---|
InvalidGameTypeError
|
If game_type is GameType.PASS. |
get_next_valid_bid(current_bid: Optional[int]) -> int
Calculates the next highest valid bid.
Raises:
| Type | Description |
|---|---|
NoHigherBidPossible
|
If no higher bid is possible. |
get_valid_actions(state: T_ISkOGameState, player_idx: PlayerIdx) -> Generator[Action, None, None]
Implement per base class docstring.
has_suit(hand: list[Card], suit: Suit) -> bool
Does the hand contain cards with suit?
has_trump(hand: list[Card], game_type: GameType) -> bool
Does the hand contain trump cards
is_card_trump(card: Card, game_type: GameType) -> bool
Is card trump?
is_valid_action(state: T_ISkOGameState, action: Action) -> bool
Can action be applied in state? Checks if player is active and per action validity according to the rules.
Raises:
| Type | Description |
|---|---|
InvalidActionError
|
If action is not checked, because it is unknown. |
is_valid_action_type_during_phase(action: Action, phase: GamePhase) -> bool
Tests if action type is possible in game phase. Does not imply the particular action itself is valid (e.g. for Bid(17), GamePhase.BIDDING it would return True, as Bid is a valid action in bidding even though Bid(17) itself is not valid).
is_valid_bid(state: T_ISkOGameState, bid: DeclareBid | Listen | Pass) -> bool
Determines if bid is valid for player in player_pos in the context of previous_bids and bidding_phase. Passing is allowed for every player in every bidding phase if they have not passed before or bid/listened before and are the only one left.
is_valid_card_play(hand: list[Card], card: Card, first_card: Optional[Card], game_type: GameType) -> bool
Determines if player can play card in a trick started with first_card in some GameType. For an empty trick, first_card is None. If game_type is GameType.PASS always returns False regardless of card and first_card.
is_valid_game_declaration(declaration: GameDeclaration) -> bool
Determines if the game declaration is formally correct. It does not check if the game satisfies the bid. A player is allowed to overbid, but will lose during score calculation.
tops(cards: list[Card], game_type: GameType) -> int
Calculates the amount of tops according to ISkO 2.3.
Raises:
| Type | Description |
|---|---|
ValueError
|
If the cards list is empty. |
InvalidGameTypeError
|
If game_type is GameType.NULL or GameType.PASS. |
trump_suit(game_type: GameType) -> Optional[Suit]
Return trump suit for game type if exists.