Skip to content

Commit

Permalink
Finish GameMetadata rework
Browse files Browse the repository at this point in the history
  • Loading branch information
trevorbayless committed Jun 5, 2024
1 parent b24eba6 commit f027531
Show file tree
Hide file tree
Showing 13 changed files with 114 additions and 112 deletions.
2 changes: 1 addition & 1 deletion src/cli_chess/core/game/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@
from .game_presenter_base import GamePresenterBase, PlayableGamePresenterBase
from .online_game.online_game_presenter import start_online_game
from .offline_game.offline_game_presenter import start_offline_game
from .game_metadata import GameMetadata, Player
from .game_metadata import GameMetadata, PlayerMetadata, ClockMetadata
49 changes: 27 additions & 22 deletions src/cli_chess/core/game/game_metadata.py
Original file line number Diff line number Diff line change
@@ -1,36 +1,41 @@
from dataclasses import dataclass
from chess import Color
from typing import Optional


@dataclass
class Player:
title: str = None
name: str = None
rating: str = None
ai_level: str = None # only used online
rating_diff: int = None
is_provisional_rating: bool = False # only used online
class PlayerMetadata:
title: Optional[str] = None
name: Optional[str] = None
rating: Optional[str] = None
rating_diff: Optional[int] = None
is_provisional_rating: bool = False
ai_level: Optional[str] = None


@dataclass
class Clock:
class ClockMetadata:
units: str = "ms"
time: int = 0
increment: int = 0
time: Optional[int] = None
increment: Optional[int] = None


@dataclass
class GameStatus:
status: str = None
winner: str = None
class GameStatusMetadata:
status: Optional[str] = None
winner: Optional[str] = None


@dataclass
class GameMetadata:
players = [Player(), Player()]
clocks = [Clock(), Clock()]
game_status = GameStatus()
game_id: str = None
variant: str = None
my_color: str = None # TODO: Find a better solution
rated: bool = False # only used online
speed: str = None # only used online
def __init__(self):
self.players = [PlayerMetadata(), PlayerMetadata()]
self.clocks = [ClockMetadata(), ClockMetadata()]
self.game_status = GameStatusMetadata()
self.game_id: Optional[str] = None
self.variant: Optional[str] = None
self.my_color: Optional[Color] = None
self.rated: bool = False
self.speed: Optional[str] = None

def reset(self):
self.__init__()
3 changes: 1 addition & 2 deletions src/cli_chess/core/game/game_presenter_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,7 @@ def __init__(self, model: GameModelBase):
self.move_list_presenter = MoveListPresenter(model.move_list_model)
self.material_diff_presenter = MaterialDifferencePresenter(model.material_diff_model)
self.player_info_presenter = PlayerInfoPresenter(model)
# TODO: Revert this after testing
# self.clock_presenter = ClockPresenter(model)
self.clock_presenter = ClockPresenter(model)
self.view = self._get_view()

self.model.e_game_model_updated.add_listener(self.update)
Expand Down
5 changes: 2 additions & 3 deletions src/cli_chess/core/game/game_view_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,8 @@ def __init__(self, presenter: GamePresenterBase) -> None:
self.material_diff_lower_container = presenter.material_diff_presenter.view_lower
self.player_info_upper_container = presenter.player_info_presenter.view_upper
self.player_info_lower_container = presenter.player_info_presenter.view_lower
# TODO: Revert this after testing
# self.clock_upper = presenter.clock_presenter.view_upper
# self.clock_lower = presenter.clock_presenter.view_lower
self.clock_upper = presenter.clock_presenter.view_upper
self.clock_lower = presenter.clock_presenter.view_lower
self.alert = AlertContainer()
self._container = self._create_container()

Expand Down
2 changes: 1 addition & 1 deletion src/cli_chess/core/game/offline_game/offline_game_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ def _update_game_metadata(self, **kwargs) -> None:
try:
if 'game_parameters' in kwargs:
data = kwargs['game_parameters']
self.game_metadata.my_color = COLOR_NAMES[self.my_color]
self.game_metadata.my_color = self.my_color

self.game_metadata.variant = data[GameOption.VARIANT]
self.game_metadata.players[self.my_color].name = player_info_config.get_value(player_info_config.Keys.OFFLINE_PLAYER_NAME) # noqa: E501
Expand Down
101 changes: 48 additions & 53 deletions src/cli_chess/core/game/online_game/online_game_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from cli_chess.core.game.game_options import GameOption
from cli_chess.core.api import GameStateDispatcher
from cli_chess.utils import log, threaded, RequestSuccessfullySent
from chess import COLOR_NAMES, WHITE
from chess import COLORS, COLOR_NAMES, WHITE, BLACK, Color
from typing import Optional


