diff --git a/Server/game.py b/Server/game.py index c6fa27d..5bf36e0 100644 --- a/Server/game.py +++ b/Server/game.py @@ -5,15 +5,23 @@ from random import shuffle class Game: - def __init__(self, player1: Player, player2: Player, rule_base: RuleBase = RuleBase()): - """ - Initializes a new instance of the Game class. + """ + Initializes a new instance of the Game class. - Args: - player1 (Player): The first player. - player2 (Player): The second player. - rule_base (RuleBase, optional): The rule base for the game. Defaults to RuleBase(). - """ + Parameters: + player1 (Player): The first player. + player2 (Player): The second player. + rule_base (RuleBase, optional): The rule base for the game. Defaults to RuleBase(). + + Properties: + current_player_uuid (str): The UUID string of the current player. + winner (Player): The winner of the game. + + Methods: + move(self, player: int, new_position: tuple[int, int]): Makes a move in the game. + """ + + def __init__(self, player1: Player, player2: Player, rule_base: RuleBase = RuleBase()): self._uuid: uuid.UUID = uuid.uuid4() self._id: int = self._uuid.int self.state = GameState() @@ -26,7 +34,7 @@ def move(self, player: int, new_position: tuple[int, int]): """ Makes a move in the game. - Args: + Parameters:: player (int): The player making the move. new_position (tuple[int, int]): The new position to place the player's move. @@ -39,7 +47,7 @@ def move(self, player: int, new_position: tuple[int, int]): self.rule_base.check_win(self.state) except ValueError as e: raise ValueError(e) - + @property def current_player_uuid(self) -> str: """ diff --git a/Server/gamestate.py b/Server/gamestate.py index 6b95487..b58703b 100644 --- a/Server/gamestate.py +++ b/Server/gamestate.py @@ -4,19 +4,20 @@ class GameState: """ Represents the state of a Tic-Tac-Toe game. - Attributes: - _playfield (list[list[int]]): The playfield of the game, represented as a 2D list. - _finished (bool): Indicates whether the game has finished. - _winner (int): The number of the winning player, if any. + Parameters: + playfield_dimensions (tuple[int, int]): The dimensions of the playfield (default: (3, 3)). + + Properties: + winner (int): The index of the winning player. + finished (bool): True if the game has finished, False otherwise. + playfield (list[list[int]]): The playfield of the game. + current_player (int): The index of the current player. + playfield_dimensions (tuple[int, int]): The dimensions of the playfield. Methods: - __init__(self, starting_player: int, playfiled_dimensions: (int, int) = (3,3)): Initializes a new instance of the GameState class. set_player_position(self, player: int, new_position: (int, int)): Sets the position of a player on the playfield. set_winner(self, winner: int): Sets the winner of the game. - winner(self) -> int: Returns the number of the winning player. - finished(self) -> bool: Returns True if the game has finished, False otherwise. - playfield(self) -> list[list[int]]: Returns the playfield of the game. - playfield_value(self, position: (int, int)) -> int: Returns the value at the specified position on the playfield. + playfield_value(self, position: (int, int)): Returns the value at the specified position on the playfield. """ _playfield: list[list[int]] diff --git a/Server/player.py b/Server/player.py index cdb2399..90c9fe4 100644 --- a/Server/player.py +++ b/Server/player.py @@ -11,7 +11,12 @@ class Player: uuid (UUID): The UUID of the player. display_name (str): The display name of the player. color (int): The color of the player. + color_str (str): The color of the player as a html string. ready (bool): Whether the player is ready to start the game. + + Class Methods: + from_dict(player_dict: dict) -> Player: Create a Player object from a dictionary. + with_color_str(display_name: str, color_str: str, uuid: UUID = None, ready: bool = False) -> Player: Create a Player object with an html string as color instead of an int. """ def __init__(self, display_name: str, color: int, uuid: UUID = None, ready:bool = False): self.uuid: UUID = uuid if uuid else uuid4() diff --git a/Server/rulebase.py b/Server/rulebase.py index 1f49637..f11b21b 100644 --- a/Server/rulebase.py +++ b/Server/rulebase.py @@ -4,12 +4,16 @@ class RuleBase: """ Represents the rule base for a Tic-Tac-Toe game. - Args: + Parameters: playfield_dimensions (tuple[int, int]): The dimensions of the playfield (default: (3, 3)). Attributes: playfield_dimensions (tuple[int, int]): The dimensions of the playfield. + Functions: + is_move_valid(state: GameState, new_position: tuple[int, int]) -> bool: Check if a move is valid. + check_win(state: GameState) -> None: Check if there is a winner in the current game state. + is_game_state_valid(state: GameState) -> bool: Check if the current game state is valid. """ def __init__(self, playfield_dimensions: tuple[int, int] = (3,3)): diff --git a/Server/statistics.py b/Server/statistics.py index e96e12c..2c0da9a 100644 --- a/Server/statistics.py +++ b/Server/statistics.py @@ -9,27 +9,28 @@ class Statistics: """ Handle Statistics and Writing to permanent storage. - Methods: - get_statistics() -> list: returns all statistics - increment_emojis(player: Player, message: str) -> None: counts the emojis in the given message and updates the emoji statistics of a profile by its player object - increment_moves(player: Player) -> None: increments the moves of a profile by its player object - increment_games(player_list: list[Player], winner: int) -> None: increments the wins and losses of both players by their player objects - - Private Methods: - _increment_win(player: Player) -> None: increments the wins of a profile by its player object - _increment_loss(player: Player) -> None: increments the losses of a profile by its player object - _increment_draws(player: Player) -> None: increments the draws of a profile by its player object - _check_add_profile(player: Player) -> None: checks if a profile with the given uuid exists and adds it if it doesn't - _check_profile(uuid: str) -> bool: checks if a profile with the given uuid exists - _add_profile(player: Player) -> None: adds a new profile to the database + Parameters: + path (str): path to db file, default is './Data/statistics.db' + + Functions: + get_statistics() -> list: returns all statistics + increment_emojis(player: Player, message: str) -> None: counts the emojis in the given message and updates the emoji statistics of a profile by its player object + increment_moves(player: Player) -> None: increments the moves of a profile by its player object + increment_games(player_list: list[Player], winner: int) -> None: increments the wins and losses of both players by their player objects + + Private Functions: + _increment_win(player: Player) -> None: increments the wins of a profile by its player object + _increment_loss(player: Player) -> None: increments the losses of a profile by its player object + _increment_draws(player: Player) -> None: increments the draws of a profile by its player object + _check_add_profile(player: Player) -> None: checks if a profile with the given uuid exists and adds it if it doesn't + _check_profile(uuid: str) -> bool: checks if a profile with the given uuid exists + _add_profile(player: Player) -> None: adds a new profile to the database """ def __init__(self, path: str = os.path.abspath('Server/Data/statistics.db')) -> None: """ - Initializes the statistics object by creating a connection to the database - and creating the table if it doesn't exist - :param path: path to db file, default is './Data/statistics.db' + Initializes the statistics object by creating a connection to the database and creating the table if it doesn't exist """ self.path = path self.conn = sqlite3.connect(self.path) @@ -52,7 +53,9 @@ def __init__(self, path: str = os.path.abspath('Server/Data/statistics.db')) -> def get_statistics(self) -> list: """ Returns the statistics of all players - :return: all statistics + + Returns: + (list): all statistics """ with self.conn: self.cursor.execute(f""" @@ -63,10 +66,11 @@ def get_statistics(self) -> list: def increment_emojis(self, player: Player, message: str) -> None: """ - Counts the emojis in the given message and updates the emoji - statistics of a profile by its uuid - :param player: - :param message: message that is checked for emojis + Counts the emojis in the given message and updates the emoji statistics of a profile + + Parameters: + player (Player): player object of the profile that sent the message + message (str): message that is checked for emojis """ self._check_add_profile(player) @@ -81,8 +85,10 @@ def increment_emojis(self, player: Player, message: str) -> None: def increment_moves(self, player: Player) -> None: """ - Increments the moves of a profile by its uuid - :param player: + Increments the moves of a profile + + Parameters: + player (Player): player object of the profile that made the move """ self._check_add_profile(player) @@ -97,8 +103,10 @@ def increment_moves(self, player: Player) -> None: def increment_games(self, player_list: list[Player], winner: int) -> None: """ Increments the wins and losses of both players by their player objects - :param player_list: list of None, player1, player2 - :param winner: 0 if draw, 1 if player1 wins, 2 if player2 wins + + Parameters: + player_list (list[Player]): list of None, player1, player2 + winner (int): 0 if draw, 1 if player1 wins, 2 if player2 wins """ self._check_add_profile(player_list[1]) @@ -119,7 +127,9 @@ def increment_games(self, player_list: list[Player], winner: int) -> None: def _increment_win(self, player: Player) -> None: """ Increments the wins of a profile by its uuid - :param player: player object of the profile that is updated + + Parameters: + player (Player): player object of the profile that is updated """ self._check_add_profile(player) @@ -136,7 +146,9 @@ def _increment_win(self, player: Player) -> None: def _increment_loss(self, player: Player) -> None: """ Increments the losses of a profile by its uuid - :param player: player object of the profile that is updated + + Parameters: + player (Player): player object of the profile that is updated """ self._check_add_profile(player) @@ -153,7 +165,9 @@ def _increment_loss(self, player: Player) -> None: def _increment_draws(self, player: Player) -> None: """ Increments the draws of a profile by its uuid - :param player: player object of the profile that is updated + + Parameters: + player (Player): player object of the profile that is updated """ self._check_add_profile(player) @@ -171,8 +185,9 @@ def _increment_draws(self, player: Player) -> None: def _check_add_profile(self, player: Player) -> None: """ Checks if a profile with the given uuid exists and adds it if it doesn't - :param uuid: uuid of the profile that is checked - :return: True if the profile exists, False if it doesn't + + Parameters: + player (Player): player object of the profile that is checked """ if not self._check_profile(str(player.uuid)): self._add_profile(player) @@ -180,8 +195,9 @@ def _check_add_profile(self, player: Player) -> None: def _check_profile(self, uuid_str: str) -> bool: """ Checks if a profile with the given uuid exists - :param uuid: uuid of the profile that is checked - :return: True if the profile exists, False if it doesn't + + Parameters: + uuid_str (str): uuid of the profile that should be checked """ with self.conn: self.cursor.execute(f""" @@ -193,7 +209,9 @@ def _check_profile(self, uuid_str: str) -> bool: def _add_profile(self, player: Player) -> None: """ Adds a new profile to the database - :param player: player object of the profile that is added + + Parameters: + player (Player): player object of the profile that is added """ with self.conn: self.cursor.execute(f""" diff --git a/Server/websocket_server.py b/Server/websocket_server.py index 32394f1..301f14b 100644 --- a/Server/websocket_server.py +++ b/Server/websocket_server.py @@ -16,6 +16,22 @@ logger = logging.getLogger(__name__) class Lobby: + """ + This class represents an instance of the gameserver. + + Parameters: + admin (Player): The player object of the admin user. + port (int): The port the server is running on. Default is 8765. (This also shouldnt be changed, since many parts of the client depend on this port.) + + Functions: + handler(websocket) -> None: Handles incoming websocket messages from the client. + run() -> None: Runs the server async. + + Private Functions: + _end_game() -> None: Ends the game and sends the final playfield and winner to the clients. Then closes the server. + start_server() -> None: Starts the server. + """ + def __init__(self, admin:Player, port: int = 8765) -> None: self._players = {} self._admin = admin @@ -37,6 +53,9 @@ def __init__(self, admin:Player, port: int = 8765) -> None: async def handler(self, websocket): + """ + Handle all websocket messages and pass them to the appropriate game function. + """ self._connections.add(websocket) @@ -210,12 +229,12 @@ async def _end_game(self): await asyncio.sleep(1) exit() - async def start_server(self): + async def _start_server(self): async with websockets.serve(self.handler, host = "", port = self._port): await asyncio.Future() # run forever def run(self): - asyncio.run(self.start_server()) + asyncio.run(self._start_server()) if __name__ == "__main__": lobby = Lobby(port = 8765, admin = Player(uuid=uuid.UUID("c4f0eccd-a6a4-4662-999c-17669bc23d5e"), display_name="admin", color=0xffffff, ready=True))