def setUp(self): self.com = MockCom(self) self.game = Game(self.com, 3, 4, 30, 30, True) self.game.dealer = MockDealer(self) self.game.setup_new_game() self.game.dealer = MockDealer(self) self.dealer = self.game.dealer
def runWebServer(host, port, quiet): """ Install the logger and run the webserver """ # add a logger wrapper for bottle (in order to log its activity) # See http://stackoverflow.com/questions/31080214/python-bottle-always-logs-to-console-no-logging-to-file def log_to_logger(fn): """ Wrap a Bottle request so that a log line is emitted after it's handled.""" @wraps(fn) def _log_to_logger(*_args, **_kwargs): actual_response = fn(*_args, **_kwargs) weblogger.info('%s %s %s %s' % (request.remote_addr, request.method, request.url, response.status)) return actual_response return _log_to_logger # update the template paths so that in priority, # it first looks in <gameName>/server/templates/ and then in CGS/server/templates TEMPLATE_PATH.append('games/' + Game.getTheGameName() + '/server/templates/') TEMPLATE_PATH.reverse() # add the base url to all the templates #Jinja2Template.defaults['base_url'] = 'http://%s:%s/' % (host, port) Jinja2Template.defaults['base_url'] = '/' # add the game name to all the templates (for page title) Jinja2Template.defaults['GameName'] = Game.getTheGameName() # Start the web server install(log_to_logger) weblogger.message('Run the web server on port %d...', port) default_app( ).catchall = True # all the exceptions/errors are catched, and re-routed to error500 run(host=host, port=port, quiet=quiet, server='gevent', handler_class=WebSocketHandler)
def create_new_game(): """ Receive the form to create a new game -> create the game (ie runPhase it) """ # get Players player1 = RegularPlayer.getFromName(request.forms.get('player1')) player2 = RegularPlayer.getFromName(request.forms.get('player2')) # !TODO: add some options (timeout, seed, etc.) in the html, and send them to the constructor try: # the constructor will check if player1 and player2 are available to play # no need to store the game object created here Game.getTheGameClass()(player1, player2) except ValueError as e: # !TODO: redirect to an error page # TODO: log this return 'Error. Impossible to create a game with ' + str( request.forms.get('player1')) + ' and ' + str( request.forms.get('player2')) + ': "' + str(e) + '"' else: redirect('/')
def game(gameName): """Returns the webpage of a game <gameName> is the name of the game If the name is not valid, the answer with the noObject page """ g = Game.getFromName(gameName) if g: try: displayName = g.getCutename() except NotImplementedError: displayName = gameName return template('game/Game.html', host=Config.host, webPort=Config.webPort, gameName=gameName, displayName=displayName, player1=g.players[0].HTMLrepr(), player2=g.players[1].HTMLrepr()) else: return template('noObject.html', className='game', objectName=gameName)
def new_tournament(): """ Page to create a new tournament Build from HTMLFormDict class method of TournamentMode (build from all the tournament modes) """ return Tournament.HTMLFormDict(Game.getTheGameName())
args = docopt(usage) if (not args['--debug']) and (not args['--dev']) and (not args['--prod']): args['--prod'] = True args['--port'] = int(args['--port']) args['--web'] = int(args['--web']) gameName = args['<gameName>'] # import the <gameName> module and store it (in Game) try: mod = import_module('games.' + gameName + '.server.' + gameName) if gameName not in mod.__dict__: print(Fore.RED + "Error: The file `games/" + gameName + "/server/" + gameName + ".py` must contain a class named `" + gameName + "`." + Fore.RESET) quit() Game.setTheGameClass(mod.__dict__[gameName]) except ImportError as e: print(Fore.RED + "Error: Impossible to import the file `games/" + gameName + "/server/" + gameName + ".py`." + Fore.RESET) print(e) quit() # configure the loggers logger = configureRootLogger(args) # Start ! mode = 'prod' if args['--prod'] else 'dev' if args['--dev'] else 'debug' logger.message("") logger.message("#=====================================================#") logger.message("# Coding Game Server is going to start (mode=`%s`) #" % mode)
def waitForGame(self): """ Waits for a message "WAIT_GAME %s" and then wait for a game (with an Event) %s is a string like (options is like "key1=value1 key2=value2 ...") - "{options}": regular game (with options) - "TOURNAMENT NAME {options}": tournament - or "NAME {options}": play agains training player Returns nothing """ # !TODO: normalize the options: should be "[options]", "TRAINING <name> [options]" or "TOURNAMENT <name> [options]" # get the WAIT_GAME message data = self.receiveData() if not data.startswith("WAIT_GAME"): self.sendData("Bad protocol, should send 'WAIT_GAME %s' command") raise ProtocolError( "Bad protocol, should send 'WAIT_GAME %s' command") # parse the game type (in the form "TOURNAMENT NAME key1=value1..." or "NAME key1=value1 key2=value2") trainingPlayerName = "" tournamentName = "" options = {} try: terms = shlex.split(data[10:]) if terms: if "=" in terms[0]: trainingPlayerName = "" tournamentName = "" options = dict([token.split('=') for token in terms]) elif terms[0] == 'TOURNAMENT': trainingPlayerName = "" tournamentName = terms[1] options = dict([token.split('=') for token in terms[2:]]) else: trainingPlayerName = terms[0] tournamentName = "" options = dict([token.split('=') for token in terms[1:]]) except ValueError: strerr = "The string sent with 'WAIT_GAME' is not valid (should be '{options}'," \ " 'NAME {options}' or 'TOURNAMENT NAME {options}', but is '%s' instead)" self.sendData(strerr) raise ProtocolError(strerr) if trainingPlayerName: # Create a particular Game try: # create game (no need to store it in a variable) g = Game.getTheGameClass().gameFactory(trainingPlayerName, self._player, options) except ValueError as err: self.sendData( "The training player sent by '%s' command is not valid (%s)" % (data, err)) raise ProtocolError( "The training player sent by '%s' command is not valid (%s)" % (data, err)) # log it self.logger.debug( "The game %s starts with training player `%s` and options=%s" % (g.name, trainingPlayerName, options)) elif tournamentName: try: # register the player in the tournament Tournament.registerPlayer(self._player, tournamentName) except ValueError as err: self.sendData("The tournament '%s' cannot be joined: %s" % (tournamentName, err)) raise ProtocolError( "The tournament '%s' cannot be joined: %s" % (tournamentName, err)) # just send back OK self.sendData("OK") # wait for the Game # and send every second a "NOT_READY" if the game do not start # in order to know if the client has disconnected gameHasStarted = self._player.waitForGame(5) while not gameHasStarted: self.sendData("NOT_READY") gameHasStarted = self._player.waitForGame(3) # now send the game name self.sendData(self.game.name) # now send the game sizes self.sendData(self.game.getDataSize())
def runPhase(self, **kwargs): """Launch a phase of the tournament """ # check if a phase is not already running if self.isPhaseRunning: # do noting, since a phase is already running return # first launch ? if not self.hasBegan: # we first need to get the list of 2-tuple (player1's name, player2's name) # of players who will play together in the phase try: phase, self._matches = next(self._matchGen) except StopIteration: self.endTournament() else: self.newPhase(phase) else: # otherwise, start a new phase self.newPhase() # build the dictionary of the games (pair of players' name -> list of score (tuple) and current game # (we remove fake players with "" as name) self._games = {(pName1, pName2): [[0, 0], None] for pName1, pName2 in self._matches if pName1 and pName2} # run the games for self._round in range(1, 2 * self.nbRounds4Victory + 1): for (pName1, pName2), (score, _) in self._games.items(): if max(score) < self.nbRounds4Victory: # choose who starts (-1 for random, ie for the last round) start = ( self._round - 1 ) % 2 if self._round < 2 * self.nbRounds4Victory else -1 # run the game only if the two players are here (otherwise, one wins directly) player1, player2 = self._players[pName1], self._players[ pName2] if player1 and player2: self._games[(pName1, pName2)][1] = Game.getTheGameClass()( player1, player2, start=start, tournament=self, **kwargs) self.logger.info("The game `%s` vs `%s` starts", pName1, pName2) self._queue.put_nowait(None) else: # one player is not playing anymore (disconnected), so the other wins score = self._games[(pName1, pName2)][0] if player1 is not None: score[0] += 1 elif player2 is not None: score[1] += 1 else: # in the case where the 2 players are disconnected, the 1st win... # !FIXME: introduce equality (the result of a game is either WIN, LOOSE or EQUALITY !) score[start] += 1 # update the websockets (no need to update everytime a game is added) self.sendUpdateToWebSocket() # and wait for all the games to end (before running the next round) self._queue.join() time.sleep( 1 ) # !!TODO: check why is not fully working when we remove this sleep.... # update the scores self.updateScore() # Prepare the next list of 2-tuple (player1,player2) of players who will play in next phase try: phase, self._matches = next(self._matchGen) except StopIteration: # no more matches to run (end of the tournament) self.endTournament() else: self.endPhase(phase) # update data through websockets self.sendUpdateToWebSocket()
class TestGame(unittest.TestCase): def setUp(self): self.com = MockCom(self) self.game = Game(self.com, 3, 4, 30, 30, True) self.game.dealer = MockDealer(self) self.game.setup_new_game() self.game.dealer = MockDealer(self) self.dealer = self.game.dealer def tearDown(self): pass def test_chat(self): pass def test_join(self): # try to pass in a shitty name self.com.exeEvt("join", 1, "|shit") self.assertTrue(self.com.invalid["called"]) self.assertEqual(self.com.invalid["p"][1], "|shit") self.com.reset_p() self.com.exeEvt("join", 3, "player1") self.com.exeEvt("join", 4, "player2") self.assertEqual(len(self.game.clients), 2) # now add 3rd player, check if it starts to count down self.com.exeEvt("join", 5, "player3") self.assertEqual(self.game.status, 1) started_time = self.game.count_time # now add 4th players, check if it resets the count down self.com.exeEvt("join", 10, "player4") self.assertNotEqual(self.game.count_time, started_time) started_time = self.game.count_time # now add 5th players, check if it politely ask the player to wait # and not reset the count down self.com.exeEvt("join", 11, "player5") self.assertEqual(self.game.count_time, started_time) self.assertTrue(self.com.wait["called"]) self.assertEqual(self.com.wait["p"][0], 11) self.assertEqual(self.com.wait["p"][1], "player5") self.com.reset_p() # now try to put more player than the queue size # supposed to kick them out self.game.maxQueue = 4 self.com.exeEvt("join", "player6", 14) self.assertTrue(self.com.kick["called"]) self.assertEqual(self.com.kick["p"][0], 14) def test_check_time(self): self.game.check_time() self.com.exeEvt("join", 3, "player1") self.com.exeEvt("join", 4, "player2") self.com.exeEvt("join", 5, "player3") self.game.cd_join = 0 self.game.check_time() self.assertTrue(self.dealer.sg_c["called"]) self.assertEqual(self.dealer.sg_c["p"][0], 3) self.assertTrue(self.com.start["called"]) self.assertEqual(self.com.start["p"][0], "player1,player2,player3")