def _get_voted_out_player(self, team: Team, gamedb: Database) -> [Player, None]: high = 0 candidates = [] log_message(message="Counting votes from team {}.".format(team), game_id=self._game_id) team_votes = gamedb.count_votes(from_team=team) log_message(message="Got votes {}.".format(pprint.pformat(team_votes)), game_id=self._game_id) for _, votes in team_votes.items(): if votes > high: high = votes for id, votes in team_votes.items(): if votes == high: candidates.append(id) num_candidates = len(candidates) if num_candidates == 1: return gamedb.player_from_id(candidates[0]) elif num_candidates > 1: return gamedb.player_from_id(candidates[random.randint( 0, num_candidates - 1)]) else: raise GameError("Unable to determine voted out player.")
def _score_entries_top_k_players(self, team: Team, challenge: Challenge, gamedb: Database, engine: Engine) -> List[Player]: player_scores = {} top_scores = list() losing_players = list() entries = gamedb.stream_entries( from_team=team, from_challenge=challenge) with ThreadPoolExecutor(max_workers=self._options.engine_worker_thread_count) as executor: executor.submit(self._score_entries_top_k_players_fn, entries=entries, challenge=challenge, score_dict=player_scores, gamedb=gamedb, engine=engine) for player_id, score in player_scores.items(): heapq.heappush(top_scores, (score, player_id)) # note that the default python heap pops in ascending order, # so the rank here is actually worst to best. num_scores = len(top_scores) if num_scores == 1: raise GameError( "Unable to rank losing players with team size = 1.") else: for rank in range(num_scores): score, player_id = heapq.heappop(top_scores) log_message("Player {} rank {} with score {}.".format( player_id, rank, score)) # all but the highest scorer lose if rank < (num_scores - 1): losing_players.append(gamedb.player_from_id(player_id)) return losing_players
def _run_finalist_tribe_council(self, finalists: List[Player], gamedb: Database, engine: Engine) -> Player: gamedb.clear_votes() engine.add_event( events.NotifyFinalTribalCouncilEvent( game_id=self._game_id, game_options=self._options, finalists=finalists)) tribal_council_start_timestamp = _unixtime() # wait for votes while (((_unixtime() - tribal_council_start_timestamp) < self._options.final_tribal_council_time_sec) and not self._stop.is_set()): log_message("Waiting for tribal council to end.") time.sleep(self._options.game_wait_sleep_interval_sec) # count votes player_votes = gamedb.count_votes(is_for_win=True) max_votes = 0 winner = None for player_id, votes in player_votes.items(): if votes > max_votes: max_votes = votes winner = gamedb.player_from_id(id=player_id) # announce winner engine.add_event(events.NotifyWinnerAnnouncementEvent( game_id=self._game_id, game_options=self._options, winner=winner)) return winner
def _score_entries_top_k_teams_fn(self, entries: Iterable, challenge: Challenge, score_dict: Dict, gamedb: Database, engine: Engine): entries_iter = iter(entries) while not self._stop_event.is_set(): try: entry = next(entries_iter) log_message(message="Entry {}.".format(entry), game_id=self._game_id) points = self._score_entry(entry=entry) player = gamedb.player_from_id(entry.player_id) engine.add_event( events.NotifyPlayerScoreEvent(game_id=self._game_id, game_options=self._options, player=player, challenge=challenge, entry=entry, points=points)) if player.team_id not in score_dict: score_dict[player.team_id] = points else: score_dict[player.team_id] += points except StopIteration: break
def _run_finalist_tribe_council(self, finalists: List[Player], gamedb: Database, engine: Engine) -> Player: gamedb.clear_votes() self._wait_for_tribal_council_start_time() engine.add_event( events.NotifyFinalTribalCouncilEvent(game_id=self._game_id, game_options=self._options, finalists=finalists)) self._wait_for_tribal_council_end_time() # count votes player_votes = gamedb.count_votes(is_for_win=True) max_votes = 0 winner = None for player_id, votes in player_votes.items(): if votes > max_votes: max_votes = votes winner = gamedb.player_from_id(id=player_id) for player in gamedb.stream_players( active_player_predicate_value=True): if player.id != winner.id: gamedb.deactivate_player(player=player) # announce winner engine.add_event( events.NotifyWinnerAnnouncementEvent(game_id=self._game_id, game_options=self._options, winner=winner)) return winner
def _score_entries_tribe_aggregate_fn(self, entries: Iterable, challenge: Challenge, score_dict: Dict, gamedb: Database, engine: Engine): """Note that all built-ins are thread safe in python, meaning we can atomically increment the score int held in score_dict.""" entries_iter = iter(entries) while not self._stop.is_set(): try: entry = next(entries_iter) pprint.pprint(entry) points = entry.likes / entry.views player = gamedb.player_from_id(entry.player_id) engine.add_event(events.NotifyPlayerScoreEvent( game_id=self._game_id, game_options=self._options, player=player, challenge=challenge, entry=entry, points=points)) score_dict['score'] += points except StopIteration: break
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