def __run_turn(self): """ This method runs a single turn by prompting the current player in the internal state to make a move. """ current_player_obj = self.__get_player_by_color(self.__state.current_player) try: # Get action from player using a deep copy of state action = utils.timed_call(Referee.PLAYER_TIMEOUT, current_player_obj, 'get_action', args=(self.__state.deepcopy(),)) # If call was not successful or anything but an Action object was returned, the player failed if not isinstance(action, Action): self.__kick_player(current_player_obj, PlayerKickReason.FAILING) else: # Use game tree to validate action (will throw InvalidPositionException if # action is illegal) self.__state = self.__game_tree.try_action(action) if Referee.DEBUG: print(f'{current_player_obj.color} just moved from {action.src} to {action.dst}') self.__fire_game_state_changed() except AssertionError as e: # Raise assertion errors are these are used for testing raise e except InvalidActionException: self.__kick_player(current_player_obj, PlayerKickReason.CHEATING)
def __broadcast_tournament_end(self) -> None: """ This methods calls tournament_has_ended() on each IPlayer partaking in the tournament to let it know that the tournament has ended. Failure on the part of any player to acknowledge this in a timely fashion will result in their disconnection. :return: None """ # Trim down player list of winning players to those that ack the notification present_players = [] for p in self.__players: if utils.timed_call(Manager.PLAYER_TIMEOUT, p, 'tournament_has_ended', tuple([True])): present_players.append(p) else: self.__tournament_losers.append(p) self.__tournament_kicked.append(p) p.kick(PlayerKickReason.FAILING.name) self.__players = present_players self.__tournament_winners = present_players # Notify loser players that they lost for loser in self.__tournament_losers: if loser not in self.__tournament_kicked: loser.tournament_has_ended(False)
def __run_placements(self) -> bool: """ Runs placements rounds until everyone has placed their avatars. Players may get removed in the process for either failing or cheating. If all players get removed then the function returns False to indicate there is no point in pressing forward with the game. Otherwise, it returns True. :return: boolean indicating whether any players remain """ # Determine how many avatars there are to place avatars_to_place = self.__avatars_per_player * len(self.__players) # Prompt players to place until we've exhausted all avatars while avatars_to_place > 0: # Cycle over players and have them provide a Position object describing where they # wish to place their avatars for p in self.__players: # Check if player has either failed or cheated; if they have, skip 'em over if p in self.__failing_players or p in self.__cheating_players: avatars_to_place -= 1 continue # Get placement for player using a deep copy of state placement = utils.timed_call(Referee.PLAYER_TIMEOUT, p, 'get_placement', args=(self.__state.deepcopy(),)) # Validate placement received if not isinstance(placement, Position): # If it's not a Position, mark out player as failing & remove player from # state self.__kick_player(p, PlayerKickReason.FAILING) # Decrement avatars needed to be placed avatars_to_place -= 1 continue try: # Try to place on board self.__state.place_avatar(p.color, placement) except InvalidPositionException: # Position is out-of-bounds, already occupied or a hole. Mark player # as cheating & remove player from state. self.__kick_player(p, PlayerKickReason.CHEATING) # Decrement avatars needed to be placed avatars_to_place -= 1 continue if Referee.DEBUG: print(f'got placement of {placement} from player {p.color}') self.__fire_game_state_changed() # Decrement avatars needed to be placed avatars_to_place -= 1 # Check if any players remain after placement (everyone might have gotten kicked) return self.__state.players_no != 0
def __notify_player_colors(self): """ Assign each player the color that correspond to their position in the player list and notify each player which colors they will be playing against. If player's fail to acknowledge the color messages, their are marked as failing players. :return: None """ # Assign each player the color that correspond to their position in the player list game_colors = [] for index, p in enumerate(self.__players): ack = utils.timed_call(Referee.PLAYER_TIMEOUT, p, 'set_color', args=(Color(index),)) game_colors.append(Color(index)) # if the player doesn't ack, they are a failing player if ack is None or not ack: self.__failing_players.append(p) # Notify each player which colors they will be playing against for player in self.__players: colors = [color for color in game_colors if color != player.color] ack = utils.timed_call(Referee.PLAYER_TIMEOUT, player, 'notify_opponent_colors', args=tuple([colors])) # if the player doesn't ack, they are a failing player if ack is None or not ack: self.__failing_players.append(player)
def __broadcast_tournament_start(self) -> None: """ This methods calls tournament_has_started() on each IPlayer partaking in the tournament to let it know that the tournament has begun. Failure on the part of any player to acknowledge this in a timely fashion will result in their disconnection. :return: None """ # Trim down player list to players that have acknowledged that the tournament has started present_players = [] for p in self.__players: if utils.timed_call(Manager.PLAYER_TIMEOUT, p, 'tournament_has_started', ()): present_players.append(p) else: self.__tournament_kicked.append(p) p.kick(PlayerKickReason.FAILING.name) self.__players = present_players
def simimgs(in_csv_path: str, out_csv_path: str): """This function, simimgs, is the main entrypoint for the script. Read through an input csv file, in_csv_path, that contains a list of image path paris, compute their similarity score and record the time it takes to run this computation. For each valid pair of images, write out the similarity score and running time in an output file, out_csv_path. Args: in_csv_path: file path to the input csv file out_csv_path: file path to the output csv file """ try: with open(in_csv_path, 'rt', encoding='utf-8') as csv_in: with open(out_csv_path, 'wt', encoding='utf-8', newline='') as csv_out: csv_reader = csv.reader(csv_in, delimiter=',') csv_writer = csv.writer(csv_out, delimiter=',') next(csv_reader) # skip input csv header csv_writer.writerow(['image1', 'image2', 'similar', 'elapsed']) for line_numb, row in enumerate(csv_reader, start=1): im1_path, im2_path = row[0], row[1] invalid_paths = list( filter(lambda path: not os.path.exists(path), [im1_path, im2_path])) if len(invalid_paths) > 0: print("Invalid line {} in input csv".format(line_numb)) if im1_path in invalid_paths: print("--- Image 1 has an invalid path: {}".format( im1_path)) if im2_path in invalid_paths: print("--- Image 2 has an invalid path: {}".format( im2_path)) continue else: similarity_score, run_time = timed_call( get_similarity, [im1_path, im2_path]) csv_writer.writerow( [im1_path, im2_path, similarity_score, run_time]) except Exception as e: print('Error: ', e)
def __notify_players(self, losers: [IPlayer], winners: [IPlayer], kicked: [IPlayer]) -> [[IPlayer], [IPlayer], [IPlayer]]: """ Notifies the given collection of winning and losing players that they have either won the game (a game, not necessarily the tournament) or that they lost the tournament (if you lose a game, you lose the tournament). A failure on the part of the winning players to accept the notification will result in them being moved to the losers collection. :param losers: list of IPlayer representing losing players :param winners: list of IPlayer representing winning players :param kicked: list of IPlayer representing kicked players :return: resulting losers: [IPlayer], winners: [IPlayer], kicked: [IPlayer] """ # Initialize list to hold IPlayer objects that fail to acknowledge that they won failing_winners = [] # Notify this round's winners that they won for winner in winners: try: result = utils.timed_call(Manager.PLAYER_TIMEOUT, winner, 'status_update', (PlayerStatus.WON_GAME, )) # Make sure we get back True if not isinstance(result, bool) or not result: raise TypeError() except Exception: # If an exception was thrown the winner becomes a loser. failing_winners.append(winner) # Remove failing winners from winners and add them to collections of losers for failing_winner in failing_winners: # A failing winner is not a winner winners.remove(failing_winner) # A failing winner is a game loser losers.append(failing_winner) # A failing winner is a kicked players kicked.append(failing_winner) # A failing winner is a tournament loser self.__tournament_losers.append(failing_winner) # Notify this rounds kicked players that they've been kicked for kick in kicked: # append tournament kicked players self.__tournament_kicked.append(kick) try: kick.status_update(PlayerStatus.DISCONTINUED) except Exception: # Nothing to do. pass # Notify this round's losers that they've lost, only if they haven't already been kicked for loser in losers: # append tournament losers self.__tournament_losers.append(loser) if loser not in kicked: try: loser.status_update(PlayerStatus.LOST_GAME) except Exception: # Nothing to do. pass # Return updated winners & losers return winners, losers, kicked