From 43a4e99bb06de031f36663988081bbcb0530b690 Mon Sep 17 00:00:00 2001 From: Paco Schatz Date: Tue, 12 Mar 2024 14:40:36 +0100 Subject: [PATCH 1/3] Added a bot class, and two hockey bots --- comprl/server/bot.py | 124 ++++++++++++++++++++++++++++++++++++++ comprl/server/managers.py | 14 +++++ 2 files changed, 138 insertions(+) create mode 100644 comprl/server/bot.py diff --git a/comprl/server/bot.py b/comprl/server/bot.py new file mode 100644 index 0000000..7af6ff0 --- /dev/null +++ b/comprl/server/bot.py @@ -0,0 +1,124 @@ +""" + this module contains the bot class and two bots for the hockey game +""" +import abc +import logging as log +import laserhockey.hockey_env as h_env + +from comprl.server.interfaces import IAction, IPlayer + +class bot(IPlayer): + """A bot player running locally on the server""" + + def __init__(self) -> None: + super().__init__() + # put the token for the bot here: + self.token = "" + #TODO: The bot needs to know its user_id + + def authenticate(self, result_callback): + """authenticates player + + Args: + result_callback (Callable): callback + """ + return result_callback(self.token) + + def is_ready(self, result_callback) -> bool: + """checks if the player is ready to play + + Returns: + bool: returns true if the player is ready to play + """ + return result_callback(True) + + def notify_start(self, game_id): + """notifies player that the game has started""" + return + + def get_action(self, obv, result_callback) -> IAction: + """gets an action from the player + + Args: + obv (Any): observation + result_callback (Callable): callback + + Returns: + IAction: action + """ + return result_callback(self._get_step(obv)) + + @abc.abstractmethod + def _get_step(self, obv): + """gets the step from the bot + + Args: + obv (_type_): observation + + Returns: + step list[float]: the step to be performed + """ + ... + + def notify_end(self, result, stats): + """notifies player that the game has ended""" + return + + def disconnect(self, reason: str): + """disconnect the player""" + log.debug(f"Bot was tried to disconnect for reason: {reason}") + return + + def notify_error(self, error: str): + """notifies the player of an error""" + log.debug(f"Bot received error: {error}") + return + + def notify_info(self, msg: str): + """notifies the player of an information""" + log.debug(f"Bot received info: {msg}") + return + +# TODO: maybe move these to examples + +class Weak_Hockey_Bot(bot): + """ The weak hockey bot """ + def __init__(self): + self.token = "weak_bot" + self.bot = h_env.BasicOpponent() # initialize agent + super().__init__() + # TODO: This ist not at all good. Simply put the user_id of the bot registered + # in the database here + self.user_id = 151 + + def _get_step(self, obv) -> IAction: + """gets the step from the bot + + Args: + obv (_type_): observation + + Returns: + step list[float]: the step to be performed + """ + return self.bot.act(obv).tolist() + +class Strong_Hockey_Bot(bot): + """ The strong hockey bot """ + def __init__(self): + self.token = "strong_bot" + self.bot = h_env.BasicOpponent(weak=False) # initialize agent + super().__init__() + # TODO: This ist not at all good. Simply put the user_id of the bot registered + # in the database here + self.user_id = 152 + + def _get_step(self, obv) -> IAction: + """gets the step from the bot + + Args: + obv (_type_): observation + + Returns: + step list[float]: the step to be performed + """ + return self.bot.act(obv).tolist() \ No newline at end of file diff --git a/comprl/server/managers.py b/comprl/server/managers.py index 151fef3..308bd55 100644 --- a/comprl/server/managers.py +++ b/comprl/server/managers.py @@ -4,6 +4,7 @@ import logging as log import pickle +import random from typing import Type from datetime import datetime from openskill.models import PlackettLuce @@ -14,6 +15,7 @@ from comprl.shared.types import GameID, PlayerID from comprl.server.data import GameData, UserData from comprl.server.util import ConfigProvider +from comprl.server.bot import Weak_Hockey_Bot, Strong_Hockey_Bot class GameManager: @@ -358,6 +360,18 @@ def _update(self, start_index: int = 0) -> None: # players are matched and removed from queue. continue searching self._update(i) return + player_id, _, _, _, time_stamp = self._queue[i] + if (datetime.now() - time_stamp).total_seconds() > 60 * 5: + # match players that have been waiting for more than 5 minutes with a + # bot + player = self.player_manager.get_player_by_id(player_id) + if player is None: + continue + log.debug(f"Player {player_id} was matched with a bot") + bot = random.choice([Weak_Hockey_Bot(), Strong_Hockey_Bot()]) + self.remove(player_id) + game = self.game_manager.start_game([player, bot]) + game.add_finish_callback(self._end_game) return def _min_players_waiting(self) -> int: From 6f53e488d396f0aab32712c33eb60610cb7a77bf Mon Sep 17 00:00:00 2001 From: Paco Schatz Date: Tue, 12 Mar 2024 14:44:27 +0100 Subject: [PATCH 2/3] formatting and a new threshold --- comprl/server/bot.py | 29 ++++++++++++++++++----------- comprl/server/managers.py | 7 ++++--- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/comprl/server/bot.py b/comprl/server/bot.py index 7af6ff0..0a457d8 100644 --- a/comprl/server/bot.py +++ b/comprl/server/bot.py @@ -1,20 +1,22 @@ """ this module contains the bot class and two bots for the hockey game """ + import abc import logging as log import laserhockey.hockey_env as h_env from comprl.server.interfaces import IAction, IPlayer + class bot(IPlayer): """A bot player running locally on the server""" - + def __init__(self) -> None: super().__init__() # put the token for the bot here: self.token = "" - #TODO: The bot needs to know its user_id + # TODO: The bot needs to know its user_id def authenticate(self, result_callback): """authenticates player @@ -47,7 +49,7 @@ def get_action(self, obv, result_callback) -> IAction: IAction: action """ return result_callback(self._get_step(obv)) - + @abc.abstractmethod def _get_step(self, obv): """gets the step from the bot @@ -78,16 +80,19 @@ def notify_info(self, msg: str): """notifies the player of an information""" log.debug(f"Bot received info: {msg}") return - + + # TODO: maybe move these to examples + class Weak_Hockey_Bot(bot): - """ The weak hockey bot """ + """The weak hockey bot""" + def __init__(self): self.token = "weak_bot" self.bot = h_env.BasicOpponent() # initialize agent super().__init__() - # TODO: This ist not at all good. Simply put the user_id of the bot registered + # TODO: This ist not at all good. Simply put the user_id of the bot registered # in the database here self.user_id = 151 @@ -101,14 +106,16 @@ def _get_step(self, obv) -> IAction: step list[float]: the step to be performed """ return self.bot.act(obv).tolist() - + + class Strong_Hockey_Bot(bot): - """ The strong hockey bot """ + """The strong hockey bot""" + def __init__(self): - self.token = "strong_bot" + self.token = "strong_bot" self.bot = h_env.BasicOpponent(weak=False) # initialize agent super().__init__() - # TODO: This ist not at all good. Simply put the user_id of the bot registered + # TODO: This ist not at all good. Simply put the user_id of the bot registered # in the database here self.user_id = 152 @@ -121,4 +128,4 @@ def _get_step(self, obv) -> IAction: Returns: step list[float]: the step to be performed """ - return self.bot.act(obv).tolist() \ No newline at end of file + return self.bot.act(obv).tolist() diff --git a/comprl/server/managers.py b/comprl/server/managers.py index 308bd55..b134d76 100644 --- a/comprl/server/managers.py +++ b/comprl/server/managers.py @@ -287,6 +287,7 @@ def __init__( self.model = PlackettLuce() self._MATCH_QUALITY_THRESHOLD = 0.8 self._PERCENTAGE_MIN_PLAYERS_WAITING = 0.1 + self.BOT_WAITING_THRESHOLD = 60 * 5 # 5 minutes def try_match(self, player_id: PlayerID) -> None: """ @@ -361,10 +362,10 @@ def _update(self, start_index: int = 0) -> None: self._update(i) return player_id, _, _, _, time_stamp = self._queue[i] - if (datetime.now() - time_stamp).total_seconds() > 60 * 5: - # match players that have been waiting for more than 5 minutes with a + if (datetime.now() - time_stamp).total_seconds() > self.BOT_WAITING_THRESHOLD: + # match players that have been waiting for more than 5 minutes with a # bot - player = self.player_manager.get_player_by_id(player_id) + player = self.player_manager.get_player_by_id(player_id) if player is None: continue log.debug(f"Player {player_id} was matched with a bot") From a82da42789aa47533bdb476cd1f7793b7cbde62d Mon Sep 17 00:00:00 2001 From: Paco Schatz Date: Tue, 12 Mar 2024 14:46:52 +0100 Subject: [PATCH 3/3] formatting --- comprl/server/managers.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/comprl/server/managers.py b/comprl/server/managers.py index b134d76..7ddecd8 100644 --- a/comprl/server/managers.py +++ b/comprl/server/managers.py @@ -287,7 +287,7 @@ def __init__( self.model = PlackettLuce() self._MATCH_QUALITY_THRESHOLD = 0.8 self._PERCENTAGE_MIN_PLAYERS_WAITING = 0.1 - self.BOT_WAITING_THRESHOLD = 60 * 5 # 5 minutes + self.BOT_WAITING_THRESHOLD = 60 * 5 # 5 minutes def try_match(self, player_id: PlayerID) -> None: """ @@ -362,7 +362,9 @@ def _update(self, start_index: int = 0) -> None: self._update(i) return player_id, _, _, _, time_stamp = self._queue[i] - if (datetime.now() - time_stamp).total_seconds() > self.BOT_WAITING_THRESHOLD: + if ( + datetime.now() - time_stamp + ).total_seconds() > self.BOT_WAITING_THRESHOLD: # match players that have been waiting for more than 5 minutes with a # bot player = self.player_manager.get_player_by_id(player_id)