Exemple #1
0
def area_descriptor(area: Area, board: Board):
    neighborhood = area.get_adjacent_areas()
    player_name = area.get_owner_name()
    enemy_areas = [
        adj for adj in neighborhood
        if board.get_area(adj).get_owner_name() != player_name
    ]
    owned_areas = [
        adj for adj in neighborhood
        if board.get_area(adj).get_owner_name() == player_name
    ]
    unique_enemies = {
        board.get_area(adj).get_owner_name()
        for adj in neighborhood
    }
    if player_name in unique_enemies:
        unique_enemies.remove(player_name)
    max_region_size = max(
        len(r) for r in board.get_players_regions(player_name))

    feature_vector = [
        survival_prob(area, board),
        rel_area_power(area, board),
        len(enemy_areas),
        len(owned_areas),
        len(unique_enemies), max_region_size / len(board.areas)
    ]
    return np.asarray(feature_vector, dtype=np.float32)
    def _heuristic(self, players: List[int], board: Board) -> Dict[int, float]:
        """
        Computes the heuristic function for all the given players.

        :param players: Names of players.
        :param board: The game board.
        :return: A dictionary where keys are names of given players and values
                 are values of the heuristic function for these players in a
                 current game board.
        """
        h = {}
        for player in players:
            h[player] = board.get_player_dice(player)

            players_regions = board.get_players_regions(player)
            players_regions_sizes = []
            for region in players_regions:
                region_size = len(region)
                h[player] += self.__REG_HEURISTIC_WEIGHT * region_size
                players_regions_sizes.append(region_size)

            h[player] += \
                self.__LARGEST_REG_HEURISTIC_WEIGHT * max(players_regions_sizes)

        return h
Exemple #3
0
def possible_attacks(board: Board, player_name: int):
    for source in board.get_player_border(player_name):
        if not source.can_attack():
            continue
        for adj in source.get_adjacent_areas():
            target = board.get_area(adj)
            if target.get_owner_name() != player_name:
                succ_prob = ATTACK_SUCC_PROBS[source.get_dice()][
                    target.get_dice()]
                yield source, target, succ_prob
Exemple #4
0
def possible_attacks(board: Board,
                     player_name: int) -> Iterator[Tuple[int, int]]:
    for area in board.get_player_border(player_name):
        if not area.can_attack():
            continue

        neighbours = area.get_adjacent_areas()

        for adj in neighbours:
            adjacent_area = board.get_area(adj)
            if adjacent_area.get_owner_name() != player_name:
                yield (area, adjacent_area)
Exemple #5
0
def path_heuristics(board: Board, path: list) -> float:
    """
        Heuristika = součet všech dílčích heuristik v cestě
    """
    if len(path) < 2:
        return 0

    with simulate_battle(board.get_area(path[0]), board.get_area(path[1])):
        h = path_heuristics(board, path[1:])

    h += battle_heuristic(board, board.get_area(path[0]), board.get_area(path[1]))

    return h
Exemple #6
0
def game_descriptor(board: Board, player_name: int, players: list):
    areas = board.get_player_areas(player_name)
    regions = board.get_players_regions(player_name)
    border = board.get_player_border(player_name)
    max_region_size = max(len(r) for r in regions)
    rel_border_size_1 = len(border) / sum(
        len(board.get_player_border(name)) for name in players)
    rel_border_size_2 = sum(a.get_dice() for a in border) / sum(a.get_dice()
                                                                for a in areas)
    rel_area_size = len(areas) / sum(
        len(board.get_player_areas(name)) for name in players)

    best_border = []
    for r in regions:
        if len(r) == max_region_size:
            for area in map(lambda a: board.get_area(a), r):
                if board.is_at_border(area):
                    best_border.append(area)

    feature_vector = [
        max_region_size / len(board.areas), rel_border_size_1,
        rel_border_size_2, rel_area_size,
        np.mean([survival_prob(a, board) for a in best_border]),
        np.mean([rel_area_power(a, board) for a in areas])
    ]
    return np.asarray(feature_vector, dtype=np.float32)
Exemple #7
0
def get_attackable(board: Board, active_area: Area):
    neighbors = active_area.get_adjacent_areas()
    attackable = []
    for nb in neighbors:
        a = board.get_area(nb)
        if active_area.get_owner_name() != a.get_owner_name():
            attackable.append(a)

    return attackable
Exemple #8
0
def survival_prob(target: Area, board: Board):
    prob = 1.
    for adj in target.get_adjacent_areas():
        source = board.get_area(adj)
        if source.get_owner_name() == target.get_owner_name(
        ) or not source.can_attack():
            continue
        prob *= 1. - ATTACK_SUCC_PROBS[source.get_dice()][target.get_dice()]
    return prob
