Source code for maverick.player

import warnings
from abc import ABC, ABCMeta, abstractmethod
from typing import TYPE_CHECKING, Optional
import uuid

from .enums import ActionType
from .playeraction import PlayerAction
from .playerstate import PlayerState
from ._registered_players import registered_players

if TYPE_CHECKING:  # pragma: no cover
    from .game import Game
    from .events import GameEvent

__all__ = ["Player"]

# Registry for classes by their cls_uid
_uid_registry: dict[str, type["Player"]] = {}


class PlayerMeta(ABCMeta):

    def __init__(self, name, bases, namespace, *args, **kwargs):
        super().__init__(name, bases, namespace, *args, **kwargs)

    def __new__(metaclass, name, bases, namespace, *args, **kwargs):
        cls = super().__new__(metaclass, name, bases, namespace, *args, **kwargs)
        if not ABC in bases and getattr(cls, "register", True):
            registered_players[cls.__name__] = cls
            # Register by cls_uid if it exists
            cls_uid = getattr(cls, "cls_uid", None)
            if cls_uid is not None:
                _uid_registry[cls_uid] = cls
        return cls


[docs] class Player(metaclass=PlayerMeta): """Abstract base class for a poker player.""" register: bool = True cls_uid: Optional[str] = None
[docs] def __init__( self, *, uid: Optional[str] = None, name: str, state: Optional[PlayerState | dict] = None, **kwargs, ): _id = kwargs.pop("id", None) if _id: warnings.warn( "Passing id= to Player.__init__ is deprecated, use uid= instead.", DeprecationWarning, stacklevel=2, ) self.uid = uid or _id or uuid.uuid4().hex self.name = name self.state = ( state if isinstance(state, PlayerState) or state is None else PlayerState.model_validate(state) )
@property def id(self) -> str: """Deprecated: use ``uid`` instead.""" warnings.warn( "Player.id is deprecated, use Player.uid instead.", DeprecationWarning, stacklevel=2, ) return self.uid
[docs] @classmethod def get_by_uid(cls, uid: str) -> Optional[type["Player"]]: """ Get a player class by its unique identifier. Parameters ---------- uid : str The unique identifier of the player class. Returns ------- Optional[type[Player]] The player class with the given uid, or None if not found. """ return _uid_registry.get(uid)
[docs] @abstractmethod def decide_action( self, *, game: "Game", valid_actions: list[ActionType], min_raise_amount: int, call_amount: int, min_bet_amount: int, ) -> PlayerAction: """ Decide on an action to take during the player's turn. The function should return a valid instance of PlayerAction. Parameters ---------- game : Game The game instance containing the current state. valid_actions : list[ActionType] List of valid actions the player can take. min_raise_amount : int Minimum extra chips this player must add right now to complete a minimum raise. call_amount : int Amount of chips this player must add right now to call the current bet. min_bet_amount : int Minimum chips this player must add right now to make a bet. Returns ------- PlayerAction An instance of PlayerAction representing the chosen action. """ ...
[docs] def on_event(self, event: "GameEvent", game: "Game") -> None: # pragma: no cover """ Optional hook called when a game event occurs. This method is called synchronously after global event handlers. Exceptions in this method are caught and logged by the engine. Parameters ---------- event : GameEvent The game event that occurred. game : Game The game instance containing the current state. Notes ----- This is an optional hook. The default implementation does nothing. Subclasses can override this method to observe events. """ ...
[docs] def to_dict(self) -> dict: """ Serialize the player to a dictionary. Returns ------- dict A dictionary representation of the player. """ return { "uid": self.uid, "name": self.name, "state": self.state.model_dump() if self.state else None, }