Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bots #108

Closed
wants to merge 3 commits into from
Closed

Bots #108

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
131 changes: 131 additions & 0 deletions comprl/server/bot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
"""
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()
17 changes: 17 additions & 0 deletions comprl/server/managers.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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:
Expand Down Expand Up @@ -285,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:
"""
Expand Down Expand Up @@ -358,6 +361,20 @@ 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() > 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)
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:
Expand Down
Loading