Exemple #9
0
def rel_area_power(area: Area, board: Board):
    player_power = area.get_dice()
    total_power = player_power
    for adj in area.get_adjacent_areas():
        adj_area = board.get_area(adj)
        dice = adj_area.get_dice()
        if adj_area.get_owner_name() == area.get_owner_name():
            player_power += dice
        total_power += dice
    return player_power / total_power
Exemple #10
0
def add_dice_to_player(board: Board, player_name: int):
    """Vytvoří dočasné herní pole s náhodně přidělenými kostkami pro vybraného
    hráče. Základní logika předělování kostek je převzatá z implemenetace hry
    viz dicewars/server/game.py line:230.

    Funkce se používá stejně jako funkce simulate_battle.

    Args
    ----
        board (Board): Aktuální stav herního pole
        player_name (int): Jméno hráče, kterému se mají přiřadit kostky
    """

    affected_areas: Dict[int, int] = {}
    dice = 0
    regions = board.get_players_regions(player_name)
    for region in regions:
        dice = max(dice, len(region))

    areas: List[Area] = []
    for area in board.get_player_areas(player_name):
        areas.append(area)

    if dice > 64:
        dice = 64

    while dice and areas:
        area = random.choice(areas)
        if area.dice >= 8:
            areas.remove(area)
        else:
            if area.name not in affected_areas:
                affected_areas[area.name] = area.dice
            area.dice += 1
            dice -= 1

    try:
        yield
    finally:
        # Vrátit herní pole do původního stavu
        for name in affected_areas:
            board.get_area(name).dice = affected_areas[name]
    def __largest_region(player_name: int, board: Board) -> List[int]:
        """
        Returns the largest region of a given player.

        :param player_name: A name (ID) of the associated player.
        :param board: The game board.
        :return: The largest region of a given player. (A list of area
                 names (IDs) in the largest region.)
        """
        players_regions = board.get_players_regions(player_name)
        max_region_size = max(len(r) for r in players_regions)

        return [r for r in players_regions if len(r) == max_region_size][0]
    def __perform_atack(board: Board, a_name: int, b_name: int) -> None:
        """
        Performes a successful atack.

        :param board: The game board.
        :param a_name: A name of an atacker area.
        :param b_name: A name of a defender area.
        """
        a = board.get_area(a_name)
        b = board.get_area(b_name)

        b.set_dice(a.get_dice() - 1)
        a.set_dice(1)
        b.set_owner(a_name)
