Пример #1
0
    def test_finish_incomplete_bot(self):
        dictBuilder = DictBuilder()
        dictBuilder.beginArgument()

        dictBuilder.addBot(RegularBot(1, 0, 0), 0)

        with self.assertRaises(Exception):
            dictBuilder.endArgument()
Пример #2
0
    def test_empty_arg(self):
        argBuilder = DictBuilder()

        argBuilder.beginArgument()

        with self.assertRaises(Exception):
            argBuilder.endArgument()
Пример #3
0
    def test_add_more_than_five(self):
        dictBuilder = DictBuilder()

        dictBuilder.beginArgument()

        fill_builder(dictBuilder)

        with self.assertRaises(Exception):
            dictBuilder.addBot(RegularBot(1, 0, 0), 0)
Пример #4
0
    def test_valid_construction(self):
        dictBuilder = DictBuilder()

        dictBuilder.beginArgument()
        fill_builder(dictBuilder)
        dictBuilder.endArgument()

        result = dictBuilder.getResult()

        for i in range(5):
            if i not in result["bots"].keys():
                self.fail("Bot number {} not in result keys !".format(i))

            for parameter in ["life", "flag", "cooldown"]:
                if parameter not in result["bots"][i].keys():
                    self.fail(
                        "Parameter {} wasn't set in bot number {}.".format(
                            parameter, i))
Пример #5
0
class JSONBuilder(ArgBuilder):
    def getResult(self):
        """
        Return the argument built by this builder.

        Return :
            json (string) : string in JSON format
        """
        return json.dumps(self._dictBuilder.getResult())

    def beginArgument(self):
        self._dictBuilder = DictBuilder()
        self._dictBuilder.beginArgument()

    def endArgument(self):
        self._dictBuilder.endArgument()

    def addBot(self, bot, botId, seen):
        self._dictBuilder.addBot(bot, botId, seen)

    def addMissedTicks(self, missedTicks):
        self._dictBuilder.addMissedTicks(missedTicks)
Пример #6
0
    def test_get_empty_result(self):
        dictBuilder = DictBuilder()
        dictBuilder.beginArgument()

        with self.assertRaises(Exception):
            dictBuilder.getResult()
Пример #7
0
 def beginArgument(self):
     self._dictBuilder = DictBuilder()
     self._dictBuilder.beginArgument()
Пример #8
0
    def __init__(self, Player1, Player2, map_file = './maps/map_01.txt'):
        """
        Initialize game data.
  
        Parameters: 
           Player1 (Player): The player in control of the Red team.
           Player2 (Player): The player in control of the Blue team.
        """
        mapData = RegularMap.loadMapData(map_file)

        # Generate an empty map and send it to the players
        # The bots starting positions will be sent at the first polling
        self._map = RegularMap(mapData)

        self._ruleset = Ruleset.GetRuleset()

        self._engine = PhysicsEngine(self._ruleset, self._map)

        self._engine.createCollisionMap("RegularBot", 36)

        self._argBuilder = DictBuilder()

        self._players = dict()

        self._playerProcesses = dict()

        self.turn = 0

        self.stopwatch = TimeManager()
        
        self.countdownremaining = self._ruleset["StartCountdownSeconds"] * 1000

        self.game_over = False

        self.winner = None

        self.mouse_coords = (0,0)

        self.shoots = [] # all shoots to display, reset each turn.
        self.to_exec = [] # functions to execute at some ms

        #### Implementation Simu ####
        try:
            self._players["1"] = Player1(mapData, self._ruleset, team=1)
        except:
            print("Player 1 can't be evaluated because it failed to initialize")

        try:
            self._players["2"] = Player2(mapData, self._ruleset, team=2)
        except:
            print("Player 2 can't be evaluated because it failed to initialize")
        ####

        self._teams = dict()
        self._teamFails = dict()
        self._teamMissedTicks = dict()

        for team in range(1,3): # 2 Players
            teamId = str(team)

            self._teamFails[teamId] = 0 # Keep track of each failure to respond from players
            self._teamMissedTicks[teamId] = 0

            self._teams[teamId] = { "bots": {} }

            for i in range(0,int(self._ruleset["BotsCount"])):
                botId = teamId + "_" + str(i) # Bot identifier is <team>_<number>

                (x, y) = self._map.GetRandomPositionInSpawn(team, 36)
                self._teams[teamId]["bots"][botId] = RegularBot(team, x, y)
                self._teams[teamId]["bots"][botId].setCooldown(self.stopwatch.GetTimeMs())

            
            self._playerProcesses[teamId] = PlayerProcess(self, teamId, self._players[teamId])
            self._playerProcesses[teamId].start()

        self._lastFlagPosition = [
            (-1, -1),
            (-1, -1)
        ]