Expand All @@ -12,12 +12,11 @@ class OnlineGameModel(PlayableGameModelBase):
"""
def __init__(self, game_parameters: dict, is_vs_ai: bool):
super().__init__(play_as_color=game_parameters[GameOption.COLOR], variant=game_parameters[GameOption.VARIANT], fen=None)
self._update_game_metadata(game_parameters=game_parameters)

self.game_state_dispatcher = Optional[GameStateDispatcher]
self.vs_ai = is_vs_ai
self.playing_game_id = None
self.searching = False
self.vs_ai = is_vs_ai
self._update_game_metadata(game_parameters=game_parameters)
self.game_state_dispatcher = Optional[GameStateDispatcher]

try:
from cli_chess.core.api.api_manager import api_client, api_iem
Expand All @@ -37,17 +36,17 @@ def create_game(self) -> None:
self.searching = True

if self.vs_ai: # Challenge Lichess AI (stockfish)
self.api_client.challenges.create_ai(level=self.game_metadata['ai_level'],
clock_limit=self.game_metadata['clock']['white']['time'],
clock_increment=self.game_metadata['clock']['white']['increment'],
color=self.game_metadata['my_color_str'],
variant=self.game_metadata['variant'])
self.api_client.challenges.create_ai(level=self.game_metadata.players[not self.my_color].ai_level,
clock_limit=self.game_metadata.clocks[WHITE].time * 60, # challenges need time in seconds
clock_increment=self.game_metadata.clocks[WHITE].increment,
color=COLOR_NAMES[self.game_metadata.my_color],
variant=self.game_metadata.variant)
else: # Find a random opponent
self.api_client.board.seek(time=self.game_metadata['clock']['white']['time'], # Both players initially have the same time
increment=self.game_metadata['clock']['white']['increment'],
color=self.game_metadata['my_color_str'],
variant=self.game_metadata['variant'],
rated=self.game_metadata['rated'],
self.api_client.board.seek(time=self.game_metadata.clocks[WHITE].time,
increment=self.game_metadata.clocks[WHITE].increment,
color=COLOR_NAMES[self.game_metadata.my_color],
variant=self.game_metadata.variant,
rated=self.game_metadata.rated,
rating_range=None)

def _start_game(self, game_id: str) -> None:
Expand Down Expand Up @@ -92,7 +91,7 @@ def handle_game_state_dispatcher_event(self, **kwargs) -> None:
if 'gameFull' in kwargs:
event = kwargs['gameFull']
self._update_game_metadata(gsd_gameFull=event)
self.board_model.reinitialize_board(variant=self.game_metadata['variant'],
self.board_model.reinitialize_board(variant=self.game_metadata.variant,
orientation=(self.my_color if self.board_model.get_variant_name() != "racingkings" else WHITE),
fen=event.get('initialFen', ""))
self.board_model.make_moves_from_list(event.get('state', {}).get('moves', []).split())
Expand Down Expand Up @@ -219,56 +218,52 @@ def _update_game_metadata(self, **kwargs) -> None:
try:
if 'game_parameters' in kwargs: # This is the data that came from the menu selections
data = kwargs['game_parameters']
self.game_metadata['my_color_str'] = COLOR_NAMES[self.my_color]
self.game_metadata['variant'] = data.get(GameOption.VARIANT)
self.game_metadata['rated'] = data.get(GameOption.RATED, False) # Games against AI will not have this data
self.game_metadata['ai_level'] = data.get(GameOption.COMPUTER_SKILL_LEVEL) # Only games against AI will have this data
self.game_metadata['clock']['white']['time'] = data.get(GameOption.TIME_CONTROL)[0] # mins
self.game_metadata['clock']['white']['increment'] = data.get(GameOption.TIME_CONTROL)[1] # secs
self.game_metadata['clock']['black'] = self.game_metadata['clock']['white']

if self.game_metadata['ai_level']:
self.game_metadata['clock']['white']['time'] = data.get(GameOption.TIME_CONTROL)[0] * 60 # challenges need time in seconds
self.game_metadata['clock']['black'] = self.game_metadata['clock']['white']
self.game_metadata.my_color = self.my_color
self.game_metadata.variant = data.get(GameOption.VARIANT)
self.game_metadata.rated = data.get(GameOption.RATED, False)
self.game_metadata.players[not self.my_color].ai_level = data.get(GameOption.COMPUTER_SKILL_LEVEL) if self.vs_ai else None

for color in COLORS:
self.game_metadata.clocks[color].time = data.get(GameOption.TIME_CONTROL)[0] # mins
self.game_metadata.clocks[color].increment = data.get(GameOption.TIME_CONTROL)[1] # secs

elif 'iem_gameStart' in kwargs:
# Reset game metadata
# self.game_metadata = self._default_game_metadata()
self.game_metadata.reset()

data = kwargs['iem_gameStart']
self.game_metadata['gameId'] = data.get('gameId')
self.game_metadata['my_color_str'] = data.get('color')
self.game_metadata['rated'] = data.get('rated')
self.game_metadata['variant'] = data.get('variant', {}).get('name')
self.game_metadata['speed'] = data['speed']
self.game_metadata.game_id = data.get('gameId')
self.game_metadata.my_color = self.my_color
self.game_metadata.rated = data.get('rated')
self.game_metadata.variant = data.get('variant', {}).get('name')
self.game_metadata.speed = data['speed']

elif 'iem_gameFinish' in kwargs:
data = kwargs['iem_gameFinish']
self.game_metadata['players'][COLOR_NAMES[self.my_color]]['rating_diff'] = data.get('ratingDiff', "")
self.game_metadata['players'][COLOR_NAMES[not self.my_color]]['rating_diff'] = data.get('opponent', {}).get('ratingDiff', "")
self.game_metadata.players[self.my_color].rating_diff = data.get('ratingDiff', "")
self.game_metadata.players[not self.my_color].rating_diff = data.get('opponent', {}).get('ratingDiff', "")

elif 'gsd_gameFull' in kwargs:
data = kwargs['gsd_gameFull']

for color in COLOR_NAMES:
if data.get(color, {}).get('name'):
self.game_metadata['players'][color]['title'] = data.get(color, {}).get('title')
self.game_metadata['players'][color]['name'] = data.get(color, {}).get('name', "?")
self.game_metadata['players'][color]['rating'] = data.get(color, {}).get('rating', "?")
self.game_metadata['players'][color]['provisional'] = data.get(color, {}).get('provisional', False)
elif data.get(color, {}).get('aiLevel'):
self.game_metadata['players'][color]['name'] = f"Stockfish level {data.get(color, {}).get('aiLevel', '?')}"

self.game_metadata['clock']['units'] = "ms"
self.game_metadata['clock']['white']['time'] = data.get('state', {}).get('wtime')
self.game_metadata['clock']['white']['increment'] = data.get('state', {}).get('winc')
self.game_metadata['clock']['black']['time'] = data.get('state', {}).get('btime')
self.game_metadata['clock']['black']['increment'] = data.get('state', {}).get('binc')
side_data = data.get(color, {})
color_as_bool = Color(COLOR_NAMES.index(color))
if side_data.get('name'):
self.game_metadata.players[color_as_bool].title = side_data.get('title')
self.game_metadata.players[color_as_bool].name = side_data.get('name', "?")
self.game_metadata.players[color_as_bool].rating = side_data.get('rating', "?")
self.game_metadata.players[color_as_bool].is_provisional_rating = side_data.get('provisional', False)
elif self.vs_ai:
self.game_metadata.players[color_as_bool].name = f"Stockfish level {side_data.get('aiLevel', '?')}"

self.game_metadata.clocks[color_as_bool].units = "ms"
self.game_metadata.clocks[color_as_bool].time = data.get('state', {}).get('wtime' if color == "white" else 'btime')
self.game_metadata.clocks[color_as_bool].increment = data.get('state', {}).get('winc' if color == "white" else 'binc')

elif 'gsd_gameState' in kwargs:
data = kwargs['gsd_gameState']
self.game_metadata['clock']['white']['time'] = data.get('wtime')
self.game_metadata['clock']['black']['time'] = data.get('btime')
self.game_metadata.clocks[WHITE].time = data.get('wtime')
self.game_metadata.clocks[BLACK].time = data.get('btime')

self._notify_game_model_updated()
except Exception as e:
Expand All @@ -280,8 +275,8 @@ def _report_game_over(self, status: str, winner: str) -> None:
This should only ever be called if the game is confirmed to be over
"""
self._game_end()
self.game_metadata['state']['status'] = status # status list can be found in lila status.ts
self.game_metadata['state']['winner'] = winner
self.game_metadata.game_status.status = status # status list can be found in lila status.ts
self.game_metadata.game_status.winner = winner
self._notify_game_model_updated(onlineGameOver=True)

