From 678989b62cb7f6ecb252ea90a7b5f7b64ac825e3 Mon Sep 17 00:00:00 2001 From: Felix Kloss Date: Mon, 27 Jan 2025 15:02:44 +0100 Subject: [PATCH 1/7] Show list lengths in monitoring output --- comprl/src/comprl/server/__main__.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/comprl/src/comprl/server/__main__.py b/comprl/src/comprl/server/__main__.py index fd8f069..9412bd2 100644 --- a/comprl/src/comprl/server/__main__.py +++ b/comprl/src/comprl/server/__main__.py @@ -115,15 +115,18 @@ def plog(*args): plog(datetime.datetime.now().isoformat(sep=" ")) - plog("\nConnected players:") + n_connected = len(self.player_manager.connected_players) + plog(f"\nConnected players ({n_connected}):") for player in self.player_manager.connected_players.values(): plog(f"\t{player.username} [{player.id}]") - plog("\nGames:") + n_games = len(self.game_manager.games) + plog(f"\nGames ({n_games}):") for game in self.game_manager.games.values(): plog(f"\t{game.id} {tuple(str(pid) for pid in game.players)}") - plog("\nPlayers in queue:") + n_queue = len(self.matchmaking._queue) + plog(f"\nPlayers in queue ({n_queue}):") for entry in self.matchmaking._queue: plog( f"\t{entry.user.username} [{entry.player_id}]" From 79cc8e40c52d8b38032dfd4eb9576b1dda106546 Mon Sep 17 00:00:00 2001 From: Felix Kloss Date: Mon, 27 Jan 2025 15:02:44 +0100 Subject: [PATCH 2/7] Do not update player ratings if game ended with disconnect Do not update player ratings for games that ended due to a disconnect. Those games are incomplete, so they should not go into the rating. --- comprl/src/comprl/server/managers.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/comprl/src/comprl/server/managers.py b/comprl/src/comprl/server/managers.py index 4775d94..eb0570e 100644 --- a/comprl/src/comprl/server/managers.py +++ b/comprl/src/comprl/server/managers.py @@ -15,7 +15,7 @@ from comprl.shared.types import GameID, PlayerID from comprl.server.data import GameData, UserData from comprl.server.data.sql_backend import User -from comprl.server.data.interfaces import UserRole +from comprl.server.data.interfaces import UserRole, GameEndState from comprl.server.config import get_config @@ -557,15 +557,15 @@ def _rate_match_quality(self, player1: QueueEntry, player2: QueueEntry) -> float return match_quality def _end_game(self, game: IGame) -> None: - """ - Readds players to queue after game has ended. + """Update user ratings and re-add players to the queue. Args: - game (IGame): The game to be ended. + game: The game that is ending. """ # update elo values result = game.get_result() - if result is not None: + # if a player disconnected during the game, simply don't update the ratings + if result is not None and result.end_state is not GameEndState.DISCONNECTED: mu_p1, sigma_p1 = self.player_manager.get_matchmaking_parameters( result.user1_id ) From f9892b02a619250edbe91352f468e95b92a28125 Mon Sep 17 00:00:00 2001 From: Felix Kloss Date: Mon, 27 Jan 2025 15:02:44 +0100 Subject: [PATCH 3/7] Rename _update to update This method is intended to be called from outside, so should not be marked as private. --- comprl/src/comprl/server/__main__.py | 2 +- comprl/src/comprl/server/managers.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/comprl/src/comprl/server/__main__.py b/comprl/src/comprl/server/__main__.py index 9412bd2..5f1d25f 100644 --- a/comprl/src/comprl/server/__main__.py +++ b/comprl/src/comprl/server/__main__.py @@ -95,7 +95,7 @@ def on_remote_error(self, player: IPlayer, error: Exception): def on_update(self): """gets called every update cycle""" - self.matchmaking._update() + self.matchmaking.update() self._write_monitoring_data() def _write_monitoring_data(self): diff --git a/comprl/src/comprl/server/managers.py b/comprl/src/comprl/server/managers.py index eb0570e..59d8d8a 100644 --- a/comprl/src/comprl/server/managers.py +++ b/comprl/src/comprl/server/managers.py @@ -412,7 +412,7 @@ def remove(self, player_id: PlayerID) -> None: """ self._queue = [entry for entry in self._queue if (entry.player_id != player_id)] - def _update(self) -> None: + def update(self) -> None: self._match_quality_scores = {} self._search_for_matches() From b91c59fe39530bc6e6fe969894008f65c3e5fddf Mon Sep 17 00:00:00 2001 From: Felix Kloss Date: Mon, 27 Jan 2025 15:02:44 +0100 Subject: [PATCH 4/7] Add limit for number of parallel games The limit can be configured via the setting `max_parallel_games`. Matchmaking simply returns early if the limit is reached. Note that this results in missing or incomplete match quality scores when the limit is reached but I prefer this over a more complicated implementation (those scores are anyway only relevant when games can be started). --- comprl-hockey-game/config.toml | 6 ++++-- comprl/CHANGELOG.md | 2 ++ comprl/src/comprl/server/config.py | 2 ++ comprl/src/comprl/server/managers.py | 12 ++++++++++++ 4 files changed, 20 insertions(+), 2 deletions(-) diff --git a/comprl-hockey-game/config.toml b/comprl-hockey-game/config.toml index 5229acf..40badf0 100644 --- a/comprl-hockey-game/config.toml +++ b/comprl-hockey-game/config.toml @@ -1,7 +1,7 @@ [CompetitionServer] port = 65335 timeout = 10 - log_level = "DEBUG" + log_level = "INFO" game_path = "hockey_game.py" game_class = "HockeyGame" database_path = "hockey.db" @@ -9,5 +9,7 @@ match_quality_threshold = 0.8 percentage_min_players_waiting = 0.1 percental_time_bonus = 0.1 - registration_key = "secret" + max_parallel_games = 100 monitor_log_path = "/dev/shm/comprl_monitor" + + registration_key = "secret" diff --git a/comprl/CHANGELOG.md b/comprl/CHANGELOG.md index 1b186f8..0ae4111 100644 --- a/comprl/CHANGELOG.md +++ b/comprl/CHANGELOG.md @@ -25,6 +25,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 config file location instead of to the working directory. - Matchmaking now samples from all candidates with quality above the threshold instead of using the first in the list. +- Do not update player ratings for games with disconnect. +- Add option to limit number of games run in parallel. ## Removed - The `Agent.event` decorator has been removed. Instead of using it, create diff --git a/comprl/src/comprl/server/config.py b/comprl/src/comprl/server/config.py index ce77118..bd42d31 100644 --- a/comprl/src/comprl/server/config.py +++ b/comprl/src/comprl/server/config.py @@ -41,6 +41,8 @@ class Config: percentage_min_players_waiting: float = 0.1 #: (Minutes waiting * percentage) added as a time bonus for waiting players percental_time_bonus: float = 0.1 + #: Maximum number of games that can be played in parallel + max_parallel_games: int = 100 #: File to which monitoring information is written. Ideally use a in-memory file #: (e.g. in /dev/shm). diff --git a/comprl/src/comprl/server/managers.py b/comprl/src/comprl/server/managers.py index 59d8d8a..cbf95d8 100644 --- a/comprl/src/comprl/server/managers.py +++ b/comprl/src/comprl/server/managers.py @@ -341,6 +341,7 @@ def __init__( self._match_quality_threshold = config.match_quality_threshold self._percentage_min_players_waiting = config.percentage_min_players_waiting self._percental_time_bonus = config.percental_time_bonus + self._max_parallel_games = config.max_parallel_games # cache matchmaking scores self._match_quality_scores: dict[frozenset[str], float] = {} @@ -413,6 +414,7 @@ def remove(self, player_id: PlayerID) -> None: self._queue = [entry for entry in self._queue if (entry.player_id != player_id)] def update(self) -> None: + """Try to match players in the queue and start games.""" self._match_quality_scores = {} self._search_for_matches() @@ -462,6 +464,16 @@ def _search_for_matches(self) -> None: i = 0 while i < len(self._queue) - 1: + # stop early if the limit for parallel games is reached + num_games = len(self.game_manager.games) + if num_games >= self._max_parallel_games: + self._log.debug( + "Limit for parallel games is reached (running: %d, limit: %d).", + num_games, + self._max_parallel_games, + ) + return + player1 = self._queue[i] candidates = self._queue[i + 1 :] From 8e17e52da9dbd1ac69ddd3c708c0135fc46ecee6 Mon Sep 17 00:00:00 2001 From: Felix Kloss Date: Mon, 27 Jan 2025 15:02:44 +0100 Subject: [PATCH 5/7] Add update interval to hockey example config --- comprl-hockey-game/config.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/comprl-hockey-game/config.toml b/comprl-hockey-game/config.toml index 40badf0..813e86a 100644 --- a/comprl-hockey-game/config.toml +++ b/comprl-hockey-game/config.toml @@ -1,5 +1,6 @@ [CompetitionServer] port = 65335 + server_update_interval = 5 timeout = 10 log_level = "INFO" game_path = "hockey_game.py" From fdacf7e822e652ceee7562393ce2e194bbeeee3b Mon Sep 17 00:00:00 2001 From: Felix Kloss Date: Mon, 27 Jan 2025 15:02:44 +0100 Subject: [PATCH 6/7] Remove indentation from hockey config --- comprl-hockey-game/config.toml | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/comprl-hockey-game/config.toml b/comprl-hockey-game/config.toml index 813e86a..a1d460c 100644 --- a/comprl-hockey-game/config.toml +++ b/comprl-hockey-game/config.toml @@ -1,16 +1,16 @@ [CompetitionServer] - port = 65335 - server_update_interval = 5 - timeout = 10 - log_level = "INFO" - game_path = "hockey_game.py" - game_class = "HockeyGame" - database_path = "hockey.db" - data_dir = "/tmp" - match_quality_threshold = 0.8 - percentage_min_players_waiting = 0.1 - percental_time_bonus = 0.1 - max_parallel_games = 100 - monitor_log_path = "/dev/shm/comprl_monitor" +port = 65335 +server_update_interval = 5 +timeout = 10 +log_level = "INFO" +game_path = "hockey_game.py" +game_class = "HockeyGame" +database_path = "hockey.db" +data_dir = "/tmp" +match_quality_threshold = 0.8 +percentage_min_players_waiting = 0.1 +percental_time_bonus = 0.1 +max_parallel_games = 100 +monitor_log_path = "/dev/shm/comprl_monitor" - registration_key = "secret" +registration_key = "secret" From d72ac8bf9a280f8051922f8e3d45ff910a6f3f52 Mon Sep 17 00:00:00 2001 From: Felix Kloss Date: Mon, 27 Jan 2025 15:02:44 +0100 Subject: [PATCH 7/7] Bump version to 0.2.3-dev --- comprl/pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/comprl/pyproject.toml b/comprl/pyproject.toml index 3e02fae..f34e73f 100644 --- a/comprl/pyproject.toml +++ b/comprl/pyproject.toml @@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta" [project] name = "comprl" -version = "0.2.2-dev" +version = "0.2.3-dev" description = "Competition Server for Reinforcement Agents -- Teamprojekt WS 23/24" authors = [ {name = "Author Name", email = "optional@example.com"},