def spawn_food(self, board: BoardState, n): unoccupied_points = self.get_unoccupied_points(board=board) n = min(n, len(unoccupied_points)) if n > 0: point_indices = np.random.choice(len(unoccupied_points), size=n, replace=False) for i in range(n): food = Food(position=unoccupied_points[point_indices[i]]) board.add_food(food)
def feel_busy(self, board: BoardState, snake: Snake, grid_map: GridMap): possible_actions = snake.possible_actions() head = snake.get_head() if possible_actions is None: return None actions_without_obstacle = [] for action in possible_actions: next_field = head.advanced(action) object_at_field = grid_map.get_value_at_position(next_field) if board.is_out_of_bounds(next_field): continue if object_at_field == Occupant.Snake: continue actions_without_obstacle.append(action) if len(actions_without_obstacle) > 0: return np.random.choice(actions_without_obstacle) else: return None
def best_food_around_body(snake: Snake, board: BoardState) -> Tuple[int, Optional[Position]]: length = snake.get_length() health = snake.get_health() body = snake.get_body() foods = [] #Tuple[int, Position] position = 0 any_f = False for part in body: for direction in Direction: if board.is_occupied_by_food(part.advanced(direction)): diff = length - position add = diff, part foods.append(add) any_f = True position += 1 best_dist = 999999 best_food = None if any_f: for dist, food in foods: if (dist == snake.get_length()) and (health <= 1): return 1, food if dist < best_dist and dist <= health: if dist == health: return dist, food best_dist = dist best_food = food return best_dist, best_food else: return 0, None
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 snake_death_reason(snake: Snake, board: BoardState): ee = snake.elimination_event ec = ee.cause if ec == EliminatedCause.EliminatedByCollision: s = board.get_snake_by_id(ee.by) return "Ran into {}'s body".format(s.snake_name) elif ec == EliminatedCause.EliminatedByHeadToHeadCollision: s = board.get_snake_by_id(ee.by) return 'Lost head-to-head with {}'.format(s.snake_name) elif ec == EliminatedCause.EliminatedByOutOfBounds: return 'Moved out of bounds' elif ec == EliminatedCause.EliminatedByOutOfHealth: return 'Out of health' elif ec == EliminatedCause.EliminatedBySelfCollision: return 'Collided with itself' else: print('WARNING: unknown eliminated cause:', ec) return ''
def create_initial_board_state(self, width: int, height: int, snake_ids: List[str]): board: BoardState = BoardState(width=width, height=height) for snake_id in snake_ids: board.add_snake(Snake(snake_id=snake_id)) self.place_snakes(board) self.place_food(board) return board
def get_valid_actions(board: BoardState, possible_actions: List[Direction], snakes: List[Snake], my_snake: Snake, grid_map: GridMap[Occupant], avoid_food: bool) -> List[Direction]: my_head = my_snake.get_head() snake_tails = [] val_actions = [] forbidden_fields = [] for snake in snakes: if snake.snake_id != my_snake.snake_id: for direc in snake.possible_actions(): enemy_head = snake.get_head() forbidden_fields.append(enemy_head.advanced(direc)) if snake.health == 100: continue snake_tails.append(snake.get_tail()) for direction in possible_actions: next_position = my_head.advanced(direction) # avoid eating if my_snake.health > Params_ValidActions.FOOD_BOUNDARY and avoid_food and my_snake.get_length( ) > 5: if grid_map.get_value_at_position( next_position) is Occupant.Food: continue # outofbounds if board.is_out_of_bounds(next_position): continue # body crash -> ganze Gegner Schlange minus letzten Teil if grid_map.get_value_at_position( next_position ) is Occupant.Snake and next_position not in snake_tails: continue # if next_position in forbidden_fields: # continue val_actions.append(direction) if not val_actions: for direction in possible_actions: next_position = my_head.advanced(direction) # eat if its the only possible valid action if grid_map.get_value_at_position( next_position) is Occupant.Food: val_actions.append(direction) return val_actions
def place_food_fixed(self, board: BoardState): # Place 1 food within exactly 2 moves of each snake for s in board.snakes: snake_head = s.get_head() possible_food_locations = [ Position(x=snake_head.x - 1, y=snake_head.y - 1), Position(x=snake_head.x - 1, y=snake_head.y + 1), Position(x=snake_head.x + 1, y=snake_head.y - 1), Position(x=snake_head.x + 1, y=snake_head.y + 1), ] available_food_locations = [] for p in possible_food_locations: if not board.is_occupied_by_food(p): available_food_locations.append(p) if len(available_food_locations) <= 0: raise ValueError('not enough space to place food') food_position = np.random.choice(available_food_locations) food = Food(position=food_position) board.add_food(food) # Finally, always place 1 food in center of board for dramatic purposes center_position = Position(x=int((board.width - 1) / 2), y=int((board.height - 1) / 2)) if board.is_occupied(center_position): raise ValueError('not enough space to place food') center_food = Food(position=center_position) board.add_food(center_food)
def test_clone(self): snake_a = Snake(snake_id="snake-a", body=[Position(x=3, y=3)]) snake_b = Snake(snake_id="snake-a", body=[Position(x=9, y=9)]) food_a = Food(x=1, y=2) snakes = [snake_a, snake_b] food = [food_a] board = BoardState(width=15, height=15, snakes=snakes, food=food) board_clone = board.clone() self.assertNotEqual(id(board), id(board_clone)) self.assertNotEqual(id(board.snakes[0]), id(board_clone.snakes[0])) self.assertNotEqual(id(board.food[0]), id(board_clone.food[0])) board_export = board.to_json() board_clone_export = board_clone.to_json() self.assertEqual(board_export, board_clone_export)
def move(self, game_info: GameInfo, turn: int, board: BoardState, you: Snake) -> MoveResult: possible_actions = you.possible_actions() if possible_actions is None: return None grid_map = board.generate_grid_map() busy_action = self.feel_busy(board, you, grid_map) if busy_action is not None: return MoveResult(direction=busy_action) random_action = np.random.choice(possible_actions) return MoveResult(direction=random_action)
def food_all_around_body(snake: Snake, board: BoardState) -> bool: body = snake.get_body() food_next_to_body = 0 for part in body: thispart = False for direction in Direction: if board.is_occupied_by_food(part.advanced(direction)): thispart = True if thispart: food_next_to_body += 1 if food_next_to_body == len(body): return True else: return False
def test_to_json(self): snake_a = Snake(snake_id="snake-a", body=[Position(x=3, y=3)]) snake_b = Snake(snake_id="snake-a", body=[Position(x=9, y=9)]) food_a = Food(x=1, y=2) hazard_a = Hazard(x=4, y=1) snakes = [snake_a, snake_b] food = [food_a] hazards = [hazard_a] board = BoardState(width=15, height=20, snakes=snakes, food=food, hazards=hazards) json_data = board.to_json() self.assertEqual(json_data['width'], 15) self.assertEqual(json_data['height'], 20) self.assertListEqual(json_data['food'], [{'x': 1, 'y': 2}]) self.assertListEqual(json_data['hazards'], [{'x': 4, 'y': 1}])
def display(self, board: BoardState): """ Zeigt das Spiel an :param board: Game Objekt, das angezeigt wird :return: """ # Der Hintergrund wird mit schwarz gefüllt self.screen.fill((0, 0, 0)) # Die Spieloberfläche wird gezeichnet self.render(board, self.surface_game) self.render_leaderboard(board, self.surface_leaderboard) game_position = (GAME_PADDING, GAME_PADDING) self.screen.blit(self.surface_game, game_position) # Das Leaderboard wird gezeichnet leaderboard_y_start = GAME_PADDING + max( (self.game_pixel_height - self.leaderboard_pixel_height) / 2, 0) leaderboard_position = (GAME_PADDING + self.game_pixel_width + GAME_PADDING, leaderboard_y_start) self.screen.blit(self.surface_leaderboard, leaderboard_position) # Wenn das Spiel gewonnen wurde, Info anzeigen if board.finished() and self.draw_winner_overlay: if len(board.snakes) == 1: snake = board.snakes[0] message = snake.snake_name + ' hat gewonnen' else: message = 'Unentschieden' font = pygame.font.Font(None, 40) text = font.render(message, True, (255, 255, 255)) text_rect = text.get_rect() text_x = self.screen.get_width() / 2 - text_rect.width / 2 text_y = self.screen.get_height() / 2 - text_rect.height / 2 self.screen.blit(text, [text_x, text_y]) # GUI aktualisieren pygame.display.flip()
def move(self, game_info: GameInfo, turn: int, board: BoardState, you: Snake) -> MoveResult: if you.latency: print("Latency", you.latency) start_time = time.time() grid_map: GridMap[Occupant] = board.generate_grid_map() self.Decision.set_round(turn) next_action = self.Decision.decide(you, board, grid_map) if not next_action: possible_actions = you.possible_actions() for action in possible_actions: next_position = you.get_head().advanced(action) for snake in board.snakes: if next_position == snake.get_tail() and snake.health != 100: next_action = action if not next_action: next_action = np.random.choice(possible_actions) print("Move-DAUER: ", time.time() - start_time) return MoveResult(direction=next_action)
def maybeFeedSnakes(self, board: BoardState): food_not_eaten = [] for f in board.food: food_has_been_eaten = False for snake in board.snakes: # Ignore eliminated and zero-length snakes, they can't eat. if not snake.is_alive() or len(snake.body) == 0: continue head = snake.get_head() if head.is_position_equal_to(f): self.feed_snake(snake) food_has_been_eaten = True if not food_has_been_eaten: food_not_eaten.append(f) board.food = food_not_eaten
def parse_board(json): height = json['height'] width = json['width'] food_json_list = json['food'] hazards_json_list = json['hazards'] snakes_json_list = json['snakes'] # kilab specific attribute dead_snakes_json_list = json[ 'dead_snakes'] if 'dead_snakes' in json else [] food = Importer.parse_food_array(food_json_list) hazards = Importer.parse_hazard_array(hazards_json_list) snakes = Importer.parse_snake_array(snakes_json_list) dead_snakes = Importer.parse_snake_array(dead_snakes_json_list) board = BoardState(width=width, height=height, food=food, hazards=hazards, snakes=snakes, dead_snakes=dead_snakes) return board
def food_around_head(head: Position, board: BoardState) -> bool: for direction in Direction: if not board.is_occupied(head.advanced(direction)): return False return True
def render(self, board: BoardState, surface: pygame.Surface): surface.fill(FieldColor.background) # draw alive snakes on top of dead snakes # snakes = sorted(board.all_snakes, key=lambda s: s.is_alive(), reverse=False) snakes = board.get_all_snakes_sorted() # Snakes zeichnen for snake in snakes: snake_id = snake.snake_id snake_body = snake.body snake_length = len(snake_body) snake_color = snake.snake_color is_alive = snake.is_alive() if not is_alive: snake_color = GameRenderer.mix_colors(snake_color, FieldColor.background, 0.5) directions = [] for i in range(snake_length - 1): body_cur = snake_body[i] body_last = snake_body[i + 1] d = DirectionUtil.direction_to_reach_field( from_position=body_last, to_position=body_cur) # body may have overlapping parts in the beginning if d is None: snake_length = i + 1 break directions.append(d) if len(directions) == 0: directions.append(Direction.UP) # copy last direction for tail directions.append(directions[len(directions) - 1]) for body_idx in range(snake_length): body_part = snake_body[body_idx] direction = directions[body_idx] x_min, y_min, x_max, y_max = body_part.x, body_part.y, body_part.x + 1, body_part.y + 1, if body_idx == 0: # Kopf zeichnen # Liste mit Pointen. Achtung im Format (x, y) # normal direction: Right pts = [[x_min, y_min], [x_max, y_min], [x_max, y_min + 0.1], [x_min + 0.5, y_min + 0.6], [x_max, y_max], [x_min, y_max]] pts = np.array(pts) center = np.array((x_min + 0.5, y_min + 0.5)) pts_pg = self.game_to_pixel_coordinates(pts) center_pg = self.game_to_pixel_coordinates(center) pts_pg = GameRenderer.rotate_points( direction, pts_pg, center_pg, y_inverted=self.y_inverted) pygame.draw.polygon(surface, snake_color, pts_pg) auge_center = (x_min + 0.2, y_min + 0.2) auge_center = np.array(auge_center) auge_center_pg = self.game_to_pixel_coordinates( auge_center) auge_radius = int(0.1 * self.pixel_per_field) auge_center_pg = GameRenderer.rotate_points( direction, auge_center_pg, center_pg, y_inverted=self.y_inverted).astype(np.int) pygame.draw.circle(surface, FieldColor.background, auge_center_pg, auge_radius) elif body_idx == snake_length - 1: # Hinterteil zeichnen # Liste mit Pointen. Achtung im Format (x, y) # normal direction: Right pts = [[x_max, y_max], [x_min + 0.6, y_max], [x_min + 0.3, y_min + 0.5], [x_min + 0.6, y_min], [x_max, y_min]] pts = np.array(pts) center = np.array((x_min + 0.5, y_min + 0.5)) pts_pg = self.game_to_pixel_coordinates(pts) center_pg = self.game_to_pixel_coordinates(center) pts_pg = GameRenderer.rotate_points( direction, pts_pg, center_pg, y_inverted=self.y_inverted) pygame.draw.polygon(surface, snake_color, pts_pg) else: pts = [[x_min, y_min], [x_max, y_max]] pts = np.array(pts) pts_pg = self.game_to_pixel_coordinates(pts) p_x_min = min(pts_pg[:, 0]) p_x_max = max(pts_pg[:, 0]) p_y_min = min(pts_pg[:, 1]) p_y_max = max(pts_pg[:, 1]) pygame.draw.rect( surface, snake_color, pygame.Rect(p_x_min, p_y_min, p_x_max - p_x_min, p_y_max - p_y_min), 0) # draw food for f in board.food: center = np.array([f.x + 0.5, f.y + 0.5]) radius_pg = int(0.8 * self.pixel_per_field / 2) center_pg = self.game_to_pixel_coordinates(center) pygame.draw.circle(surface, FieldColor.food, center_pg, radius_pg)
def render_leaderboard(self, board: BoardState, surface: pygame.Surface): """ Leaderboard zeichnen :param board: aktuelles Spiel :param surface: pygame surface (Fenster) :return: """ surface.fill((0, 0, 0)) surface_width = surface.get_width() snakes = board.get_all_snakes_sorted(reverse=True) text_color = (255, 255, 255) padding_content_left = 20 for i in range(len(snakes)): snake = snakes[i] x_start = 0 y_start = i * LEADERBOARD_ITEM_HEIGHT snake_color = snake.snake_color snake_health = max(snake.health, 0) snake_text_color = text_color if snake.elimination_event is not None: snake_eliminated_cause = snake.elimination_event.cause snake_eliminated_cause_text = GameRenderer.snake_death_reason( snake, board=board ) if snake_eliminated_cause is not None else None else: snake_eliminated_cause_text = None if not snake.is_alive(): snake_text_color = GameRenderer.mix_colors( snake_text_color, FieldColor.background, 0.3) snake_color = GameRenderer.mix_colors(snake_color, FieldColor.background, 0.5) snake_latency_text = '{} ms'.format( int(1000 * snake.latency) if snake.latency is not None else '') textsurface_snake_name = self.myfont_name.render( snake.snake_name, True, snake_text_color) textsurface_snake_length = self.myfont_name.render( str(snake.get_length()), True, snake_text_color) textsurface_eliminated_cause = self.myfont_die_reason.render( snake_eliminated_cause_text, True, snake_text_color) textsurface_latency = self.myfont_latency.render( snake_latency_text, True, snake_text_color) textsurface_snake_length_rect = textsurface_snake_length.get_rect() textsurface_latency_rect = textsurface_latency.get_rect() surface.blit(textsurface_snake_name, (x_start + padding_content_left, y_start)) surface.blit(textsurface_snake_length, (x_start + surface_width - textsurface_snake_length_rect.width, y_start)) surface.blit(textsurface_latency, (x_start + surface_width - textsurface_latency_rect.width, y_start + 25)) eliminated_reason_y = y_start + 50 surface.blit(textsurface_eliminated_cause, (x_start + padding_content_left, eliminated_reason_y)) bar_y_start = y_start + 50 health_bar_height = 20 color_rect = pygame.Rect(x_start, y_start, 5, LEADERBOARD_ITEM_HEIGHT - 25) AAfilledRoundedRect(surface, color_rect, snake_color, 1.0) background_bar_color = GameRenderer.mix_colors( snake.snake_color, FieldColor.background, 0.5) background_bar_rect = pygame.Rect(x_start, bar_y_start, surface_width, health_bar_height) # pygame.draw.rect(surface, background_bar_color, background_bar_rect) # AAfilledRoundedRect(surface, background_bar_rect, background_bar_color, 1.0) # snake_health = 1 if snake.is_alive() and snake_health > 0: bar_rect = pygame.Rect( x_start + padding_content_left, bar_y_start, snake_health / 100 * (surface_width - padding_content_left), health_bar_height) # pygame.draw.rect(surface, snake_info.color, bar_rect) AAfilledRoundedRect(surface, bar_rect, snake.snake_color, 1.0)