예제 #1
0
    def _merge_teams(self, target_team_size: int, tribe: Tribe, gamedb: Database, engine: Engine):
        # team merging is only necessary when the size of the team == 2
        # once a team size == 2, it should be merged with another team. the optimal
        # choice is to keep team sizes as close to the intended size as possible

        # find all teams with size = 2, these players need to be merged
        small_teams = gamedb.stream_teams(
            from_tribe=tribe, team_size_predicate_value=2)
        merge_candidates = Queue()

        for team in small_teams:
            log_message("Found team of 2. Deacticating team {}.".format(team))

            # do not deactivate the last active team in the tribe
            if gamedb.count_teams(from_tribe=tribe, active_team_predicate_value=True) > 1:
                gamedb.deactivate_team(team)

            for player in gamedb.list_players(from_team=team):
                log_message("Adding merge candidate {}.".format(player))
                merge_candidates.put(player)

        sorted_teams = gamedb.stream_teams(
            from_tribe=tribe, order_by_size=True, descending=False)

        log_message("Redistributing merge candidates...")
        # round robin redistribution strategy
        # simplest case, could use more thought.
        visited = {}
        while not merge_candidates.empty() and sorted_teams:
            for team in sorted_teams:
                other_options_available = team.id not in visited
                visited[team.id] = True

                if (team.size >= target_team_size and other_options_available):
                    log_message("Team {} has size >= target {} and other options are available. "
                                "Continuing search...".format(team, target_team_size))
                    continue

                player = merge_candidates.get()
                if player.team_id == team.id:
                    continue

                log_message("Merging player {} from team {} into team {}.".format(
                    player, player.team_id, team.id))
                player.team_id = team.id
                team.size = team.size + 1
                gamedb.save(team)
                gamedb.save(player)

                # notify player of new team assignment
                engine.add_event(events.NotifyTeamReassignmentEvent(game_id=self._game_id, game_options=self._options, player=player,
                                                                    team=team))
예제 #2
0
 def _merge_tribes(self, tribe1: Tribe, tribe2: Tribe, new_tribe_name: Text,
                   gamedb: Database, engine: Engine) -> Tribe:
     log_message(message=f"Merging tribes into {new_tribe_name}.")
     with engine:
         new_tribe = gamedb.tribe(name=new_tribe_name)
         gamedb.batch_update_tribe(from_tribe=tribe1, to_tribe=new_tribe)
         gamedb.batch_update_tribe(from_tribe=tribe2, to_tribe=new_tribe)
         # after tribes merge, sweep the teams to ensure no size of 2
         self._merge_teams(target_team_size=self._options.target_team_size,
                           tribe=new_tribe,
                           gamedb=gamedb,
                           engine=engine)
         game = gamedb.game_from_id(gamedb.get_game_id())
         game.count_tribes = 1
         gamedb.save(game)
         return new_tribe
예제 #3
0
    def _run_challenge(self, challenge: Challenge, gamedb: Database,
                       engine: Engine):
        # wait for challenge to begin
        self._wait_for_challenge_start_time(challenge=challenge)

        # notify players
        engine.add_event(
            events.NotifyTribalChallengeEvent(game_id=self._game_id,
                                              game_options=self._options,
                                              challenge=challenge))

        # wait for challenge to end
        self._wait_for_challenge_end_time(challenge=challenge)

        challenge.complete = True
        gamedb.save(challenge)
예제 #4
0
    def _run_challenge(self, challenge: Challenge, gamedb: Database, engine: Engine):
        # wait for challenge to begin
        while (_unixtime() < challenge.start_timestamp) and not self._stop.is_set():
            log_message("Waiting {}s for challenge to {} to begin.".format(
                challenge.start_timestamp - _unixtime(), challenge))
            time.sleep(self._options.game_wait_sleep_interval_sec)

        # notify players
        engine.add_event(
            events.NotifyTribalChallengeEvent(game_id=self._game_id, game_options=self._options, challenge=challenge))

        # wait for challenge to end
        while (_unixtime() < challenge.end_timestamp) and not self._stop.is_set():
            log_message("Waiting {}s for challenge to {} to end.".format(
                challenge.end_timestamp - _unixtime(), challenge))
            time.sleep(self._options.game_wait_sleep_interval_sec)

        challenge.complete = True
        gamedb.save(challenge)
