"""A collection of player classes.
Includes:
    * Player
    * FixedPolicyPlayer
"""
from abc import ABC, abstractmethod
from enum import Enum
from typing import Sequence
import numpy as np
from imperfecto.misc.utils import get_action
[docs]class Player(ABC):
    """
    An abstract player class.
   Attributes:
        name (str): The name of the player.
        strategy (dict): map from instostate to np.array of shape (n_actions,).
    """
    def __init__(self, name=""):
        self.name = name
        self.strategy = {}
    def __str__(self) -> str:
        if self.name == "":
            return self.__class__.__name__
        else:
            return "Player(" + self.name + ")"
    def __repr__(self) -> str:
        if self.name is None:
            return self.__class__.__name__
        else:
            return "Player(" + self.name + ")"
[docs]    def act(self, infostate: str) -> int:
        """
        Returns the action to take given an infostate.
        Args:
            infostate: The infostate to take the action in.
        Returns:
            The action to take.
        """
        if infostate not in self.strategy:
            # uniform
            self.strategy[infostate] = np.ones(
                len(self.game.actions)) / len(self.game.actions)
        return get_action(self.strategy[infostate]) 
[docs]    @abstractmethod
    def update_strategy(self, history: Sequence[Enum], player_id: int) -> None:
        """Update the strategy of the player at the end of the game.
        Args:
            history: The history of the game, which *must* be a terminal node.
            player_id: The id of the player to update (i.e., my id).
        """
        pass 
    @property
    def game(self):
        return self._game
    @game.setter
    def game(self, game):
        self._game = game
    @property
    def strategy(self):
        """A dict with key = infostate and value = np.array of shape (n_actions,)."""
        return self._strategy
    @strategy.setter
    def strategy(self, strategy: dict):
        self._strategy = strategy 
[docs]class FixedPolicyPlayer(Player):
    """A player with a given fixed strategy.
    Args:
        name: The name of the player.
        strategy: The fixed strategy of the player.
    Attributes:
        name (str): The name of the player.
        strategy (dict): The fixed strategy of the player.
    """
    def __init__(self, name: str, strategy: dict):
        super().__init__(name)
        self.strategy = strategy
[docs]    def update_strategy(self, history: Sequence[Enum], player_id: int) -> None:
        del history, player_id  # do nothing
        pass