def get_draw_action(drawing: Drawable, player_state: PlayerState, board: Board, start_index: int = 0) \ -> List[Tuple[PlayerAction, int]]: # def return variable to return all possible actions possible_actions: List[Tuple[PlayerAction, int]] = [] # check if drawing this pattern is possible if start_index >= 0: pattern = get_pattern(drawing) if start_index < len(pattern): possible_actions += list( __get_possible_actions(pattern, board.copy(), player_state, abs(start_index)).items()) # check if drawing the inverted pattern is possible if start_index <= 0: inverted_pattern = get_pattern(drawing, True) if abs(start_index) < len(inverted_pattern): possible_actions += [ (action, -new_start_index) for action, new_start_index in __get_possible_actions( inverted_pattern, board.copy(), player_state, abs(start_index)).items() ] # return possible actions with the next start_index return possible_actions
def __init__(self, width: int, height: int, player_count: int): self.board = Board(width, height) start_point_distance = self.board.cell_count // (player_count + 1) self.players = [] # Init Player y_start_positions = list(range(1, self.board.width - 1)) x_start_positions = list(range(1, self.board.height - 1)) del y_start_positions[::2] del x_start_positions[::2] start_positions = list( itertools.product(y_start_positions, x_start_positions)) random.shuffle(start_positions) for player_id in range(1, player_count + 1): start_cell = start_positions.pop() player = Player( player_id, PlayerState(random.choice(list(PlayerDirection)), 1, start_cell[0], start_cell[1])) self.board.set_cell(player.current_state.position_x, player.current_state.position_y, player.player_id) self.players.append(player) self.is_started = False self.deadline = None self.on_round_start = Event() self.all_player_moved = False
def calculate_risk_areas(board: Board): risk_evaluation = np.zeros((board.height, board.width)) existing_cell_occupation = np.array(board.cells) risk_evaluation[existing_cell_occupation != 0] = 1 rows, cols = np.where(risk_evaluation == 1) rows = list(rows) cols = list(cols) # Add the board borders rows += [-1] * board.width + [board.height] * board.width + [ -1 ] * board.height + [board.width] * board.height cols += list(range(0, board.width)) * 2 + list(range(0, board.height)) * 2 last_evaluated = {(cols[index], rows[index]): 1 for index in range(0, len(rows))} while len(last_evaluated) > 0: # Get neighbors of last evaluated to_be_evaluated: Dict[Tuple[int, int], RiskClass] = {} for last in last_evaluated.keys(): for neighbor in board.get_neighbors(last[0], last[1]): if not neighbor or risk_evaluation[neighbor[1]][ neighbor[0]] > 0 or neighbor in to_be_evaluated: continue neighbors_risk_clockwise = [ (1 if i is None else risk_evaluation[i[1]][i[0]]) for i in board.get_neighbors(neighbor[0], neighbor[1]) ] to_be_evaluated[neighbor] = RiskClass( 0, neighbors_risk_clockwise) last_evaluated.clear() # Sort neighbors by pattern weight to_be_evaluated = { k: v for k, v in sorted(to_be_evaluated.items(), key=lambda item: item[1].pattern.value[2], reverse=True) } for n in to_be_evaluated.items(): position = n[0] neighbors_risk_clockwise = [ (1 if i is None else risk_evaluation[i[1]][i[0]]) for i in board.get_neighbors(position[0], position[1]) ] # Set neighbors neighbors new & calculate risk n[1].set_neighbors(neighbors_risk_clockwise) # Add the neighbor to last evaluated last_evaluated[position] = n[1] # Add the neighbor to risk evaluation risk_evaluation[position[1]][position[0]] = n[1].get_risk() return risk_evaluation
def do_move(self, board: Board) -> object: if self.action is None: raise Exception('No action has been performed yet') child = self.copy() child.action = None x_offset, y_offset = self.direction.to_direction_tuple() if self.direction == PlayerDirection.UP: child.position_y -= self.speed elif self.direction == PlayerDirection.RIGHT: child.position_x += self.speed elif self.direction == PlayerDirection.DOWN: child.position_y += self.speed elif self.direction == PlayerDirection.LEFT: child.position_x -= self.speed # Calculate the new steps child.steps_to_this_point = list( Board.get_points_in_rectangle(self.position_x + x_offset, self.position_y + y_offset, child.position_x, child.position_y)) jump_needed = False for x, y in child.steps_to_this_point[1:-1]: if board.point_is_available(x, y): jump_needed = True if jump_needed and self.roundModulo == -1: self.roundModulo = 0 # Remove the jumped over cells do_jump = self.roundModulo == 0 while do_jump and len(child.steps_to_this_point) > 2: child.steps_to_this_point.pop(1) # Add self to previous of child child.previous.append(self) # All new passed positions for step in child.steps_to_this_point: child.collided_with_own_line |= bool( child.all_steps.get(step, False)) if not child.collided_with_own_line: child.all_steps.setdefault(step, child) # Increase round modulo if self.roundModulo == -1: child.roundModulo = -1 else: child.roundModulo = (self.roundModulo + 1) % 6 return child
def calculate_probabilities_for_player( board: Board, player_state: PlayerState, depth: int, step_offset: int = 1, probability_cutoff: float = 0, global_probability_factor: float = 1. ) -> Tuple[np.ndarray, np.ndarray]: """ Returns tuple of numpy arrays: - probability of reaching the given cells in the [depth] next steps - minimum amount of steps needed for the given player to reach the cell """ probabilities = np.zeros((board.height, board.width)) min_player_steps = np.ones( (board.height, board.width)) * __INIT_VALUE_PLAYER_STEPS valid_player_state_tuples = [] for action in PlayerAction: new_player_state = player_state.copy() new_player_state.do_action(action) next_player_state = new_player_state.do_move() if next_player_state.verify_move(board): valid_player_state_tuples.append( (new_player_state, next_player_state)) possible_action_count = len(valid_player_state_tuples) if possible_action_count > 0: local_probability_factor = ( 1 / possible_action_count) * global_probability_factor for new_player_state, next_player_state in valid_player_state_tuples: affected_cells = next_player_state.steps_to_this_point for cell_x, cell_y in affected_cells: if board.point_is_on_board(cell_x, cell_y): probabilities[cell_y, cell_x] += local_probability_factor min_player_steps[cell_y, cell_x] = step_offset # print(f"{local_probability_factor}\t{probability_cutoff}") if depth > 1 and local_probability_factor > probability_cutoff: recursion_probabilities, recursion_min_player_steps = \ calculate_probabilities_for_player(board, next_player_state, depth=depth - 1, step_offset=step_offset + 1, probability_cutoff=probability_cutoff, global_probability_factor=local_probability_factor) probabilities += recursion_probabilities min_player_steps = np.minimum(min_player_steps, recursion_min_player_steps) return probabilities, min_player_steps
def verify_move(self, board: Board) -> bool: if not self.verify_state(board): return False # check for collisions with other players # check for collisions with myself for step in self.steps_to_this_point: if not board.point_is_available( step[0], step[1]) or self.collided_with_own_line: return False return True
def update(self, step_info: dict) -> List[Tuple[int, int]]: last_round_board = None new_occupied_cells = [] if self.cells: Board.from_cells(self.cells) self.cells = step_info["cells"] decision_relevant_player_states = { player_id: player.current_state for player_id, player in self.players.items() } # Update all players for player_id, player_data in step_info["players"].items(): self.players.setdefault( player_id, Enemy(player_id, PlayerDirection[player_data["direction"].upper()], player_data["speed"], player_data["x"], player_data["y"], step_info["width"], step_info["height"])).update(player_data) new_occupied_cells += self.players[ player_id].current_state.steps_to_this_point # recalculate_aggressiveness for all enemies for player in self.players.values(): # only for active players and not for your own player if player.is_active and player.player_id != step_info["you"]: players_enemies = [ v for k, v in decision_relevant_player_states.items() if k != player.player_id ] player.recalculate_aggressiveness(players_enemies, last_round_board) return new_occupied_cells
def determine_cutting_and_fill_values( player_state: PlayerState, board: Board, search_length: int ) -> Tuple[Dict[PlayerAction, float], Dict[PlayerAction, float]]: original_array = np.array(board.cells) original_labels = get_safe_areas_labels(original_array) original_label_count = get_safe_areas_label_count(original_labels) result_fill_values = {} result_cutting_values = {} for action in PlayerAction: local_base_state = player_state.copy() local_base_state.do_action(action) local_next_state = local_base_state.do_move() fill_value = 0. cutting_value = 1. if local_next_state.verify_move(board): adapted_array = np.array(board.cells) for x, y in local_next_state.steps_to_this_point: adapted_array[y, x] = 1. adapted_labels = get_safe_areas_labels(adapted_array) adapted_labels_count = get_safe_areas_label_count(adapted_labels) if adapted_labels_count > original_label_count: cutting_value = 0. else: x, y = local_base_state.position_x, local_base_state.position_y x_direction, y_direction = local_next_state.direction.to_direction_tuple( ) for distance_idx in range(search_length): x += x_direction y += y_direction if not board.point_is_available(x, y): fill_value = 1 - ((distance_idx - 1) / search_length) break result_fill_values[action] = fill_value result_cutting_values[action] = cutting_value return result_cutting_values, result_fill_values
def do_move(self) -> object: if self.action is None: raise Exception('No action has been performed yet') child = self.copy() child.action = None x_offset = 0 y_offset = 0 if self.direction == PlayerDirection.UP: child.position_y -= self.speed y_offset = -1 elif self.direction == PlayerDirection.RIGHT: child.position_x += self.speed x_offset = 1 elif self.direction == PlayerDirection.DOWN: child.position_y += self.speed y_offset = 1 elif self.direction == PlayerDirection.LEFT: child.position_x -= self.speed x_offset = -1 # Calculate the new steps child.steps_to_this_point = list( Board.get_points_in_rectangle(self.position_x + x_offset, self.position_y + y_offset, child.position_x, child.position_y)) # Remove the jumped over cells do_jump = self.game_round % 6 == 0 while do_jump and len(child.steps_to_this_point) > 2: child.steps_to_this_point.pop(1) # Add self to previous of child child.previous.append(self) # All new passed positions for step in child.steps_to_this_point: child.collided_with_own_line |= bool( child.all_steps.get(step, False)) if not child.collided_with_own_line: child.all_steps.setdefault(step, child) # Increase round number child.game_round += 1 return child
def calculate_ranges_for_player(board: Board, initial_state: PlayerState, lookup_round_count: int = -1, updated_last_result=None) \ -> Dict[Tuple[int, int], Dict[PlayerDirection, Dict[int, PlayerState]]]: if updated_last_result is None: updated_last_result = {} result_data = updated_last_result next_states = [initial_state] next_states += [state for directions in result_data.values() for speeds in directions.values() for state in speeds.values()] current_round = 0 while len(next_states) > 0 and lookup_round_count != current_round: next_states = calculate_next_states(board, next_states, result_data) current_round += 1 return result_data if __name__ == "__main__": start = time.time() print(F"start full_range @{datetime.now().time()}") print(len(calculate_ranges_for_player(Board(64, 64), PlayerState(PlayerDirection.DOWN, 1, 4, 4)).keys())) end = time.time() print(F"total seconds: {end - start}") print(F"end full_range @{datetime.now().time()}")
def handle_step(self, step_info, slice_viewer): self.roundCounter += 1 own_player = step_info["players"][str(step_info["you"])] # init on first step info if self.roundCounter == 1: self.board = Board(step_info["width"], step_info["height"]) self.playerState = PlayerState( PlayerDirection[own_player["direction"].upper()], own_player["speed"], own_player["x"], own_player["y"], self.roundCounter) # update cells self.board.cells = step_info["cells"] # build enemy player states enemy_player_states = [] for player_id, player in step_info["players"].items(): if str(step_info["you"]) != player_id and player["active"]: enemy_player_states.append( PlayerState(PlayerDirection[player["direction"].upper()], player["speed"], player["x"], player["y"], self.roundCounter)) # calculate enemy prediction enemy_probabilities, enemy_min_steps = \ probability_based_prediction.calculate_probabilities_for_players(self.board, enemy_player_states, depth=7) # add enemy prediction to viewer slice_viewer.add_data("enemy_probability", enemy_probabilities, normalize=False) slice_viewer.add_data("enemy_min_steps", enemy_min_steps, normalize=True) # add safe_area sizes to viewer safe_areas, safe_area_labels = get_risk_evaluated_safe_areas( self.board) safe_area_sizes = np.zeros(safe_area_labels.shape) for area in safe_areas: for point in area.points: safe_area_sizes[point[1], point[0]] = area.risk slice_viewer.add_data("safe_area_sizes", safe_area_sizes, normalize=False) # add risk_area to viewer slice_viewer.add_data("risk_evaluation", risk_area_calculation.calculate_risk_areas( self.board), normalize=False) # apply threshold to probabilities enemy_probabilities[enemy_probabilities > 0.19] = 1 enemy_probabilities[enemy_probabilities != 1] = 0 # update board with probabilities self.board.cells = enemy_probabilities.tolist() # calculate action full_range_result = no_risk_full_range.calculate_ranges_for_player( self.board, self.playerState) path_options = [ state for directions in full_range_result.values() for speeds in directions.values() for state in speeds.values() ] if len(path_options) > 0: # determine action with highest amount of reachable points action_histogram = { player_action: 0 for player_action in PlayerAction } for path_option in path_options: player_states = path_option.previous + [path_option] path_action = player_states[self.roundCounter - 1].action action_histogram[path_action] += 1 action = max(action_histogram, key=action_histogram.get) # random action if no way to survive else: action = random.choice(list(PlayerAction)) # add path options to viewer path_steps_array = np.zeros((step_info["height"], step_info["width"])) for option in path_options: x = option.position_x y = option.position_y current_value = path_steps_array[y, x] new_value = option.game_round - self.roundCounter path_steps_array[y, x] = new_value if current_value == 0 else min( current_value, new_value) slice_viewer.add_data("full_range_steps", path_steps_array) # apply action to local model self.playerState.do_action(action) self.playerState = self.playerState.do_move() return action
def verify_state(self, board: Board) -> bool: # check for speed conditions and if the new position is on the board return self.verify_speed() and board.point_is_on_board( self.position_x, self.position_y)
def handle_step(self, step_info, slice_viewer): self.roundCounter += 1 own_player = step_info["players"][str(step_info["you"])] # init on first step info if self.roundCounter == 1: self.board = Board(step_info["width"], step_info["height"]) self.playerState = PlayerState(PlayerDirection[own_player["direction"].upper()], own_player["speed"], own_player["x"], own_player["y"], self.roundCounter) self.pathFinder = BidirectionalPathFinder(self.board, 5, 2) # update cells self.board.cells = step_info["cells"] # build enemy player states enemy_player_states = [] for player_id, player in step_info["players"].items(): if str(step_info["you"]) != player_id and player["active"]: enemy_player_states.append( PlayerState( PlayerDirection[player["direction"].upper()], player["speed"], player["x"], player["y"], self.roundCounter)) # calculate enemy prediction enemy_probabilities, enemy_min_steps = \ probability_based_prediction.calculate_probabilities_for_players(self.board, enemy_player_states, depth=7) # add enemy prediction to viewer slice_viewer.add_data("enemy_probability", enemy_probabilities, normalize=False) slice_viewer.add_data("enemy_min_steps", enemy_min_steps, normalize=True) # get path finder results for each possible action self.pathFinder.update(self.board, self.playerState, enemy_probabilities, enemy_min_steps) path_finder_rating_result_map = self.pathFinder.get_result_rating_map() path_finder_steps_result_map = self.pathFinder.get_result_steps_map() # calculate reachable points for full range results max_reachable_points_value = \ max(max([np.sum(result) for result in path_finder_rating_result_map.values()]), 1) reachable_points = {player_action: np.sum(result) / max_reachable_points_value for player_action, result in path_finder_rating_result_map.items()} # calculate action distribution for full range results fill_distribution = corridor_fill_detection.determine_fill_values(self.playerState, self.board) # build slow down force slow_force = {player_action: 0. for player_action in PlayerAction} slow_base_state = self.playerState.copy() slow_base_state.do_action(PlayerAction.SLOW_DOWN) slow_next_state = slow_base_state.do_move() if slow_next_state.verify_move(self.board): slow_force[PlayerAction.SLOW_DOWN] = 1. # calculate weighted evaluation for each possible action print(f"\t\t\treachable:\t\t{reachable_points}") print(f"\t\t\tfill:\t\t\t{fill_distribution}") print(f"\t\t\tslow:\t\t\t{slow_force}") weighted_action_evaluation = {action: reachable_points[action] * self.REACHABLE_POINT_WEIGHT + fill_distribution[action] * self.FILL_WEIGHT + slow_force[action] * self.SLOW_FORCE_WEIGHT for action in PlayerAction} print(f"\t\t\tevaluation:\t\t{weighted_action_evaluation}") # chose action based of highest value action = max(weighted_action_evaluation, key=weighted_action_evaluation.get) # add reachable points to viewer slice_viewer.add_data("reachable_points_rating", path_finder_rating_result_map[action], normalize=False) slice_viewer.add_data("reachable_points_steps", path_finder_steps_result_map[action], normalize=True) # apply action to local model self.playerState.do_action(action) self.playerState = self.playerState.do_move() return action
def handle_step(self, step_info, slice_viewer): self.roundCounter += 1 own_player = step_info["players"][str(step_info["you"])] # init on first step info if self.roundCounter == 1: self.board = Board(step_info["width"], step_info["height"]) self.playerState = PlayerState(PlayerDirection[own_player["direction"].upper()], own_player["speed"], own_player["x"], own_player["y"], self.roundCounter) # update cells self.board.cells = step_info["cells"] # build enemy player states enemy_player_states = [] for player_id, player in step_info["players"].items(): if str(step_info["you"]) != player_id and player["active"]: enemy_player_states.append( PlayerState( PlayerDirection[player["direction"].upper()], player["speed"], player["x"], player["y"], self.roundCounter)) # calculate enemy prediction enemy_probabilities, enemy_min_steps = \ probability_based_prediction.calculate_probabilities_for_players(self.board, enemy_player_states, depth=7) # add enemy prediction to viewer slice_viewer.add_data("enemy_probability", enemy_probabilities, normalize=False) slice_viewer.add_data("enemy_min_steps", enemy_min_steps, normalize=True) # add safe_area sizes to viewer safe_areas, safe_area_labels = get_risk_evaluated_safe_areas(self.board) safe_area_sizes = np.zeros(safe_area_labels.shape) for area in safe_areas: for point in area.points: safe_area_sizes[point[1], point[0]] = area.risk slice_viewer.add_data("safe_area_sizes", safe_area_sizes, normalize=False) # add risk_area to viewer slice_viewer.add_data("risk_evaluation", risk_area_calculation.calculate_risk_areas(self.board), normalize=False) # apply threshold to probabilities enemy_probabilities[enemy_probabilities > 0.19] = 1 enemy_probabilities[enemy_probabilities != 1] = 0 # update board with probabilities self.board.cells = enemy_probabilities.tolist() # determine amount of reachable points for each action player_action_array = [player_action for player_action in PlayerAction] pool = mp.Pool(mp.cpu_count()) path_option_results = pool.map( self.get_full_range_path_options_for_action, player_action_array) pool.close() action_histogram = {player_action: len(path_option_results[player_action_array.index(player_action)]) for player_action in PlayerAction} # apply inverse weight based on probability of next possible enemy step probabilities_in_next_step = np.copy(enemy_probabilities) probabilities_in_next_step[enemy_min_steps != 1] = 0 for action, possible_points_count in action_histogram.items(): if possible_points_count > 0: current_player_state = self.playerState.copy() current_player_state.do_action(action) possible_next_player_state = current_player_state.do_move() max_probability_of_steps = 0 for x, y in possible_next_player_state.steps_to_this_point: max_probability_of_steps = max(probabilities_in_next_step[y, x], max_probability_of_steps) action_histogram[action] = (1 - max_probability_of_steps) * possible_points_count # chose action based of highest value action = max(action_histogram, key=action_histogram.get) # add path options to viewer path_steps_array = np.zeros((step_info["height"], step_info["width"])) path_options = path_option_results[player_action_array.index(action)] for option in path_options: x = option.position_x y = option.position_y current_value = path_steps_array[y, x] new_value = option.game_round - self.roundCounter path_steps_array[y, x] = new_value if current_value == 0 else min(current_value, new_value) slice_viewer.add_data("full_range_steps", path_steps_array) # apply action to local model self.playerState.do_action(action) self.playerState = self.playerState.do_move() return action
result_data) # Weight next_status for state in next_states: step_risk_sum = 0 step_count = 0 for step in state.steps_to_this_point: step_risk_sum += game_board[step[1]][step[0]] step_count += 1 state_risk = step_risk_sum / step_count prev_length = len(state.previous) - 1 prev_risk = state.previous[-1].optional_risk state.optional_risk = (state_risk + prev_risk * prev_length) / (prev_length + 1) current_round += 1 return result_data if __name__ == "__main__": board = Board(5, 10) for i in range(0, board.height): for j in range(0, board.width): board[i][j] = random() calculate_ranges_for_player(board, PlayerState(PlayerDirection.RIGHT, 1, 0, 0))
def handle_step(self, step_info, slice_viewer): self.roundCounter += 1 own_player = step_info["players"][str(step_info["you"])] # init on first step info if self.roundCounter == 1: self.board = Board(step_info["width"], step_info["height"]) self.playerState = PlayerState( PlayerDirection[own_player["direction"].upper()], own_player["speed"], own_player["x"], own_player["y"], self.roundCounter) # update cells self.board.cells = step_info["cells"] # build enemy player states enemy_player_states = [] for player_id, player in step_info["players"].items(): if str(step_info["you"]) != player_id and player["active"]: enemy_player_states.append( PlayerState(PlayerDirection[player["direction"].upper()], player["speed"], player["x"], player["y"], self.roundCounter)) # calculate enemy prediction enemy_probabilities, enemy_min_steps = \ probability_based_prediction.calculate_probabilities_for_players(self.board, enemy_player_states, depth=7) # add enemy prediction to viewer slice_viewer.add_data("enemy_probability", enemy_probabilities, normalize=False) slice_viewer.add_data("enemy_min_steps", enemy_min_steps, normalize=True) # add safe_area sizes to viewer safe_areas, safe_area_labels = get_risk_evaluated_safe_areas( self.board) safe_area_sizes = np.zeros(safe_area_labels.shape) for area in safe_areas: for point in area.points: safe_area_sizes[point[1], point[0]] = area.risk slice_viewer.add_data("safe_area_sizes", safe_area_sizes, normalize=False) # add risk_area to viewer slice_viewer.add_data("risk_evaluation", risk_area_calculation.calculate_risk_areas( self.board), normalize=False) # get full range result for each possible action player_action_array = [player_action for player_action in PlayerAction] input_array = [(self.playerState, player_action, self.board, enemy_probabilities, enemy_min_steps) for player_action in player_action_array] pool = mp.Pool(mp.cpu_count()) path_option_results = pool.starmap( enemy_probability_full_range.calculate_ranges_for_player_action, input_array) pool.close() full_range_results = { player_action: path_option_results[player_action_array.index(player_action)] for player_action in PlayerAction } # calculate reachable points for full range results max_reachable_points_value = \ max(max([np.sum(full_range_result) for full_range_result in full_range_results.values()]), 1) reachable_points = { player_action: np.sum(full_range_result) / max_reachable_points_value for player_action, full_range_result in full_range_results.items() } # calculate action distribution for full range results cutting_distribution, fill_distribution = \ basic_cut_fill_area_detection.determine_cutting_and_fill_values(self.playerState, self.board, 4) # build slow down force slow_force = {player_action: 0. for player_action in PlayerAction} slow_base_state = self.playerState.copy() slow_base_state.do_action(PlayerAction.SLOW_DOWN) slow_next_state = slow_base_state.do_move() if slow_next_state.verify_move(self.board): slow_force[PlayerAction.SLOW_DOWN] = 1. # calculate weighted evaluation for each possible action print(f"\t\t\treachable:\t\t{reachable_points}") print(f"\t\t\tcutting:\t\t{cutting_distribution}") print(f"\t\t\tfill:\t\t\t{fill_distribution}") print(f"\t\t\tslow:\t\t\t{slow_force}") weighted_action_evaluation = { action: reachable_points[action] * self.REACHABLE_POINT_WEIGHT + cutting_distribution[action] * self.CUTTING_WEIGHT + fill_distribution[action] * self.FILL_WEIGHT + slow_force[action] * self.SLOW_FORCE_WEIGHT for action in PlayerAction } print(f"\t\t\tevaluation:\t\t{weighted_action_evaluation}") # chose action based of highest value action = max(weighted_action_evaluation, key=weighted_action_evaluation.get) # add reachable points to viewer selected_reachable_points = full_range_results[action] slice_viewer.add_data("full_range_probability", selected_reachable_points, normalize=False) # apply action to local model self.playerState.do_action(action) self.playerState = self.playerState.do_move() return action
def handle_step(self, step_info, slice_viewer): self.roundCounter += 1 own_player = step_info["players"][str(step_info["you"])] # init on first step info if self.roundCounter == 1: self.board = Board(step_info["width"], step_info["height"]) self.playerState = PlayerState(PlayerDirection[own_player["direction"].upper()], own_player["speed"], own_player["x"], own_player["y"], self.roundCounter) # update cells self.board.cells = step_info["cells"] # build enemy player states enemy_player_states = [] for player_id, player in step_info["players"].items(): if str(step_info["you"]) != player_id and player["active"]: enemy_player_states.append( PlayerState( PlayerDirection[player["direction"].upper()], player["speed"], player["x"], player["y"], self.roundCounter)) # calculate enemy prediction enemy_probabilities, enemy_min_steps = \ probability_based_prediction.calculate_probabilities_for_players(self.board, enemy_player_states, depth=15, probability_cutoff=0.001) # add enemy prediction to viewer slice_viewer.add_data("enemy_probability", enemy_probabilities, normalize=False) slice_viewer.add_data("enemy_min_steps", enemy_min_steps, normalize=True) # add safe_area sizes to viewer safe_areas, safe_area_labels = get_risk_evaluated_safe_areas(self.board) safe_area_sizes = np.zeros(safe_area_labels.shape) for area in safe_areas: for point in area.points: safe_area_sizes[point[1], point[0]] = area.risk slice_viewer.add_data("safe_area_sizes", safe_area_sizes, normalize=False) start_time = time.time() # add risk_area to viewer slice_viewer.add_data("risk_evaluation", risk_area_calculation.calculate_risk_areas(self.board), normalize=False) # determine amount of reachable points for each action pool_input_array = [(player_action, enemy_probabilities, enemy_min_steps) for player_action in PlayerAction] pool = mp.Pool(mp.cpu_count()) weighted_points_results = pool.starmap(self.get_weighted_points_for_action, pool_input_array) pool.close() action_rating = {} action_weight_mapping = {} for idx, weighted_points_result in enumerate(weighted_points_results): action_weight_mapping[pool_input_array[idx][0]] = weighted_points_result action_rating[pool_input_array[idx][0]] = np.sum(weighted_points_result) # chose action based of highest value action = max(action_rating, key=action_rating.get) # add weighted points to viewer slice_viewer.add_data("weighted_points", action_weight_mapping[action], normalize=True) # apply action to local model self.playerState.do_action(action) self.playerState = self.playerState.do_move() return action
def get_risk_pattern(neighbors_risk_clockwise: List[float]): neighbors_sorted = neighbors_risk_clockwise.copy() neighbors_sorted.sort() neighbor_differences = [(neighbors_sorted[i], neighbors_sorted[i + 1] - neighbors_sorted[i]) for i in range(0, len(neighbors_sorted) - 1)] low_limit = max(neighbor_differences, key=lambda difference_tuple: difference_tuple[1]) if low_limit[0] == 1: return RiskPattern.Surrounded actual_pattern = ''.join([ 'H' if neighbor > low_limit[0] else 'L' for neighbor in neighbors_risk_clockwise ]) for pattern in RiskPattern: if actual_pattern in pattern.value[0] * 2: return pattern return RiskPattern.Surrounded if __name__ == "__main__": # Test Data b = Board(25, 25) print(calculate_risk_areas(b))
def handle_step(self, step_info, slice_viewer): new_occupied_cells = self.enemies.update(step_info) self.roundCounter += 1 own_player = step_info["players"][str(step_info["you"])] # init on first step info if self.roundCounter == 1: self.board = Board(step_info["width"], step_info["height"]) self.playerState = PlayerState(PlayerDirection[own_player["direction"].upper()], own_player["speed"], own_player["x"], own_player["y"], self.roundCounter) # update cells self.board.cells = step_info["cells"] if self.full_range_result: # recycle the last full_range result new_enemies_occupied_cells = [cell for cell in new_occupied_cells if cell not in self.playerState.steps_to_this_point] new_full_range_result = update_full_range_result(self.playerState.game_round, self.playerState.get_position_tuple(), self.full_range_result, new_enemies_occupied_cells) new_path_options = [state for directions in new_full_range_result.values() for speeds in directions.values() for state in speeds.values()] # add new path options to viewer new_path_steps_array = np.zeros((step_info["height"], step_info["width"])) for option in new_path_options: x = option.position_x y = option.position_y current_value = new_path_steps_array[y, x] new_value = option.game_round - self.roundCounter new_path_steps_array[y, x] = new_value if current_value == 0 else min(current_value, new_value) slice_viewer.add_data("recycled_full_range_steps", new_path_steps_array) # calculate action self.full_range_result = no_risk_full_range.calculate_ranges_for_player(self.board, self.playerState, 8) path_options = [state for directions in self.full_range_result.values() for speeds in directions.values() for state in speeds.values()] if len(path_options) > 0: random_player_state_choice = random.choice(path_options) player_states = random_player_state_choice.previous + [random_player_state_choice] action = player_states[self.roundCounter - 1].action # random action if no way to survive else: action = random.choice(list(PlayerAction)) # add path options to viewer path_steps_array = np.zeros((step_info["height"], step_info["width"])) for option in path_options: x = option.position_x y = option.position_y current_value = path_steps_array[y, x] new_value = option.game_round - self.roundCounter path_steps_array[y, x] = new_value if current_value == 0 else min(current_value, new_value) slice_viewer.add_data("full_range_steps", path_steps_array) # apply action to local model self.playerState.do_action(action) self.playerState = self.playerState.do_move() return action
class LocalGameService: def __init__(self, width: int, height: int, player_count: int): self.board = Board(width, height) start_point_distance = self.board.cell_count // (player_count + 1) self.players = [] # Init Player y_start_positions = list(range(1, self.board.width - 1)) x_start_positions = list(range(1, self.board.height - 1)) del y_start_positions[::2] del x_start_positions[::2] start_positions = list( itertools.product(y_start_positions, x_start_positions)) random.shuffle(start_positions) for player_id in range(1, player_count + 1): start_cell = start_positions.pop() player = Player( player_id, PlayerState(random.choice(list(PlayerDirection)), 1, start_cell[0], start_cell[1])) self.board.set_cell(player.current_state.position_x, player.current_state.position_y, player.player_id) self.players.append(player) self.is_started = False self.deadline = None self.on_round_start = Event() self.all_player_moved = False # Starts the Game and sends first Notification to the Player def start(self): self.is_started = True self.__reset_deadline() self.__notify_player() self.__wait_and_end_round() # processes an User interaction def do_action(self, player: int, player_action): if not self.is_started: raise Exception('Game is not started') elif self.players[player - 1].next_action is None: self.players[player - 1].next_action = player_action else: self.players[player - 1].is_active = False if self.__is_running() and all( not p.is_active or p.next_action is not None for p in self.players): self.all_player_moved = True def __notify_player(self): self.on_round_start.notify( json.dumps({ "width": self.board.width, "height": self.board.height, "cells": self.board.cells, "players": { player.player_id: player.to_dict() for player in self.players }, "you": 1, "running": self.__is_running(), "deadline": self.deadline.replace(microsecond=0).isoformat("T") + "Z" })) def __reset_deadline(self): deadline_seconds = SIMULATION_DEADLINE if not deadline_seconds: # default deadline is 1 Day deadline_seconds = 60 * 60 * 24 self.deadline = datetime.utcnow() + timedelta(seconds=deadline_seconds) def __is_running(self) -> bool: return self.is_started and sum(p.is_active for p in self.players) > 1 # is running in extra thread: checks the deadline and ends round def __wait_and_end_round(self): while self.__is_running(): time.sleep(0.1) if self.all_player_moved or self.deadline and self.deadline < datetime.utcnow( ): self.__reset_deadline() for player in self.players: if player.is_active: player.do_action_and_move() for point in player.current_state.steps_to_this_point: self.board.set_cell(point[0], point[1], player.player_id) player.is_active &= player.current_state.verify_state( self.board) for player in self.players: if player.is_active: for point in player.current_state.steps_to_this_point: if self.board[point[1]][point[0]] == -1: player.is_active = False self.all_player_moved = False self.__notify_player()