def cleanup(self) -> None:
Expand Down
4 changes: 2 additions & 2 deletions src/cli_chess/core/game/online_game/online_game_presenter.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ def update(self, **kwargs) -> None:
def _parse_and_present_game_over(self) -> None:
"""Triages game over status for parsing and sending to the view for display"""
if not self.is_game_in_progress():
status: str = self.model.game_metadata['state']['status']
winner_str = self.model.game_metadata['state']['winner']
status = self.model.game_metadata.game_status.status
winner_str = self.model.game_metadata.game_status.winner

if winner_str: # Handle win/loss output
self._display_win_loss_output(status, winner_str)
Expand Down
4 changes: 2 additions & 2 deletions src/cli_chess/core/game/online_game/online_game_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@ def _create_container(self) -> Container:
VSplit([
self.board_output_container,
HSplit([
#self.clock_upper,
self.clock_upper,
self.player_info_upper_container,
self.material_diff_upper_container,
self.move_list_container,
self.material_diff_lower_container,
self.player_info_lower_container,
#self.clock_lower
self.clock_lower
])
]),
self.input_field_container,
Expand Down
27 changes: 14 additions & 13 deletions src/cli_chess/core/game/online_game/watch_tv/watch_tv_model.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from cli_chess.core.game import GameModelBase, GameMetadata
from cli_chess.core.game import GameModelBase
from cli_chess.menus.tv_channel_menu import TVChannelMenuOptions
from cli_chess.utils.event import Event
from cli_chess.utils.logging import log
from chess import COLOR_NAMES, Color, WHITE, BLACK
from chess import COLOR_NAMES, COLORS, Color, WHITE, BLACK
from berserk.exceptions import ResponseError
from time import sleep
import threading
Expand Down Expand Up @@ -30,7 +30,7 @@ def _update_game_metadata(self, **kwargs) -> None:
if 'tv_descriptionEvent' in kwargs:
data = kwargs['tv_descriptionEvent']
if 'tv_startGameEvent' in kwargs:
self.game_metadata = GameMetadata()
self.game_metadata.reset()

