Skip to content

Commit

Permalink
Merge pull request #26 from TINF21CS1/serverdocs
Browse files Browse the repository at this point in the history
Serverdocs
  • Loading branch information
Petzys authored Mar 20, 2024
2 parents 26b98b9 + 27dd770 commit da011bd
Show file tree
Hide file tree
Showing 6 changed files with 110 additions and 55 deletions.
28 changes: 18 additions & 10 deletions Server/game.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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.
Expand All @@ -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:
"""
Expand Down
19 changes: 10 additions & 9 deletions Server/gamestate.py
Original file line number Diff line number Diff line change
Expand Up @@ -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]]
Expand Down
5 changes: 5 additions & 0 deletions Server/player.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
6 changes: 5 additions & 1 deletion Server/rulebase.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)):
Expand Down
84 changes: 51 additions & 33 deletions Server/statistics.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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"""
Expand All @@ -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)
Expand All @@ -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)
Expand All @@ -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])
Expand All @@ -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)
Expand All @@ -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)
Expand All @@ -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)
Expand All @@ -171,17 +185,19 @@ 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)

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"""
Expand All @@ -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"""
Expand Down
23 changes: 21 additions & 2 deletions Server/websocket_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)

Expand Down Expand Up @@ -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))
Expand Down

0 comments on commit da011bd

Please sign in to comment.