Example #1
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)
Example #2
0
    def test_parse_snake(self):

        data = {
            "id": "snake-508e96ac-94ad-11ea-bb37",
            "name": "My Snake",
            "health": 54,
            "body": [
                {"x": 0, "y": 5},
                {"x": 1, "y": 5},
                {"x": 1, "y": 4}
            ],
            "latency": "111",
            "head": {"x": 0, "y": 0},
            "length": 3,
            "shout": "why are we shouting??",
            "squad": "A"
        }

        snake = Importer.parse_snake(data)

        self.assertEqual(snake.snake_id, "snake-508e96ac-94ad-11ea-bb37")
        self.assertEqual(snake.snake_name, "My Snake")
        self.assertEqual(snake.health, 54)
        self.assertEqual(len(snake.body), 3)
        self.assertEqual(snake.body[0], Position(x=0, y=5))
        self.assertEqual(snake.body[1], Position(x=1, y=5))
        self.assertEqual(snake.body[2], Position(x=1, y=4))
        self.assertEqual(snake.get_length(), 3)
        self.assertEqual(snake.latency, "111")
        self.assertEqual(snake.shout, "why are we shouting??")
        self.assertEqual(snake.squad, "A")
Example #3
0
    def neighbor_positions(pos: Position) -> List[Position]:

        return [
            Position(x=pos.x - 1, y=pos.y),
            Position(x=pos.x, y=pos.y - 1),
            Position(x=pos.x, y=pos.y + 1),
            Position(x=pos.x + 1, y=pos.y),
        ]
Example #4
0
    def _action_flood_fill(self, flood_queue: List, step: int, visited: List,
                           action_plan: np.ndarray, enemy: bool):
        x_size, y_size = (self.board.width, self.board.height)
        new_queue = []
        for (x, y) in flood_queue:

            if (x, y) in visited:
                continue

            if self.board.is_out_of_bounds(Position(x, y)):
                continue

            if enemy:
                if self.valid_board[x][y] + step < 0:
                    continue

                if self.valid_board[x][y] == 0 or step <= abs(
                        self.valid_board[x][y]):
                    self.valid_board[x][y] = step

                if step < 4:
                    action_plan[x][y] = Params_ValidActions.AREA_VALUE * (4 -
                                                                          step)

            if not enemy:
                if step < self.valid_board[x][y] < 20 or self.valid_board[x][
                        y] == 0:
                    self.valid_board[x][y] = -step

                # eigenenes Schwanzende berücksichtigen
                if 20 < self.valid_board[
                        x, y] < 40 and self.valid_board[x, y] % 20 <= step:
                    self.valid_board[x][y] = -step

                # Schwanzanfang berücksichtigen
                if step == 1:
                    for snake in self.board.snakes:
                        tail = snake.get_tail()
                        if snake.health != 100 and (x, y) == (
                                tail.x,
                                tail.y) and self.valid_board[x, y] != 1:
                            self.valid_board[x][y] = -step

            visited.append((x, y))

            # add next steps to queue
            if x > 0 and (x - 1, y) not in visited:
                new_queue.append((x - 1, y))
            if x < (x_size - 1) and (x + 1, y) not in visited:
                new_queue.append((x + 1, y))
            if y > 0 and (x, y - 1) not in visited:
                new_queue.append((x, y - 1))
            if y < (y_size - 1) and (x, y + 1) not in visited:
                new_queue.append((x, y + 1))

        return new_queue, visited, action_plan
Example #5
0
    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
Example #6
0
    def test_parse_request(self):

        with open(os.path.dirname(__file__) + '/data/request_1.json') as json_file:
            data = json.load(json_file)

        print(data)

        parsed = Importer.parse_request(data)
        print(parsed)

        game_info, turn, board, you = parsed

        self.assertEqual(game_info.id, "game-00fe20da-94ad-11ea-bb37")
        self.assertEqual(game_info.ruleset['name'], "standard")
        self.assertEqual(game_info.ruleset['version'], "v.1.2.3")
        self.assertEqual(game_info.timeout, 500)

        self.assertEqual(turn, 14)
        self.assertEqual(you.snake_id, "snake-508e96ac-94ad-11ea-bb37")
        self.assertEqual(you.snake_name, "My Snake")
        self.assertEqual(you.health, 54)
        self.assertEqual(you.body[1], Position(x=1, y=0))
        self.assertEqual(you.get_length(), 3)
        self.assertEqual(you.latency, "111")
        self.assertEqual(you.shout, "why are we shouting??")
        self.assertEqual(you.squad, "")

        self.assertEqual(board.height, 11)
        self.assertEqual(board.width, 11)
        self.assertEqual(len(board.snakes), 2)
        self.assertEqual(board.snakes[0].snake_id, "snake-508e96ac-94ad-11ea-bb37")
        self.assertEqual(board.snakes[1].snake_id, "snake-b67f4906-94ae-11ea-bb37")
        self.assertEqual(len(board.food), 3)
        self.assertEqual(board.food[0], Position(x=5, y=5))
        self.assertEqual(board.food[1], Position(x=9, y=0))
        self.assertEqual(board.food[2], Position(x=2, y=6))

        self.assertEqual(len(board.hazards), 1)
        self.assertEqual(board.hazards[0], Position(x=3, y=2))
