Source code for j_chess_lib.ai.ai

import logging
from abc import ABC, abstractmethod
from typing import Optional, Tuple, Dict, Any, List
from uuid import UUID

from j_chess_lib import logger as lib_logger
from .container import GameState
from ..communication.schema import MatchStatusData, MatchFormatData, MoveData


[docs]class AI(ABC): def __init__(self, name: str = None): self._name = f"{self.__class__.__name__}-PyAI" if name is None else name @property def name(self) -> str: return self._name def __str__(self): return self.name
[docs] @abstractmethod def new_match(self, match_id: UUID, enemy: str, match_format: MatchFormatData): pass
[docs] @abstractmethod def finalize_match(self, match_id: UUID, status: MatchStatusData, statistics: str): pass
[docs] @abstractmethod def new_game(self, game_id: UUID, match_id: UUID, white_player: str): pass
[docs] @abstractmethod def finalize_game(self, game_id: UUID, match_id: UUID, winner: Optional[str], pgn: str): pass
[docs] @abstractmethod def get_move(self, game_id: UUID, match_id: UUID, game_state: GameState) -> MoveData: pass
# noinspection PyMethodMayBeStatic
[docs] def metrics(self) -> List[Tuple[str, Any]]: return [("name", self.name)]
[docs]class DumbAI(AI, ABC):
[docs] def new_match(self, match_id: UUID, enemy: str, match_format: MatchFormatData): pass
[docs] def finalize_match(self, match_id: UUID, status: MatchStatusData, statistics: str): pass
[docs] def new_game(self, game_id: UUID, match_id: UUID, white_player: str): pass
[docs] def finalize_game(self, game_id: UUID, match_id: UUID, winner: Optional[str], pgn: str): pass
[docs]class StoreAI(AI, ABC): def __init__(self, name: str = None): super().__init__(name) self._match_storage: Dict[UUID, Tuple[str, MatchFormatData]] = {} self._game_storage: Dict[Tuple[UUID, UUID], str] = {} self._current_match: Optional[UUID] = None self._current_game: Optional[Tuple[UUID, UUID]] = None
[docs] def get_match(self, match_id: UUID) -> Optional[Tuple[str, MatchFormatData]]: return self._match_storage.get(match_id, None)
[docs] def get_game(self, game_id: UUID, match_id: UUID) -> Optional[str]: return self._game_storage.get((match_id, game_id), None)
[docs] def new_match(self, match_id: UUID, enemy: str, match_format: MatchFormatData): self._match_storage[match_id] = (enemy, match_format) self._current_match = match_id
[docs] def finalize_match(self, match_id: UUID, status: MatchStatusData, statistics: str): pass
[docs] def new_game(self, game_id: UUID, match_id: UUID, white_player: str): self._game_storage[(match_id, game_id)] = white_player self._current_game = (game_id, match_id)
[docs] def finalize_game(self, game_id: UUID, match_id: UUID, winner: Optional[str], pgn: str): pass
[docs] def metrics(self) -> List[Tuple[str, Any]]: metrics = super().metrics() if self._current_match is not None: enemy, match = self.get_match(match_id=self._current_match) match: MatchFormatData metrics.append(("In match", self._current_match)) metrics.append((" against", enemy)) metrics.append((" match type", f"{match.match_type_data}")) if self._current_game is not None: game_id, match_id = self._current_game enemy, match = self.get_match(match_id=match_id) white_player = self.get_game(game_id=game_id, match_id=match_id) metrics.append(("In game", game_id)) metrics.append((" of match", f"{match_id} -> {match.match_type_value.value}")) metrics.append((" against", enemy)) metrics.append((" white player", f"{white_player} <- {'me' if white_player == self.name else 'enemy'}")) return metrics
[docs]class VerboseAI(StoreAI, ABC): def __init__( self, name: str = None, verbose: bool = True, logger: logging.Logger = None, level: int = logging.INFO ): if logger is None: logger = lib_logger super().__init__(name=name) self.verbose = verbose self._logger = logger self._level = level @property def logger(self): return self._logger
[docs] def new_match(self, match_id: UUID, enemy: str, match_format: MatchFormatData): super().new_match(match_id=match_id, enemy=enemy, match_format=match_format) self._logger.log( level=self._level, msg=f"{self.name} starts a {match_format.match_type_value} match against {enemy} [{match_id}]" )
[docs] def finalize_match(self, match_id: UUID, status: MatchStatusData, statistics: str): super().finalize_match(match_id=match_id, status=status, statistics=statistics) i_am_1 = status.name_player1 == self.name my_score = status.score_player1 if i_am_1 else status.score_player2 enemy_score = status.score_player2 if i_am_1 else status.score_player1 name_enemy = status.name_player2 if i_am_1 else status.name_player1 self._logger.log( level=self._level, msg=f"{self.name} finished a match against {name_enemy}: {my_score} to {enemy_score} [{match_id}]" )
[docs] def new_game(self, game_id: UUID, match_id: UUID, white_player: str): super().new_game(game_id=game_id, match_id=match_id, white_player=white_player) enemy = self.get_match(match_id=match_id)[0] white = white_player == self.name self._logger.log( level=self._level, msg=f"{self.name} starts a game against {enemy} as {'white' if white else 'black'} [{match_id}] [{game_id}]" )
[docs] def finalize_game(self, game_id: UUID, match_id: UUID, winner: Optional[str], pgn: str): enemy = self.get_match(match_id=match_id)[0] result = 'winner' if winner == self.name else 'remis' if winner is None else 'loser' pgn = "\n".join(f"│ {x}" for x in pgn.strip().split("\n")) self._logger.log( level=self._level, msg=f"{self.name} finished a game against {enemy} as {result} [{match_id}] [{game_id}]\n" f"┌{'─' * 15}\n" f"{pgn}\n" f"└{'─' * 15}" )
[docs] def log_move(self, move_data: MoveData): self.logger.log( level=self._level, msg=f"{self.name} moves from {move_data.from_value} to {move_data.to}" f"{f' Promotion {move_data.promotion_unit}' if move_data.promotion_unit else ''}" )