Exemple #13
0
def player_heuristic(board: Board, player_name: int) -> int:
    """Výpočet heuristiky pro hráče podle aktuálního stavu herní plochy.
    koeficient = počet_polí_hlavního_území

    Args
    ----
        board (Board): Aktuální stav herní plochy
        player_name (int): Jméno hráče

    Returns
    -------
        int: Výsledná hodnota heuristiky. Větší znamená lepší
    """

    score = 0
    regions = board.get_players_regions(player_name)
    for region in regions:
        score = max(score, len(region))
    return score
    def __possible_turns(self, player_name: int,
                         board: Board) -> List[Tuple[int, int]]:
        """
        Returns possible turns with higher hold probability than a trashold.
        (The single turn expectiminimax.)

        :param player_name: A name (ID) of the associated player.
        :param board: The game board.
        :return: Possible turns with higher hold probability than a trashold.
                 (A list of tuples (atacker area; defender area) sorted by
                 a weight in descending order.)
        """
        if board.nb_players_alive() > self.__ATK_PLAYERS_LIMIT_2:
            atk_prob_treshold = self.__ATK_PROB_TRESHOLD_2
            atk_from_larges_reg_weight = self.__ATK_FROM_LARGEST_REG_WEIGHT_2
        else:
            atk_prob_treshold = self.__ATK_PROB_TRESHOLD
            atk_from_larges_reg_weight = self.__ATK_FROM_LARGEST_REG_WEIGHT

        turns = []
        largest_region = self.__largest_region(player_name, board)

        for a, b in possible_attacks(board, player_name):
            a_name = a.get_name()
            b_name = b.get_name()
            atk_power = a.get_dice()

            p = probability_of_successful_attack(board, a_name, b_name)
            p *= probability_of_holding_area(board, b_name, atk_power - 1,
                                             player_name)
            if p >= atk_prob_treshold or atk_power == self.__MAX_DICE:
                if a_name in largest_region:
                    p *= atk_from_larges_reg_weight
                turns.append((a_name, b_name, p))

        turns = sorted(turns, key=lambda t: t[2], reverse=True)
        turns = list(map(lambda t: t[:2], turns))

        return turns
    def __init__(self, player_name: int, board: Board,
                 players_order: List[int]) -> None:
        """
        Constructs the AI agent for the Dice Wars game.

        :param player_name: A name (ID) of the associated player.
        :param board: A copy of the game board.
        :param players_order: An order in which the players take turn. (A list
               of players IDs.)
        """
        super().__init__()

        self.__player_name = player_name
        self.__board = board
        self.__players_order = players_order
        self.__loger = getLogger(self.__LOGGER_NAME)

        self.__model = self.__load_model()

        nb_players = board.nb_players_alive()
        self.__loger.debug(
            f'An AI agent for the {nb_players}-player game is set up.'
            f' player_name: {player_name}; players_order: {players_order}')
    def __maxn_rec(
        self,
        board: Board,
        players_names: Deque[int],
        depth: int,
    ) -> Tuple[Union[Tuple[int, int], None], Dict[int, float]]:
        """
        The Max^n recursive algorithm. It uses the single turn expectiminimax
        for the computation of the best moves in the individual turns.

        :param board: A copy of the game board.
        :param players_names: A queue of players (names of players, i.e., IDs)
               to be considered in this algorithm. The player on the top of the
               queue is a currently associated player. After a player is
               processed, he is removed from the queue.
        :param depth: A depth of the algorithm. It declinines with recursive
               calls of this method.
        :return: A tuple where the first item is a tuple (atacker area,
                 defender area) with the best turn, or None in case there is
                 no suitable turn. And the second item of the tuple is
                 a dictionary where keys are names of players and values
                 are values of the heuristic function for these players in
                 a current game board.
        """
        # there are no more players
        if not players_names:
            # just evaluate the heuristic function for the AI's player
            return None, self._heuristic([self.__player_name], board)

        player_name = players_names[-1]
        # current player is not alive => skip him
        if not board.get_player_areas(player_name):
            players_names.pop()

            return self.__maxn_rec(deepcopy(board), players_names, depth)

        # depth is not 0 => expand lower nodes
        if depth:
            # expand some lower suitable nodes, according to the single turn
            # expectiminimax, the number of nodes to be expanded is decreasing
            # with a depth
            turns = self.__possible_turns(player_name, board)[:depth]
            if not turns:
                # no further moves possible => return just the heuristic
                # in a leaf node
                _, h = self.__maxn_rec(deepcopy(board), players_names, 0)

                return None, h

            h = {}
            turn = 0, 0
            for a, b in turns:
                board_copy = deepcopy(board)
                self.__perform_atack(board_copy, a, b)  # simulate an atack
                _, new_h = self.__maxn_rec(board_copy, players_names,
                                           depth - 1)

                # maximise the heuristic for a current player
                if player_name in new_h:
                    if player_name not in h \
                            or new_h[player_name] > h[player_name]:
                        h = deepcopy(new_h)
                        turn = a, b

            return turn if h else None, h

        # depth is 0 => advance to further players or compute the heuristic

        players_names.pop()
        # there are furthe players
        if players_names:
            return self.__maxn_rec(deepcopy(board), players_names,
                                   self.__MAXN_DEPTH)

        # compute the heuristic function for all living players
        living_players = set(a.get_owner_name() for a in board.areas.values())
        h = self._heuristic(list(living_players), board)

        return None, h
Exemple #17
0
def add_dice_to_player_worst_case(board: Board, player_name: int, is_yourself: bool, logger):
    affected_areas: Dict[int, int] = {}
    dice = 0
    regions = board.get_players_regions(player_name)
    for region in regions:
        dice = max(dice, len(region))

    if is_yourself:
        areas = board.get_player_areas(player_name)
        b_a = board.get_player_border(player_name)
        areas = list(set(areas)-set(b_a))
        if areas is None:
            areas = b_a
    else:
        areas =  board.get_player_border(player_name)

    arr = [a.get_name() for a in areas]
    logger.info("PLayer: {} with areas: {}".format(player_name, arr))

    if dice > 64:
        dice = 64

    while dice and areas:
        if is_yourself:
            area = random.choice(areas)
            while area.dice < 8:
                area.dice += 1
                dice -= 1
        else:
            area = areas.pop(0)
            areas.append(area)

        if area.dice >= 8:
            areas.remove(area)
        elif not is_yourself:
            if area.name not in affected_areas:
                affected_areas[area.name] = area.dice
            area.dice += 1
            dice -= 1

    if dice > 0:
        if is_yourself:
            areas = board.get_player_border(player_name)
        else:
            areas = board.get_player_areas(player_name)
        while dice and areas:
            area = random.choice(areas)

            if area.dice >= 8:
                areas.remove(area)
            else:
                if area.name not in affected_areas:
                    affected_areas[area.name] = area.dice
                area.dice += 1
                dice -= 1

    try:
        yield
    finally:
        # Vrátit herní pole do původního stavu
        for name in affected_areas:
            board.get_area(name).dice = affected_areas[name]