Example #7
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)
Example #8
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}])
Example #9
0
    def get_unoccupied_points(self, board: BoardState) -> List[Position]:

        occupied: GridMap[bool] = GridMap(width=board.width, height=board.height)

        for f in board.food:
            occupied.set_value_at_position(f, True)

        for snake in board.snakes:
            for p in snake.body:
                occupied.set_value_at_position(p, True)

        unoccupied_points = []
        for y in range(board.height):
            for x in range(board.width):
                if not occupied.get_value_at(x=x, y=y):
                    unoccupied_points.append(Position(x=x, y=y))

        return unoccupied_points
Example #10
0
    def next_step(snake: Snake, board: BoardState, grid_map: GridMap) -> Direction:

        head = snake.get_head()
        #tail = snake.get_tail()
        middle = Position(board.height // 2, board.width // 2)
        valid = ValidActions.get_valid_actions(board, snake.possible_actions(), [snake], snake, grid_map, False)
        #for direction in Direction:
        #    if not board.is_out_of_bounds(head.advanced(direction)):
        #        if not (head.advanced(direction) in snake.get_body()):
        #            valid.append(direction)
        #if snake.get_length() > 10:
        #    for action in valid:
        #        _, path = AStar.a_star_search(head.advanced(action), tail, board, grid_map)
        #       print(len(path))
        #        end, pat = path[len(path)-1]
        #        if not end == tail:
        #            valid.remove(action)

        if middle in snake.get_body():
            need, next_direction = SoloSurvival.need_food(snake, board, grid_map, valid)
            if need:
                if next_direction in valid:
                    return next_direction
                else:
                    #print("not valid")
                    return np.random.choice(valid)
            else:
                next_direction = SoloSurvival.tail_gate(snake, board, grid_map)
                if next_direction in valid:
                    return next_direction
                else:
                    return np.random.choice(valid)

        else:
            if SoloSurvival.food_around_head(head, board):
                _, path = AStar.a_star_search(head, middle, board, grid_map)
                _, next_direction = path[0]
            else:
                _, path = AStar.a_star_search(head, middle, board, grid_map)
                _, next_direction = path[0]
        if next_direction in valid:
            return next_direction
        else:
            return np.random.choice(valid)
Example #11
0
    def place_snakes_fixed(self, board: BoardState):

        width = board.width

        starting_positions = [
            Position(1, 1),
            Position(width - 2, width - 2),
            Position(1, width - 2),
            Position(width - 2, 1),
            Position(int((width - 1) / 2), 1),
            Position(width - 2, int((width - 1) / 2)),
            Position(int((width - 1) / 2), width - 2),
            Position(1, int((width - 1) / 2))
        ]

        num_snakes = len(board.snakes)
        if num_snakes > len(starting_positions):
            raise ValueError('too many snakes for fixed start positions')

        # starting_positions = [starting_positions[i] for i in range(num_snakes)]
        random.shuffle(starting_positions)

        for i in range(num_snakes):
            board.snakes[i].set_initial_position(starting_positions[i], n=self.snake_start_size)
Example #12
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
Example #13
0
    def get_fill_stats(board: BoardState, next_position: Position, my_id: str,
                       new_pos: bool) -> Tuple[Dict, List[Position]]:
        # TODO: Food in der Gewichtung des boards einbeziehen

        flood_queue = []
        fill_stats = {}
        fill_board = np.full((board.width, board.height), 10)
        visited = []
        no_food = False if Position(next_position.x,
                                    next_position.y) in board.food else True

        snake_length = [snake.get_length() for snake in board.snakes]
        snake_ids = [snake.snake_id for snake in board.snakes]
        order = np.argsort(snake_length)

        copy_snakes = board.snakes.copy()
        snakes = [copy_snakes[i] for i in order][::-1]
        snake_ids = [snake_ids[i] for i in order][::-1]

        snake_marker = -99
        for snake in snakes:
            if snake.snake_id == my_id:
                flood_queue.append([(next_position.x, next_position.y)])
                if new_pos:
                    snake.body.insert(0, next_position)
                    if no_food:
                        del snake.body[-1]
                snake_marker = -50
            else:
                flood_queue.append([(snake.get_head().x, snake.get_head().y)])
            for pos in snake.body:
                fill_board[pos.x][pos.y] = snake_marker
                if pos is not snake.get_head():
                    visited.append((pos.x, pos.y))

            snake_marker += 1
            fill_stats[snake.snake_id] = 0

        # iterativ Bewegungsbereich erschließen
        my_index = 0
        count = 1
        while any(flood_queue):

            snake_index = 0

            # Tail iterativ freigeben
            idx = 0
            for snakey in snakes:
                if snakey.get_length() > count:
                    fill_board[snakey.body[-count].x][
                        snakey.body[-count].y] = 10
                    if (snakey.body[-count].x,
                            snakey.body[-count].y) in visited:
                        visited.remove(
                            (snakey.body[-count].x, snakey.body[-count].y))
                        # flood_queue[idx].append((snakey.body[-(count + 1)].x, snakey.body[-(count + 1)].y))
                idx += 1

            # größte Schlange zuerst
            for snake_id in snake_ids:
                filled_fields_count = FloodFill.calcuate_step(
                    fill_board, flood_queue, snake_index, visited)

                fill_stats[snake_id] += filled_fields_count

                if snake_id == my_id:
                    my_index = snake_index
                snake_index += 1
            count += 1

        reachable_food = FloodFill.flood_food(fill_board, board.food, my_index)
        return fill_stats, reachable_food
Example #14
0
    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
Example #15
0
    def parse_position(json):
        x = json['x']
        y = json['y']

        return Position(x=x, y=y)
Example #16
0
    def expand(self, next_position: Position, direction: Direction) -> int:

        step_history = []
        dead_ends = {}
        searching = True
        value = -1
        dead = False
        longest_way = 0
        food_count = 0

        # get first field of Direction and check if valid
        if self.valid_board[next_position.x][next_position.y] != value:
            return 0
        step_history.append((next_position.x, next_position.y))
        x_coord, y_coord = next_position.x, next_position.y

        while searching:
            positions = get_valid_neigbours(x_coord, y_coord, self.valid_board)

            for x, y in positions:

                # check if next value is valid and no dead end
                if self.valid_board[x][y] == value - 1 and (
                        x, y) not in dead_ends.keys():
                    if self.grid_map.grid_cache[x][y] == Occupant.Food:
                        food_count += 1
                    # TODO: Logik dass schlangen ende berücksichtigt wird
                    if Position(x,
                                y) == self.my_snake.get_tail() and food_count:
                        longest_way -= 1
                    dead = False
                    step_history.append((x, y))
                    x_coord, y_coord = x, y
                    value -= 1
                    break
                # mark dead ends
                else:
                    dead = True

                # break if a valid endnode was found
                if self.valid_board[x][
                        y] == 0:  # or self.valid_board[x][y] == -Params_ValidActions.DEPTH
                    searching = False
                    dead = False
                    break

            # check if dead end and no more possible nodes to explore
            if dead and not step_history:
                searching = False

            # update range for each direction
            if longest_way >= value:
                longest_way = value
                self.food_on_path_count[direction] = food_count

            # check if dead end but still valid nodes to explore
            if dead and step_history:
                dead_ends[(x_coord, y_coord)] = value
                (x_pos, y_pos) = step_history.pop(-1)

                if self.grid_map.grid_cache[x_pos][y_pos] == Occupant.Food:
                    food_count -= 1

                if step_history:
                    x_coord, y_coord = step_history[-1]
                value += 1

        return longest_way
Example #17
0
    if y + 1 < square.shape[1]:
        neighbour_fields.append((x, y + 1))
    if y - 1 >= 0:
        neighbour_fields.append((x, y - 1))

    return neighbour_fields


def count_border_fields(my_head: Position, board: np.ndarray) -> int:
    count = 0
    width, height = board.shape
    valid_neigbours = get_valid_neigbours(my_head.x, my_head.y, board)

    for (x, y) in valid_neigbours:
        if x == 0 or y == 0 or x == width - 1 or y == height - 1:
            count = 1

    return count


"""
self.board.snakes[0].body = [Position(2,3),Position(2,4),Position(2,5),Position(2,6),Position(2,7),Position(2,8), 
Position(2,9),Position(3,9),Position(4,9),Position(5,9),Position(6,9)]
self.board.snakes[2].body = [Position(0,6),Position(0,7),Position(0,8),Position(0,9),Position(0,10),Position(1,10), 
Position(2,10),Position(3,10),Position(4,10),Position(5,10)Position(6,10)Position(7,10)]
self.board.snakes[1].body = [Position(0,0),Position(0,1),Position(0,2),Position(0,3)]
self.board.snakes[1].body = [Position(1,3),Position(1,4),Position(1,5),Position(1,6)]
self.board.snakes[1].body = [Position(1,3),Position(1,4),Position(1,5),Position(0,5)]
self.board.snakes[1].body = [Position(1,3),Position(1,4),Position(1,5),Position(0,5),Position(0,6),
Position(0,7),Position(0,8)]
"""