Пример #9
0
class GameModel(Model):
    """
    Implements the Game Model.

    Attributes:
        map (Map) : The game map.
        ruleset (Ruleset) : The set of rules for this game.
        players (list(Player)) : The players that will be polled each tick.
        teams (dict) : Contains player informations to be sent to them.

        countdownremaining (int) : time in milliseconds since end of start countdownremaining.
    """

    def __init__(self, Player1, Player2, map_file = './maps/map_01.txt'):
        """
        Initialize game data.
  
        Parameters: 
           Player1 (Player): The player in control of the Red team.
           Player2 (Player): The player in control of the Blue team.
        """
        mapData = RegularMap.loadMapData(map_file)

        # Generate an empty map and send it to the players
        # The bots starting positions will be sent at the first polling
        self._map = RegularMap(mapData)

        self._ruleset = Ruleset.GetRuleset()

        self._engine = PhysicsEngine(self._ruleset, self._map)

        self._engine.createCollisionMap("RegularBot", 36)

        self._argBuilder = DictBuilder()

        self._players = dict()

        self._playerProcesses = dict()

        self.turn = 0

        self.stopwatch = TimeManager()
        
        self.countdownremaining = self._ruleset["StartCountdownSeconds"] * 1000

        self.game_over = False

        self.winner = None

        self.mouse_coords = (0,0)

        self.shoots = [] # all shoots to display, reset each turn.
        self.to_exec = [] # functions to execute at some ms

        #### Implementation Simu ####
        try:
            self._players["1"] = Player1(mapData, self._ruleset, team=1)
        except:
            print("Player 1 can't be evaluated because it failed to initialize")

        try:
            self._players["2"] = Player2(mapData, self._ruleset, team=2)
        except:
            print("Player 2 can't be evaluated because it failed to initialize")
        ####

        self._teams = dict()
        self._teamFails = dict()
        self._teamMissedTicks = dict()

        for team in range(1,3): # 2 Players
            teamId = str(team)

            self._teamFails[teamId] = 0 # Keep track of each failure to respond from players
            self._teamMissedTicks[teamId] = 0

            self._teams[teamId] = { "bots": {} }

            for i in range(0,int(self._ruleset["BotsCount"])):
                botId = teamId + "_" + str(i) # Bot identifier is <team>_<number>

                (x, y) = self._map.GetRandomPositionInSpawn(team, 36)
                self._teams[teamId]["bots"][botId] = RegularBot(team, x, y)
                self._teams[teamId]["bots"][botId].setCooldown(self.stopwatch.GetTimeMs())

            
            self._playerProcesses[teamId] = PlayerProcess(self, teamId, self._players[teamId])
            self._playerProcesses[teamId].start()

        self._lastFlagPosition = [
            (-1, -1),
            (-1, -1)
        ]

    def getEngine(self):
        return self._engine

    def tick(self, deltaTime):
        """ 
        Update and handle the game data. Poll each player and process their actions.
  
        Parameters: 
           deltaTime (int): The time in milliseconds since the last call to this function.
        """

        self._engine.tick(deltaTime)
        self.deltaTime = deltaTime

        if self.game_over:
            return

        self.handleEvents()

        if self.turn >= 0 and self.turn != 1 :
            # Called before the start of the countdown and each turn after (not including) the first turn
            self.handlePlayerPolling()
            self.updateLastFlagPosition()

        if self.turn > 0:
            # Call each turn to handle physics and player response
            self.handleNormalTurn()

        # In this condition, we wemove ThinkTimeMs because it is already slept during player polling
        elif self.stopwatch.PeekDeltaTimeMs() > int(self._ruleset["StartCountdownSeconds"]) * 1000 - int(self._ruleset["ThinkTimeMs"]):
            # Call once to finish countdown
            self.handleFirstTurn()

        else:
            # Call each time during countdown after the first player polling
            self.handleStartingCountdown()

        self.checkItemsPickup()

        flagInDepot = self._map.FlagInDepot()

        if flagInDepot != 0:
            self.game_over = True
            self.countdownremaining = 0
            self.winner = flagInDepot

    def handlePlayerPolling(self):
        """
        Creates and starts each Player Process which will process the player's polling function.
        """

        # This timer will run for the whole game
        # Setting the turn to -1 will make us able to know we are in the initial countdown phase
        if self.turn == 0:
            self.stopwatch.StartTimer()
            self.turn = -1

        # The data is reset each time to make sure we don't perform outdated player actions
        self.teamsData = {
            "1" : None,
            "2" : None,
        }

        # Send polling data to each player and get their response
        for teamId in self._players.keys():
            player = self._players[teamId]

            # Start to build the pollindData
            self._argBuilder.beginArgument()

            for i in range(2):
                oldFlagX, oldFlagY = self._lastFlagPosition[i]
                
                flagX = self._map.flags[i].x
                flagY = self._map.flags[i].y


                if flagX != oldFlagX or flagY != oldFlagY:
                    self._argBuilder.addFlag(self._map.flags[i].team, (flagX, flagY))

            for botId in self._teams[teamId]["bots"].keys():
                seen = []
                bot = self._teams[teamId]["bots"][botId]
                for enemy in self._teams[str(1 if teamId == 2 else 1)]["bots"].values():
                    
                    if self._engine.sees(bot,enemy):
                        seen.append({ 
                            "type": type(enemy).__name__,
                            "currentPosition" : (enemy.x, enemy.y, enemy.angle, enemy.speed),
                            "life": enemy.health,
                            "flag": enemy.flag(),
                            "cooldown": enemy.getCooldown(),
                            })
                for categ in self._map.objects.values():
                    for obj in categ:
                        if self._engine.sees(bot,obj):
                            seen.append({
                            "type": type(obj).__name__,
                            "currentPosition" : (obj.x, obj.y),
                            })
                
                self._argBuilder.addBot(self._teams[teamId]["bots"][botId], botId, seen)

            self._argBuilder.addMissedTicks(self._teamMissedTicks[teamId])
            self._argBuilder.endArgument()

            pollingData = self._argBuilder.getResult()

            self._playerProcesses[teamId].setData(pollingData)
            
        # Start each player's computation and sleep while they should be processing
        # After we are done sleeping, two things can occur:
        #   1 - If we are in countdown phase : Players will be able to compute for the duration of the countdown
        #   2 - Else : Players will be immediatly checked and killed. If they did not finish, their (individual) turn is invalid; not affecting other players.
        for playerProcess in self._playerProcesses.values():
            playerProcess.execute()

        # The entire computation of a player must be done during this sleep (if not in countdown phase)
        TimeManager.Sleep(int(self._ruleset["ThinkTimeMs"]))

    def handleNormalTurn(self):
        """
        This stops players computing and processes their response
        """

        # This occurs right after sleeping during ThinkTimeMs milliseconds
        for playerProcess in self._playerProcesses.values():
            playerProcess.check()
                
        self.turn += 1

        # reset shoots
        self.shoots = []

        # Interpret players orders
        for teamId in self.teamsData.keys():
            if(self.teamsData[teamId] == None):
                print("At {}ms (turn {}) : Did not get a response from player {}".format(self.stopwatch.PeekDeltaTimeMs(),self.turn,teamId))
                self._teamFails[teamId] += 1
                self._teamMissedTicks[teamId] += 1
                continue
            elif self.teamsData[teamId] == {}:
                # Empty response, player pass turn
                continue
            else:
                self._teamMissedTicks[teamId] = 0

            # try:
            data = self.teamsData[teamId]
            for botId in data["bots"].keys():
                bot = self._teams[teamId]["bots"][botId]

                # Needed for shoot
                bot_old_x = bot.x
                bot_old_y = bot.y
                bot_old_angle = bot.angle

                # Unpack target
                targetX = data["bots"][botId]["targetPosition"][0]
                targetY = data["bots"][botId]["targetPosition"][1]
                targetSpeed = data["bots"][botId]["targetPosition"][2]
                
                # Perform checks                    
                bot.angle = self._engine.checkAngle(bot, targetX, targetY)
                bot.speed = self._engine.checkSpeed(bot, targetSpeed)

                bot.speed = bot.speed * self._engine.getDeltaTimeModifier()

                # Apply movement
                (realX, realY) = Physics.applyMovement(bot.x, bot.y, bot.angle, bot.speed)
                (newX,newY) = self._engine.checkCollision("RegularBot",bot.x,bot.y,realX,realY,targetX,targetY)

                bot.move(newX - bot.x, newY - bot.y)


                # bitwise comparison for actions
                actions = bin(data["bots"][botId]["actions"])
                actions = actions[::-1] # Reverse action

                if int(actions[0]) == 1: # SHOOT
                    if self.stopwatch.GetTimeMs() - self._teams[teamId]["bots"][botId].getCooldown() > int(self._ruleset["BotShootCooldown"]):
                        self._teams[teamId]["bots"][botId].setCooldown(self.stopwatch.GetTimeMs())

                        (shootedBot, (end_x, end_y)) = self._engine.getShootedBot(
                            bot_old_x,
                            bot_old_y,
                            bot_old_angle,
                            10000,
                            self.getBots(1 if int(teamId) == 2 else 2).values()
                        )

                        # if (shootedBot != None):
                        #     print("Bot {} was shooted !".format(shootedBot))

                        self.shoots.append(((bot_old_x, bot_old_y), (end_x, end_y), bot.player))

                if actions[1]: # DROP_FLAG
                    pass
            # except:
            #     print("Invalid response from player {} : {}".format(teamId,sys.exc_info()[0]))
            #     self._teamFails[teamId] += 1

        for teamId in self._teamFails.keys():
            if self._teamFails[teamId] >= Config.InvalidResponsesKick():
                print("Player {} is disqualified for failing to provide consistent responses.".format(teamId))
                self.kick(teamId)
                self._teamFails[teamId] = -1

    def handleFirstTurn(self):
        """
        Handles the end of the countdown and sets the turn to 1. This causes the turn to be handled without asking for new data, since it is collected during countdown.
        """
        self.turn = 1
        self.countdownremaining = 0

        for playerProcess in self._playerProcesses.values():
            playerProcess.check()

    def handleStartingCountdown(self):
        """
        Handles the countdown phase by always checking for a response without giving new data.
        """
        self.countdownremaining = int(self._ruleset["StartCountdownSeconds"]) * 1000 - int(self._ruleset["ThinkTimeMs"]) - self.stopwatch.PeekDeltaTimeMs()

        for playerProcess in self._playerProcesses.values():
            playerProcess.check()

    def checkItemsPickup(self):
        """
        Checks all bots for a surrounding item to pick.
        """
        allBots = self.getBots()

        for bot in allBots.values():
            for flag in self._map.flags:
                if not flag.held and Physics.distance(flag.x,bot.x,flag.y,bot.y) <= bot.reach:
                    bot.pickUp(flag)

            for effect in self._map.objects["Effect"]:
                if not effect.used and Physics.distance(effect.x,bot.x,effect.y,bot.y) <= bot.reach:
                    effect.apply(bot)
                    self.queueEvent(effect.wearOut,effect.duration,bot)


    # (needed by the View) No point in having it private, should change in the future
    def getMap(self):
        return self._map

    def getBots(self, team = None):
        """ 
        Get bots from one or both teams
  
        Returns:
            bots (list) : The list of requested bots
        """
        if team == None:
            return self.getAllBots()
            
        return self._teams[str(team)]["bots"]

    def getAllBots(self):
        """ 
        Get bots from both teams
  
        Returns:
            bots (list) : The list of all Bots in the game.
        """
        bots = self._teams["1"]["bots"].copy()
        bots.update(self._teams["2"]["bots"])
        return bots

    def register(self, player):
        """
        Only used if we make it a server.

        Add a new player to the game and provide it with necessary data.

        Returns:
            data : Contains all the data a player can have at it's init.
        """
       
        pass

    def kick(self, teamId):
        """
        Kick a player from the game.
        """
        del self._players[teamId]

    def updateLastFlagPosition(self):
        """
        Updates the last position of each flag
        """
        for i in range(2):
            self._lastFlagPosition[i] = (self._map.flags[i].x, self._map.flags[i].y)
            # TODO: If the flag is inside a base, switch to endscreen

    def stop(self):
        """
        Ends the game and terminates child processes
        """
        for playerProcess in self._playerProcesses.values():
            playerProcess.kill()

    def getShoots(self):
        return self.shoots
        
    def queueEvent(self, function, inMs, args):
        """
        Queues a function to be executer in "inMs"" time in milliseconds
        """
        self.to_exec.append((function,self.stopwatch.PeekDeltaTimeMs() + inMs,args))

    def handleEvents(self):
        """
        Checks for events to consume and consumes them
        """
        i = 0
        while i < len(self.to_exec):
            if self.stopwatch.PeekDeltaTimeMs() >= self.to_exec[i][1]:
                self.to_exec[i][0](self.to_exec[i][2])
                self.to_exec.remove(self.to_exec[i])
                i -= 1
            i += 1