def get_relevant_food(my_snake: Snake, snakes: List[Snake], all_food: List[Position], height: int, width: int) -> List[Position]: my_head = my_snake.get_head() enemy_heads = [ snake.get_head() for snake in snakes if snake.get_head() is not my_head ] my_close_food = [] for food in all_food: if food.x == 0 or food.y == 0 or food.x == width - 1 or food.y == height - 1: enemy_dist_to_me = min([ Distance.manhattan_dist(head, my_head) for head in enemy_heads ]) enemy_dist_to_food = min([ Distance.manhattan_dist(head, food) for head in enemy_heads ]) if len(all_food) > 5 and my_snake.health > 50: continue if my_snake.health > 25 and enemy_dist_to_food < 2: continue if my_snake.health > 70 and enemy_dist_to_food < 2 and enemy_dist_to_me < 2: continue my_close_food.append(food) if not my_close_food: my_close_food = all_food return my_close_food
def update_enemy_state(self, longest_snake: int) -> None: # get path to food or head that fits best the performed actions most_prob_food_path = 99999 most_prob_head_path = 99999 if self.move_profile_predictions["food"]: most_prob_food_path = min([ Distance.path_similarity(f_profile, self.previous_positions) for f_profile in self.move_profile_predictions["food"] ]) if self.move_profile_predictions["head"]: most_prob_head_path = min([ Distance.path_similarity(h_profile, self.previous_positions) for h_profile in self.move_profile_predictions["head"] ]) hungry = 0 agressive = 0 length_diff = self.length_history[0] - self.length_history[-1] head_dist_avg = sum(self.distance_to_enemy_heads) / len( self.distance_to_enemy_heads) # estimate correct state of Snake from assigning points to behaviour or snake_parameters if most_prob_food_path < most_prob_head_path: hungry += 1 if most_prob_head_path < most_prob_food_path: agressive += 1 if self.snake.health < 20: hungry += 1 if self.snake.get_length() == longest_snake: agressive += 1 if length_diff > 1: hungry += 1 elif length_diff == 0: agressive += 1 if self.state == States.AGRESSIVE: agressive += 1 elif self.state == States.HUNGRY: hungry += 1 if head_dist_avg < 4: agressive += 1 elif head_dist_avg > 5: hungry += 1
def find_next_food(snake: Snake, board: BoardState) -> Optional[Tuple[int, Position]]: head = snake.get_head() health = snake.get_health() all_food = board.food if len(all_food) == 0: print("KEIN FOOD") return None best_dist = 99999 best_food = None for food in all_food: food_dist = Distance.manhattan_dist(head, food) if food_dist > health: continue #print("food_dist: ",food_dist, "health", health) if food_dist == health: # print("1: ", food_dist, food) return food_dist, food else: diff = (health - food_dist) #print("diff: ",diff) if (best_dist > diff) and diff > 0: best_dist = diff best_food = food #print("2: ", best_dist, best_food) return best_dist, best_food
def _update_automats(self, board: BoardState, kill_board: np.ndarray, grid_map: GridMap) -> None: snake_heads = [snake.get_head() for snake in board.snakes] # longest_snake = max([snake.get_length() for snake in board.snakes]) for index, snake in enumerate(board.snakes): automat = self.automats[snake.snake_id] automat.update_snake(snake) automat.add_position(snake.get_head()) automat.monitor_dist_to_enemies( Distance.dist_to_closest_enemy_head(board.snakes, snake)) automat.monitor_food(len(board.food)) self.states[snake.snake_id] = self.automats[snake.snake_id].state if self.game_round % self.update_frequency == 0 and snake.snake_id != self.my_snake_id: automat.monitor_length(snake.get_length()) enemy_snakes = board.snakes.copy() enemy_snakes.pop(index) enemy_heads = snake_heads.copy() enemy_heads.pop(index) if self.game_round != 0: pass # automat.update_enemy_state(longest_snake) # automat.make_movement_profile_prediction(enemy_snakes, enemy_heads, board, grid_map) # automat.update_behaviour() # update my snake state self.automats[self.my_snake_id].update_my_state( board, kill_board, grid_map)
def decide(self, you: Snake, board: BoardState, grid_map: GridMap) -> Direction: ######################### # SoloSurvival snakes = board.snakes if len(snakes) == 1 and snakes[0].snake_id == you.snake_id: return SoloSurvival.next_step(you, board, grid_map) # SoloSurvival ######################## if len(self.automats) != len(board.snakes): self._delete_dead_snake(board.dead_snakes) # init ValidActions object and get basic board for action_plan from multi_level valid_action = ValidActions(board, grid_map, you, self.states[self.my_snake_id]) valid_actions, self.action_board, direction_depth, kill_board = valid_action.multi_level_valid_actions( ) # decide if we focus on monitoring enemies or on calculating our next move dist_to_closest_head = Distance.dist_to_closest_enemy_head( board.snakes, you) if dist_to_closest_head < Params_Decision.CLOSEST_HEAD_BOUNDARY: self.monitoring_time = Params_Decision.REDUCED_MONITORING_TIME self._update_automats(board, kill_board, grid_map) next_action = self._call_strategy(you, board, grid_map, valid_actions, direction_depth) print("MyState:", self.automats[self.my_snake_id].get_state()) return next_action
def a_star_search_wofood( start_field: Position, search_field: Position, board: BoardState, grid_map: GridMap) -> Tuple[int, List[Tuple[Position, Direction]]]: queue = KLPriorityQueue() came_from = {} # current node ist key parent ist value cost_so_far = {str(start_field): 0} # summierte Kosten current_position = start_field first = True while not queue.empty() or first: first = False # Check if Current Position is goal state if current_position == search_field: break # append cost for each unvisited valid direction for direction in Direction: next_position = current_position.advanced(direction) if grid_map.is_valid_at(next_position.x, next_position.y) \ and grid_map.grid_cache[next_position.x][next_position.y] != Occupant.Snake\ and (not board.is_occupied_by_food(next_position) or (next_position == search_field)): # check if state wasnt visited or cost of visited state is lower if (str(next_position) not in cost_so_far.keys()) \ or (cost_so_far[str(current_position)] < cost_so_far[str(next_position)]): cost_so_far[str(next_position)] = cost_so_far[str( current_position)] + 1 cost = Distance.euclidean_distance( search_field, next_position) queue.put(cost, (next_position, direction)) # Get best position from Priority Queue pos_dir_tuple = queue.get() best_position = pos_dir_tuple[0] best_direction = pos_dir_tuple[1] # reverse direction to get poistion where we came from opposite_direction = AStar.reverse_direction(best_direction) # only use undiscovered nodes if best_position not in came_from: came_from[best_position] = ( best_position.advanced(opposite_direction), best_direction) current_position = best_position # Berechnung des Pfades anhand von came_from cost = cost_so_far[str(current_position)] path: List[Tuple[Position, Direction]] = [] while not current_position == start_field: path.append(came_from[current_position]) current_position = came_from[current_position][0] path = path[::-1] return cost, path
def get_food_profiles( head: Position, board: BoardState, grid_map: GridMap) -> List[List[Tuple[Position, Direction]]]: profiles = [] for food in board.food: if Distance.manhattan_dist(head, food) < 10: cost, path = AStar.a_star_search(head, food, board, grid_map) profiles.append(path) return profiles
def get_head_profiles(head: Position, enemy_heads: List[Position], board: BoardState, grid_map: GridMap) -> \ List[List[Tuple[Position, Direction]]]: profiles = [] for enemy_head in enemy_heads: if Distance.manhattan_dist(head, enemy_head) < 15: cost, path = AStar.a_star_search(head, enemy_head, board, grid_map) profiles.append(path) return profiles
def tail_gate(snake: Snake, board: BoardState, grid_map : GridMap) -> Direction: head = snake.get_head() tail = snake.get_tail() body = snake.get_body() distance = Distance.manhattan_dist(head, tail) if distance == 1: for direction in Direction: if head.advanced(direction) == tail: return direction else: dist = 9999 next_direction = None for direction in Direction: advanced_head = head.advanced(direction) d = Distance.manhattan_dist(advanced_head, tail) if advanced_head not in body: if d < dist: dist = d next_direction = direction return next_direction
def flood_kill(enemy: Snake, my_snake: Snake, kill_board: np.ndarray, board: BoardState, grid_map: GridMap): # TODO: Parameter snake_dead_in_rounds um mehr valide actions zu erhalten # - breite des PFades auf 1 begrenzen my_head = my_snake.get_head() enemy_head = enemy.get_head() kill_actions = [] search = False for part in enemy.body: kill_board[part.x][part.y] -= 1000 kill_board[my_head.x][my_head.y] -= 1000 x, y = np.unravel_index(kill_board.argmax(), kill_board.shape) print(kill_board) print(Position(x, y)) for (pos_x, pos_y) in get_valid_neigbours(x, y, kill_board): if 0 > kill_board[pos_x][pos_y] > -800: x, y = pos_x, pos_y search = True break enemy_dist = Distance.manhattan_dist(enemy_head, Position(x, y)) my_dist = Distance.manhattan_dist(my_head, Position(x, y)) if enemy_dist > my_dist and search: cost, path = AStar.a_star_search(my_head, Position(x, y), board, grid_map) count = 0 for pos, dir in path: if kill_board[pos.x][pos.y] >= 0: return [] kill_actions.append(dir) count += 1 return kill_actions
def _find_invalid_actions(self) -> List[Direction]: help_board = np.zeros((self.board.width, self.board.height)) head = self.my_snake.get_head() # mark snakes on the board self._mark_snakes(help_board) old_board = self.valid_board.copy() # print(self.valid_board) # calculate new wave for each depth level from queue for direction in self.valid_actions: next_position = head.advanced(direction) flood_queue = [(next_position.x, next_position.y)] visited = [(head.x, head.y)] for step in range(1, self.depth + 1): flood_queue, visited, _ = self._action_flood_fill(flood_queue, step, visited, None, enemy=False) if self.state != States.HUNGRY and self.my_snake.get_length() > 5: for food_pos in self.board.food: if Distance.manhattan_dist(head, food_pos) > 3: self.valid_board[food_pos.x][food_pos.y] = 1 # expand for each direction depth = self.expand(next_position, direction) print(self.valid_board) self.direction_depth[direction] = depth self.kill_board = np.add(self.kill_board, self.valid_board) self.valid_board = old_board.copy() self.kill_board = np.subtract( self.kill_board, old_board * (len(self.valid_actions) - 1)) invalid_actions = self._order_directions() return invalid_actions
def follow_food(snake: Snake, board: BoardState, grid_map: GridMap, reachable_food: List[Position]) \ -> List[Tuple[Position, Direction]]: my_head = snake.get_head() food = None relevant_foods = RelevantFood.get_relevant_food(snake, board.snakes, reachable_food, board.width, board.height) if not relevant_foods: return [] start_distance = 99 # get food that is nearest to my head for relevant_food in relevant_foods: distance = Distance.manhattan_dist(my_head, relevant_food) if distance < start_distance: food = relevant_food start_distance = distance print("best food:", food) if not food or start_distance > 15: return [] cost, path = AStar.a_star_search(my_head, food, board, grid_map) path_array = path return path_array
def avoid_enemy(my_snake: Snake, board: BoardState, grid_map: GridMap, valid_actions: List[Direction], action_plan: ActionPlan, direction_depth: Dict) -> Direction: if not valid_actions: possible_actions = my_snake.possible_actions() valid_actions = ValidActions.get_valid_actions(board, possible_actions, board.snakes, my_snake, grid_map, avoid_food=False) num_snakes = 4 - len(board.snakes) flood_dist = 6 if len(board.snakes) > 2 else 99 my_head = my_snake.get_head() enemy_heads = [ snake.get_head() for snake in board.snakes if snake.snake_id != my_snake.snake_id ] middle = Position(int(board.height / 2), int(board.width / 2)) corners = [ Position(0, 0), Position(0, board.width), Position(board.height, 0), Position(board.height, board.width) ] escape_cost_dict = action_plan.escape_lane(my_head, valid_actions) # TODO: Unterscheidung der Params in Late game und early game abhängig von anzahl der Schlangen p_head = Params_Anxious.ALPHA_DISTANCE_ENEMY_HEAD[num_snakes] * (-1) p_corner = Params_Anxious.BETA_DISTANCE_CORNERS[num_snakes] p_mid = Params_Anxious.THETA_DISTANCE_MID[num_snakes] * (-1) p_border = Params_Anxious.EPSILON_NO_BORDER[num_snakes] p_food = (Params_Anxious.GAMMA_DISTANCE_FOOD[num_snakes] * 20 / my_snake.health) p_flood_min = Params_Anxious.OMEGA_FLOOD_FILL_MIN[num_snakes] * (-1) p_flood_max = Params_Anxious.OMEGA_FLOOD_FILL_MAX[num_snakes] p_flood_dead = Params_Anxious.OMEGA_FLOOD_DEAD[num_snakes] p_corridor = Params_Anxious.RHO_ESCAPE_CORRIDOR[num_snakes] * (-1) p_length = Params_Anxious.TAU_PATH_LENGTH[num_snakes] total_cost = np.array([], dtype=np.float64) direction_cost = np.zeros(10) for action in valid_actions: next_position = my_head.advanced(action) # calculate flood fill flood_fill_value, relevant_food = FloodFill.get_fill_stats( board, next_position, my_snake.snake_id, new_pos=True) enemy_flood = sum([ flood_fill_value[snake.snake_id] for snake in board.snakes if snake.snake_id != my_snake.snake_id and Distance.manhattan_dist(snake.get_head(), my_head) < flood_dist ]) # calculate all costs for heuristics direction_cost[0] = direction_depth[action] * p_length direction_cost[1] = escape_cost_dict[action] * p_corridor direction_cost[2] = ActionPlan.punish_border_fields( next_position, my_head, grid_map.width, grid_map.height) * p_border direction_cost[3] = sum([ Distance.manhattan_dist(next_position, enemy_head) for enemy_head in enemy_heads ]) * p_head direction_cost[4] = sum([ Distance.manhattan_dist(next_position, corner) for corner in corners if Distance.manhattan_dist(next_position, corner) < 9 ]) * p_corner direction_cost[5] = Distance.manhattan_dist(next_position, middle) * p_mid direction_cost[6] = flood_fill_value[ my_snake.snake_id] * p_flood_max direction_cost[7] = enemy_flood * p_flood_min direction_cost[8] = len(relevant_food) * p_food # extra points for killing enemy snake if num_snakes == 1: enemy_id = [ snake.snake_id for snake in board.snakes if snake.snake_id != my_snake.snake_id ][0] if flood_fill_value[enemy_id] < 20: flood_kill_value = (20 - flood_fill_value[enemy_id]) * 1000 direction_cost[9] = flood_kill_value * p_flood_dead total_cost = np.append(total_cost, direction_cost.sum()) direction_cost = np.zeros(10) if valid_actions: best_action = valid_actions[int(np.argmax(total_cost))] print(best_action) else: best_action = None return best_action