self.game_metadata.game_id = data.get('id')
self.game_metadata.rated = data.get('rated')
Expand All @@ -43,20 +43,21 @@ def _update_game_metadata(self, **kwargs) -> None:
color_as_bool = Color(COLOR_NAMES.index(color))
side_data = data.get('players', {}).get(color, {})
player_data = side_data.get('user')
ai_level = player_data.get('aiLevel')
if side_data and player_data: # non-ai player data
self.game_metadata.players[color_as_bool].name = player_data.get('name', "")
self.game_metadata.players[color_as_bool].rating = side_data.get('rating', "")
ai_level = side_data.get('aiLevel')
if side_data and player_data:
self.game_metadata.players[color_as_bool].title = player_data.get('title')
self.game_metadata.players[color_as_bool].name = player_data.get('name', "?")
self.game_metadata.players[color_as_bool].rating = side_data.get('rating', "?")
self.game_metadata.players[color_as_bool].is_provisional_rating = side_data.get('provisional', False)
self.game_metadata.players[color_as_bool].rating_diff = side_data.get('ratingDiff', "")
elif ai_level: # ai data
elif ai_level:
self.game_metadata.players[color_as_bool].name = f"Stockfish level {ai_level}"

if 'tv_coreGameEvent' in kwargs:
data = kwargs['tv_coreGameEvent']
self.game_metadata.clocks[WHITE].units = "sec"
self.game_metadata.clocks[BLACK].units = "sec"
self.game_metadata.clocks[WHITE].time = data.get('wc')
self.game_metadata.clocks[BLACK].time = data.get('bc')
for color in COLORS:
self.game_metadata.clocks[color].units = "sec"
self.game_metadata.clocks[color].time = data.get('wc' if color == WHITE else 'bc')

self.e_game_model_updated.notify()
except Exception as e:
Expand All @@ -77,7 +78,7 @@ def stream_event_received(self, **kwargs):
variant = event.get('variant', {}).get('key')
white_rating = int(event.get('players', {}).get('white', {}).get('rating') or 0)
black_rating = int(event.get('players', {}).get('black', {}).get('rating') or 0)
orientation = True if ((white_rating >= black_rating) or self.channel.key == "racingKings") else False
orientation = WHITE if ((white_rating >= black_rating) or self.channel.key == "racingKings") else BLACK

self._update_game_metadata(tv_descriptionEvent=event, tv_startGameEvent=True)
last_move = event.get('lastMove', "")
Expand Down
Loading

0 comments on commit f027531

Please sign in to comment.