예제 #5
0
    def generate_tribes(cls, game_id: str, players: List[DocumentSnapshot],
                        game_options: GameOptions, gamedb: Database) -> dict:
        tribes = list()
        teams = list()
        count_players = len(players)
        if count_players < game_options.target_team_size:
            raise MatchMakerError("Insufficient players for given team size")
        if count_players < game_options.multi_tribe_min_tribe_size * 2:
            raise MatchMakerError("Insufficient players to make two tribes")
        # generate tribes
        for tribe_name in [
                _DEFAULT_TRIBE_NAMES[int(n)]
                for n in random.sample(range(0, len(_DEFAULT_TRIBE_NAMES)), 2)
        ]:
            tribe = database.Tribe(id=str(uuid.uuid4()),
                                   name="{}".format(tribe_name))
            tribes.append(tribe)

        # generate teams
        for n in range(
                0, math.floor(count_players / game_options.target_team_size)):
            team = database.Team(
                id=str(uuid.uuid4()),
                name="{}".format(n),
            )
            teams.append(team)

        count_tribes = len(tribes)
        count_teams = len(teams)
        team_to_tribe_map = {}

        # randomly assign team, tribe to each player
        set_of_mutable_players = set()
        set_of_mutable_users = set()
        for n, player in enumerate(players):
            mutable_user = gamedb.find_user(
                phone_number=player.get('phone_number'))
            mutable_user.game_id = game_id
            mutable_player = gamedb.player_from_id(player.id)
            tribe = tribes[n % count_tribes]
            team = teams[n % count_teams]
            if team.id not in team_to_tribe_map:
                team_to_tribe_map[team.id] = tribe
                tribe.count_teams += 1
            mutable_player.tribe_id = tribe.id
            mutable_player.team_id = team.id
            team.tribe_id = tribe.id
            tribe.count_players += 1
            team.count_players += 1
            set_of_mutable_players.add(mutable_player)
            set_of_mutable_users.add(mutable_user)

        # Save data
        game = gamedb.game_from_id(game_id)
        game.count_tribes = count_tribes
        game.count_teams = count_teams
        game.count_players = count_players
        gamedb.save(game)

        for tribe in tribes:
            gamedb.save(tribe)
        for team in teams:
            gamedb.save(team)
        for player in set_of_mutable_players:
            gamedb.save(player)
        for user in set_of_mutable_users:
            gamedb.save(user)

        d = {}
        d['players'] = players
        d['teams'] = teams
        d['tribes'] = tribes
        d['game'] = game
        return d
예제 #6
0
    def _merge_teams(self, target_team_size: int, tribe: Tribe,
                     gamedb: Database, engine: Engine):
        with engine:
            # team merging is only necessary when the size of the team == 2
            # once a team size == 2, it should be merged with another team, because
            # a self preservation vote lands in a deadlock. in general, the optimal
            # choice is to keep team sizes as close to the intended size as possible
            # up until a merge becomes necessary.

            # find all teams with size == 2, these players need to be merged
            small_teams = gamedb.stream_teams(from_tribe=tribe,
                                              team_size_predicate_value=2)
            merge_candidates = Queue()

            for team in small_teams:
                # do not deactivate the last active team in the tribe
                count_teams = gamedb.count_teams(
                    from_tribe=tribe, active_team_predicate_value=True)
                if count_teams > 1:
                    log_message(
                        message="Found team of 2. Deactivating team {}.".
                        format(team),
                        game_id=self._game_id)
                    gamedb.deactivate_team(team)
                for player in gamedb.list_players(from_team=team):
                    log_message(
                        message="Adding merge candidate {}.".format(player),
                        game_id=self._game_id)
                    merge_candidates.put(player)

            sorted_teams = gamedb.stream_teams(from_tribe=tribe,
                                               order_by_size=True,
                                               descending=False)

            log_message(message="Redistributing merge candidates...",
                        game_id=self._game_id)
            # round robin redistribution strategy
            # simplest case, could use more thought.
            visited = {}
            while not merge_candidates.empty() and sorted_teams:
                for team in itertools.cycle(sorted_teams):
                    team.count_players = _team_count_players(team, gamedb)
                    other_options_available = team.id not in visited
                    visited[team.id] = True

                    if (team.count_players >= target_team_size
                            and other_options_available):
                        log_message(
                            message=
                            "Team {} has size >= target {} and other options are available. "
                            "Continuing search...".format(
                                team, target_team_size),
                            game_id=self._game_id)
                        continue

                    player = None
                    try:
                        player = merge_candidates.get_nowait()
                    except Empty:
                        log_message(message="Merge candidates empty.")
                        return

                    if player.team_id == team.id:
                        log_message(
                            message=
                            f"Player {str(player)} already on team {str(team)}. Continuing."
                        )
                        continue

                    log_message(
                        message="Merging player {} from team {} into team {}.".
                        format(player, player.team_id, team.id),
                        game_id=self._game_id)
                    player.team_id = team.id
                    team.count_players += 1
                    gamedb.save(team)
                    gamedb.save(player)

                    # notify player of new team assignment
                    engine.add_event(
                        events.NotifyTeamReassignmentEvent(
                            game_id=self._game_id,
                            game_options=self._options,
                            player=player,
                            team=team))