예제 #1
0
    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)
예제 #2
0
    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
예제 #3
0
    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
예제 #4
0
    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
예제 #5
0
    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 ''
예제 #6
0
    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
예제 #7
0
    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
예제 #8
0
    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)
예제 #9
0
    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)
예제 #10
0
    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)
예제 #11
0
    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
예제 #12
0
    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}])
예제 #13
0
    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()
예제 #14
0
    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)
예제 #15
0
    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
예제 #16
0
    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
예제 #17
0
 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
예제 #18
0
    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)
예제 #19
0
    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)