diff --git a/README.md b/README.md index aebe558..4bfe600 100644 --- a/README.md +++ b/README.md @@ -3,11 +3,11 @@ # Python Client for the Software-Challenge Germany 2023 [![GitHub Workflow Status](https://img.shields.io/github/workflow/status/FalconsSky/Software-Challenge-Python-Client/static%20and%20unit%20tests?label=Test)](https://github.com/FalconsSky/Software-Challenge-Python-Client) -[![Read the Docs](https://img.shields.io/readthedocs/software-challenge-python-client?label=Docs)](https://software-challenge-python-client.readthedocs.io/en/master) +[![Read the Docs](https://img.shields.io/readthedocs/software-challenge-python-client?label=Docs)](https://software-challenge-python-client.readthedocs.io/en/latest/) [![PyPI](https://img.shields.io/pypi/v/socha?label=PyPi)](https://pypi.org/project/socha/) +[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/socha?label=Python)](https://pypi.org/project/socha/) [![Discord](https://img.shields.io/discord/233577109363097601?color=blue&label=Discord)](https://discord.gg/ARZamDptG5) [![Documentation](https://img.shields.io/badge/Software--Challenge%20-Documentation-%234299e1)](https://docs.software-challenge.de/) -[![Website](https://img.shields.io/badge/Software--Challenge-Website-%23D9994F)](https://software-challenge.de/) > Please note that this is a very early version, which may still contain some bugs. However, the client is able to play > a game from start to end. @@ -29,10 +29,10 @@ Therefore, the possibility of a virtual environment is presented, which installs the packages inside the folder. -> Pleas make sure that you have at least **Python 3.10** installed. +> Pleas make sure that you have at least **Python 3.6** installed. > Check with `$ python -V` or `$ python3 -V`. -> - Windows: `> winget install -e --id Python.Python.3.10` -> - Debian: `$ sudo apt install python3.10` +> - Windows: `> winget install -e --id Python.Python.3.6` +> - Debian: `$ sudo apt install python3.6` > - Arch: `$ sudo pacman -S python` ### Globally @@ -181,4 +181,4 @@ my_player/ The `my_player` directory, or whatever you named yours, then just needs to be packaged as a ZIP archive -and your player is ready to be uploaded. πŸ₯³πŸŽ‰ \ No newline at end of file +and your player is ready to be uploaded. πŸ₯³πŸŽ‰ diff --git a/docs/index.rst b/docs/index.rst index a25056c..89ab2d7 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,8 +1,8 @@ Python Client for the Software-Challenge Germany 2023 ===================================================== -|GitHub Workflow Status| |Read the Docs| |PyPI| |Discord| -|Documentation| |Website| +|GitHub Workflow Status| |Read the Docs| |PyPI| |PyPI - Python Version| +|Discord| |Documentation| Please note that this is a very early version, which may still contain some bugs. However, the client is able to play a game from @@ -27,11 +27,11 @@ since there is no Internet connection that allows you to download packages. Therefore, the possibility of a virtual environment is presented, which installs the packages inside the folder. - Pleas make sure that you have at least **Python 3.10** installed. + Pleas make sure that you have at least **Python 3.6** installed. Check with ``$ python -V`` or ``$ python3 -V``. - - Windows: ``> winget install -e --id Python.Python.3.10`` - - Debian: ``$ sudo apt install python3.10`` + - Windows: ``> winget install -e --id Python.Python.3.6`` + - Debian: ``$ sudo apt install python3.6`` - Arch: ``$ sudo pacman -S python`` Globally @@ -210,15 +210,15 @@ uploaded. πŸ₯³πŸŽ‰ .. |GitHub Workflow Status| image:: https://img.shields.io/github/workflow/status/FalconsSky/Software-Challenge-Python-Client/static%20and%20unit%20tests?label=Test :target: https://github.com/FalconsSky/Software-Challenge-Python-Client .. |Read the Docs| image:: https://img.shields.io/readthedocs/software-challenge-python-client?label=Docs - :target: https://software-challenge-python-client.readthedocs.io/en/master + :target: https://software-challenge-python-client.readthedocs.io/en/latest/ .. |PyPI| image:: https://img.shields.io/pypi/v/socha?label=PyPi :target: https://pypi.org/project/socha/ +.. |PyPI - Python Version| image:: https://img.shields.io/pypi/pyversions/socha?label=Python + :target: https://pypi.org/project/socha/ .. |Discord| image:: https://img.shields.io/discord/233577109363097601?color=blue&label=Discord :target: https://discord.gg/ARZamDptG5 .. |Documentation| image:: https://img.shields.io/badge/Software--Challenge%20-Documentation-%234299e1 :target: https://docs.software-challenge.de/ -.. |Website| image:: https://img.shields.io/badge/Software--Challenge-Website-%23D9994F - :target: https://software-challenge.de/ Indices and tables diff --git a/docs/requirements.txt b/docs/requirements.txt index 1d833fb..4ac0df1 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,4 +1,4 @@ -sphinx==5.1.1 -sphinx-autodoc-typehints==1.19.2 -furo==2022.6.21 -xsdata==22.8 \ No newline at end of file +sphinx==4.0 +sphinx-autodoc-typehints==1.12.0 +furo==2022.4.7 +xsdata==22.7 \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index be60c16..fb4e16f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,18 +4,22 @@ build-backend = "hatchling.build" [project] name = "socha" -version = "0.9.4" +version = "0.9.5" authors = [ { name = "FalconsSky", email = "stu222782@mail.uni-kiel.de" }, ] description = "This is the package for the Software-Challenge Germany 2023. This Season the game will be 'Hey, danke fΓΌr den Fisch' a.k.a. 'Penguins' in short." readme = "README.md" -requires-python = ">=3.10" +requires-python = ">=3.6" dependencies = [ - "xsdata~=22.8" + "xsdata==22.7" ] classifiers = [ "Programming Language :: Python", + "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)", "Operating System :: OS Independent", diff --git a/readthedocs.yaml b/readthedocs.yaml index 88ecca5..26c33e1 100644 --- a/readthedocs.yaml +++ b/readthedocs.yaml @@ -9,7 +9,7 @@ version: 2 build: os: ubuntu-20.04 tools: - python: "3.10" + python: "3.6" python: install: diff --git a/requirements.txt b/requirements.txt index d745fba..ba0bab6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1 @@ -xsdata~=22.8 +xsdata==22.7 diff --git a/setup.py b/setup.py index 2238f63..03de651 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ setup( name='socha', - version='0.9.4', + version='0.9.5', packages=['socha', 'socha.api', 'socha.api.plugin', 'socha.api.protocol', 'socha.api.networking'], url='https://github.com/FalconsSky/Software-Challenge-Python-Client', diff --git a/socha/api/networking/_network_interface.py b/socha/api/networking/_network_interface.py index 07fb422..a0b0192 100644 --- a/socha/api/networking/_network_interface.py +++ b/socha/api/networking/_network_interface.py @@ -4,6 +4,7 @@ import logging import re import socket +from typing import Union class _NetworkInterface: @@ -50,7 +51,7 @@ def send(self, data: bytes): self.socket.sendall(data) logging.debug("Sent data: %s", data.decode("utf-8")) - def receive_socket_data(self) -> bytes | None: + def receive_socket_data(self) -> Union[bytes, None]: """ Receives the raw tcp socket packages. :return: A package in bytes, None if there where no packages. diff --git a/socha/api/networking/_xflux.py b/socha/api/networking/_xflux.py index dc68ab4..d78648e 100644 --- a/socha/api/networking/_xflux.py +++ b/socha/api/networking/_xflux.py @@ -106,7 +106,7 @@ def _client_loop(self): """ The client loop. This is the main loop, - where the client waites for messages from the server + where the client waits for messages from the server and handles them accordingly. """ while self.running: diff --git a/socha/api/networking/player_client.py b/socha/api/networking/player_client.py index ce5cfcf..7644d26 100644 --- a/socha/api/networking/player_client.py +++ b/socha/api/networking/player_client.py @@ -3,6 +3,7 @@ """ import logging import time +from typing import List, Union from socha.api.networking._xflux import _XFluxClient from socha.api.plugin import penguins @@ -16,9 +17,9 @@ def _convertBoard(protocolBoard: Board) -> penguins.Board: Converts a protocol Board to a usable gam board for using in the logic. :rtype: object """ - boardList: list[list[Field]] = [] + boardList: List[List[Field]] = [] for y, row in enumerate(protocolBoard.list_value): - rowList: list[Field] = [] + rowList: List[Field] = [] for x, fieldsValue in enumerate(row.field_value): fieldCoordinate = Coordinate(x, y, is_double=False).get_double_hex() rowList.append(Field(coordinate=fieldCoordinate, field=fieldsValue)) @@ -27,7 +28,7 @@ def _convertBoard(protocolBoard: Board) -> penguins.Board: class IClientHandler: - history: list[GameState | Error | Result] = [] + history: List[Union[GameState, Error, Result]] = [] def calculate_move(self) -> Move: """ diff --git a/socha/api/plugin/penguins.py b/socha/api/plugin/penguins.py index 7eb9b97..ba652a6 100644 --- a/socha/api/plugin/penguins.py +++ b/socha/api/plugin/penguins.py @@ -3,13 +3,14 @@ """ import logging import math +from typing import List, Union _hexagonTemplate = [ " _______ \xA0", - " / \ \xA0", + " / \\ \xA0", "/XXXXXXXXX\\\xA0", - "\YYYYYYYYY/\xA0", - " \_______/ \xA0" + "\\YYYYYYYYY/\xA0", + " \\_______/ \xA0" ] _emptyHexagonPlaceholder = "\xA0\xA0\xA0\xA0\xA0\xA0" @@ -110,7 +111,7 @@ def are_equal(self, other: 'Vector'): return self.magnitude() == other.magnitude() and self.get_arc_tangent() == other.get_arc_tangent() @property - def directions(self) -> list['Vector']: + def directions(self) -> List['Vector']: """ Gets the six neighbors of the vector. @@ -207,7 +208,7 @@ def get_vector(self) -> Vector: """ return Vector(self.x, self.y) - def get_hex_neighbors(self) -> list[Vector]: + def get_hex_neighbors(self) -> List[Vector]: """ Gets the six neighbors of the coordinate. @@ -352,7 +353,7 @@ class Field: Represents a field in the game. """ - def __init__(self, coordinate: Coordinate, field: int | str | Team): + def __init__(self, coordinate: Coordinate, field: Union[int, str, Team]): """ The Field represents a field on the game board. It says what state itself it has and where it is on the board. @@ -361,7 +362,7 @@ def __init__(self, coordinate: Coordinate, field: int | str | Team): :param field: The state of the field. Can be either the number of fishes, or a Team. """ self.coordinate = coordinate - self.field: int | str | Team + self.field: Union[int, str, Team] if isinstance(field, int): self.field = field elif field.isalpha(): @@ -381,13 +382,13 @@ def is_occupied(self) -> bool: """ return isinstance(self.field, Team) - def get_fish(self) -> None | int: + def get_fish(self) -> Union[None, int]: """ :return: The amount of fish on the field, None if the field is occupied. """ return None if self.is_occupied() else self.field - def get_team(self) -> Team | None: + def get_team(self) -> Union[Team, None]: """ :return: The team of the field if it is occupied by penguin, None otherwise. """ @@ -406,7 +407,7 @@ class Board: Class which represents a game board. Consisting of a two-dimensional array of fields. """ - def __init__(self, game_field: list[list[Field]]): + def __init__(self, game_field: List[List[Field]]): """ The Board shows the state where each field is, how many fish and which team is on each field. @@ -414,11 +415,11 @@ def __init__(self, game_field: list[list[Field]]): """ self._game_field = game_field - def get_empty_fields(self) -> list[Field]: + def get_empty_fields(self) -> List[Field]: """ :return: A list of all empty fields. """ - fields: list[Field] = [] + fields: List[Field] = [] for row in self._game_field: for field in row: if field.is_empty(): @@ -479,7 +480,7 @@ def get_field(self, position: Coordinate) -> Field: raise IndexError(f"Index out of range: [x={array_coordinates.x}, y={array_coordinates.y}]") - def get_field_or_none(self, position: Coordinate) -> Field | None: + def get_field_or_none(self, position: Coordinate) -> Union[Field, None]: """ Gets the field at the given position no matter if it is valid or not. @@ -507,7 +508,7 @@ def get_field_by_index(self, index: int) -> Field: y = index % self.width() return self.get_field(Coordinate(x, y, False)) - def get_all_fields(self) -> list[Field]: + def get_all_fields(self) -> List[Field]: """ Gets all Fields of the board. @@ -515,7 +516,7 @@ def get_all_fields(self) -> list[Field]: """ return [self.get_field_by_index(i) for i in range(self.width() * self.height())] - def compare_to(self, other: 'Board') -> list[Field]: + def compare_to(self, other: 'Board') -> List[Field]: """ Compares two boards and returns a list of the Fields that are different. @@ -541,7 +542,7 @@ def contains(self, field: Field) -> bool: return True return False - def contains_all(self, fields: list[Field]) -> bool: + def contains_all(self, fields: List[Field]) -> bool: """ Checks if the board contains all the given fields. @@ -553,7 +554,7 @@ def contains_all(self, fields: list[Field]) -> bool: return False return True - def get_moves_in_direction(self, origin: Coordinate, direction: Vector) -> list[Move]: + def get_moves_in_direction(self, origin: Coordinate, direction: Vector) -> List[Move]: """ Gets all moves in the given direction from the given origin. @@ -574,7 +575,7 @@ def _is_destination_valid(self, field: Coordinate) -> bool: return self.is_valid(field) and not self.is_occupied(field) and not \ self.get_field(field).is_empty() - def possible_moves_from(self, position: Coordinate) -> list[Move]: + def possible_moves_from(self, position: Coordinate) -> List[Move]: """ Returns a list of all possible moves from the given position. That are all moves in all hexagonal directions. @@ -589,7 +590,7 @@ def possible_moves_from(self, position: Coordinate) -> list[Move]: moves.extend(self.get_moves_in_direction(position, direction)) return moves - def get_penguins(self) -> list[Field]: + def get_penguins(self) -> List[Field]: """ Searches the board for all penguins. @@ -597,7 +598,7 @@ def get_penguins(self) -> list[Field]: """ return [field for field in self.get_all_fields() if field.is_occupied()] - def get_teams_penguins(self, team: Team) -> list[Coordinate]: + def get_teams_penguins(self, team: Team) -> List[Coordinate]: """ Searches the board for all penguins of the given team. @@ -613,20 +614,20 @@ def get_teams_penguins(self, team: Team) -> list[Coordinate]: teams_penguins.append(coordinates) return teams_penguins - def get_most_fish(self) -> list[Field]: + def get_most_fish(self) -> List[Field]: """ Returns a list of all fields with the most fish. :return: A list of Fields. """ fields = self.get_all_fields() - fields.sort(key=lambda field: field.get_fish(), reverse=True) + fields.sort(key=lambda field_x: field_x.get_fish(), reverse=True) for i, field in enumerate(fields): if field.get_fish() < fields[0].get_fish(): fields = fields[:i] return fields - def get_board_intersection(self, other: 'Board') -> list[Field]: + def get_board_intersection(self, other: 'Board') -> List[Field]: """ Returns a list of all fields that are in both boards. @@ -635,7 +636,7 @@ def get_board_intersection(self, other: 'Board') -> list[Field]: """ return [field for field in self.get_all_fields() if field in other.get_all_fields()] - def get_fields_intersection(self, other: list[Field]) -> list[Field]: + def get_fields_intersection(self, other: List[Field]) -> List[Field]: """ Returns a list of all fields that are in both list of Fields. @@ -673,10 +674,10 @@ def pretty_print(self): Prints the board in a pretty way. """ result = "" - for i, list in enumerate(self._game_field): + for i, column in enumerate(self._game_field): for row in _hexagonTemplate: result += _emptyHexagonPlaceholder if i % 2 != 0 else "" - for field in list: + for field in column: if field.is_empty(): hexagon = " " * len(row) elif "XXXXXXXXX" in row: @@ -754,7 +755,7 @@ def __init__(self, board: Board, turn: int, start_team: Team, fishes: Fishes, la self.current_pieces = self.board.get_teams_penguins(self.current_team) self.possible_moves = self._get_possible_moves(self.current_team) - def _get_possible_moves(self, current_team: Team = None) -> list[Move]: + def _get_possible_moves(self, current_team: Team = None) -> List[Move]: """ Gets all possible moves for the current team. That includes all possible moves from all Fields that are not occupied by a penguin from that team. @@ -775,14 +776,14 @@ def _get_possible_moves(self, current_team: Team = None) -> list[Move]: moves.extend(self.board.possible_moves_from(piece)) return moves - def get_most_fish_moves(self) -> list[Move]: + def get_most_fish_moves(self) -> List[Move]: """ Returns a list of all Moves that will get the most fish from possible moves. :return: A list of Moves. """ moves = self.possible_moves - moves.sort(key=lambda move: self.board.get_field(move.to_value).get_fish(), reverse=True) + moves.sort(key=lambda move_x: self.board.get_field(move_x.to_value).get_fish(), reverse=True) for i, move in enumerate(moves): first_fish = self.board.get_field(moves[0].to_value).get_fish() current_fish = self.board.get_field(move.to_value).get_fish() diff --git a/socha/api/protocol/protocol.py b/socha/api/protocol/protocol.py index e1927e2..e151eb8 100644 --- a/socha/api/protocol/protocol.py +++ b/socha/api/protocol/protocol.py @@ -566,7 +566,7 @@ class Meta: @dataclass class Scores: """ - Then endresult of a game when its over. + Then result of a game when its over. """ class Meta: @@ -666,7 +666,7 @@ class Meta: class Data: """ This element is sent by the server to the client to notify the client of a changing state of the game. - It can contain a move, gamestate, or winnig team with the reason. + It can contain a move, game state, or winning team with the reason. """ class Meta: