示例#1
0
    def two_enemy_endgame(self, game_state, simulations, search_depth):
        enemy_stacks = len(game_state[self.other])

        for piece in game_state[self.player]:
            temp_game = game.Game(game_state)
            temp_game.boom((piece[1], piece[2]), self.player)
            if not temp_game.get_game_state()[self.player]:
                strategy = self.monte_carlo(game_state, simulations, search_depth)
                return strategy
            if not temp_game.get_game_state()[self.other]:
                return None, (piece[1], piece[2]), "Boom", None

        # One stack
        if enemy_stacks == 1:
            enemy = game_state[self.other][0]
            enemy_xy = enemy[1], enemy[2]

            ally = self.closest_npiece(game_state, 2, self.player, enemy_xy)
            if ally is None:
                return self.make_stack(game_state)

            ally_xy = ally[1], ally[2]
            enemy_corner_xy = self.get_nearest_corner(ally_xy, enemy_xy)

            return self.go_there(2, ally, enemy_corner_xy)

        # Two seperate stacks
        else:
            return self.one_enemy_endgame(game_state, simulations, search_depth, two_enemy=True)
示例#2
0
    def one_enemy_endgame(self, game_state, simulations, search_depth, two_enemy=False):
        # If enemy can draw or we can win
        for piece in game_state[self.player]:
            home_b = features.count_pieces(game_state[self.player])
            temp_game = game.Game(game_state)
            temp_game.boom((piece[1], piece[2]), self.player)
            home_a = features.count_pieces(temp_game.get_game_state()[self.player])
            if not temp_game.get_game_state()[self.player] or (two_enemy and home_b-home_a >= 2):
                strategy = self.monte_carlo(game_state, simulations, search_depth)
                return strategy
            if not temp_game.get_game_state()[self.other]:
                return None, (piece[1], piece[2]), "Boom", None

        enemy = game_state[self.other][0]
        enemy_xy = enemy[1], enemy[2]

        ally = self.closest_npiece(game_state, 1, self.player, enemy_xy)
        ally_xy = ally[1], ally[2]

        # Close enough to boom
        if abs(enemy_xy[0] - ally_xy[0]) <= 1 and abs(enemy_xy[1] - ally_xy[1]) <= 1:
            return None, ally_xy, "Boom", None

        if two_enemy:
            enemy = game_state[self.other][1]
            enemy_xy = enemy[1], enemy[2]

            ally = self.closest_npiece(game_state, 1, self.player, enemy_xy)
            ally_xy = ally[1], ally[2]

            # Close enough to boom
            if abs(enemy_xy[0] - ally_xy[0]) <= 1 and abs(enemy_xy[1] - ally_xy[1]) <= 1:
                return None, ally_xy, "Boom", None

        return self.go_there(1, ally, enemy_xy)
示例#3
0
    def rollout_policy(self, node):

        game_state = node.data
        player = node.player
        temp_game = game.Game(game_state)

        # Assume opponent is random
        if player != self.player:
            available_moves = tokens.available_moves(player)
            pieces = game_state[player]

            strategy = None
            has_valid_move = False

            while not has_valid_move:
                move = random.choice(available_moves)
                piece = random.choice(pieces)
                xy = piece[1], piece[2]

                if move == "Boom":
                    has_valid_move = True

                    temp_game.boom(xy, player)
                    strategy = [(None, xy, move, None), temp_game.get_game_state()]

                else:
                    distance = random.randint(1, piece[0])
                    amount = random.randint(1, piece[0])

                    if temp_game.is_valid_move(xy, move, distance, player):
                        has_valid_move = True

                        temp_game.move_token(amount, xy, move, distance, player)
                        strategy = [(amount, xy, move, distance), temp_game.get_game_state()]

            temp_node = self.Node(self, strategy, game.other_player(player), None)

        # Stick to the plan
        else:
            available = self.available_states(game_state, player)
            strategy = random.choice(available)
            temp_node = self.Node(self, strategy, game.other_player(player), None)

        return temp_node
示例#4
0
    def available_states(self, game_state, player):
        available = []
        all_available = []
        other = game.other_player(player)

        for piece in game_state[player]:
            xy = piece[1], piece[2]
            available_moves = tokens.available_moves(player)

            for move in available_moves:
                if move == "Boom":
                    temp_game = game.Game(game_state)

                    if not temp_game.has_surrounding(xy):
                        continue

                    temp_game.boom(xy, player)
                    temp_game_state = temp_game.get_game_state()

                    all_available.append([(None, xy, move, None), temp_game.get_game_state()])

                    # If current number of home pieces <= current number of away pieces
                    if features.count_pieces(game_state[player]) < features.count_pieces(game_state[other]):
                        home_diff = features.count_pieces(game_state[player]) - features.count_pieces(temp_game_state[player])
                        away_diff = features.count_pieces(game_state[other]) - features.count_pieces(temp_game_state[other])

                        # Not worth it to trade for less
                        if home_diff >= away_diff:
                            continue

                    # If suicide for nothing
                    if features.count_pieces(game_state[other]) == features.count_pieces(temp_game_state[other]):
                        # Don't
                        continue

                    available.append([(None, xy, move, None), temp_game.get_game_state()])
                else:
                    if piece[0] == 1:
                        amounts = [1]
                    elif piece[0] == 2:
                        amounts = [1, 2]
                    else:
                        amount = min(piece[0], 8)
                        # Move whole stack or leave one or move one
                        amounts = [1, amount, amount - 1]

                    for n in amounts:
                        for distance in range(piece[0]):
                            distance = piece[0] - distance
                            temp_game = game.Game(game_state)
                            if temp_game.is_valid_move(xy, move, distance, player):
                                temp_game.move_token(n, xy, move, distance, player)
                                xy2 = game.dir_to_xy(xy, move, distance)

                                # We don't like a v pattern (inefficient move)
                                if self.creates_v(temp_game, xy2):
                                    continue

                                available.append([(n, xy, move, distance), temp_game.get_game_state()])

        if player != self.player or len(available) == 0:
            return all_available

        return available
示例#5
0
    def best_child(self, root):
        from math import sqrt

        game_state = root.data
        n_sim = float("-inf")

        can_boom = False
        best_boom_diff = float("-inf")
        best_strategy = None
        best_boom = None

        home_c = features.count_pieces(game_state[self.player])
        away_c = features.count_pieces(game_state[self.other])

        potential_threat = self.has_potential_threat(self.away_recently_moved, self.player)

        if potential_threat:
            temp_game = game.Game(game_state)
            temp_game.boom(self.away_recently_moved, self.other)
            temp_game_state = temp_game.get_game_state()
            home_t = features.count_pieces(temp_game_state[self.player])
            away_t = features.count_pieces(temp_game_state[self.other])
            potential_diff = home_t - away_t
            run_aways = []
            if (home_c > away_c and potential_diff > 0) or (home_c <= away_c and potential_diff >= 0):
                potential_threat = False

        for child in root.seen:
            strategy = child.move
            next_state = child.data

            if next_state[self.player] in self.past_states:
                continue

            # If won
            if not next_state[self.other] and next_state[self.player]:
                return strategy

            if strategy[2] != "Boom":
                xy = game.dir_to_xy(xy=strategy[1], direction=strategy[2], distance=strategy[3])

                damage = sqrt(features.pieces_per_boom(next_state, self.other))
                if damage == features.count_stacks(next_state):
                    continue

                if self.has_potential_threat(xy, self.other):
                    home_b = features.count_pieces(next_state[self.player])
                    away_b = features.count_pieces(next_state[self.other])

                    temp_game = game.Game(next_state)
                    temp_game.boom(xy, self.player)
                    temp_game_state = temp_game.get_game_state()

                    home_a = features.count_pieces(temp_game_state[self.player])
                    away_a = features.count_pieces(temp_game_state[self.other])

                    # IS dead
                    if home_a == 0:
                        continue
                    # Running into a trap
                    if (home_b - home_a) > (away_b - away_a):
                        continue

                if potential_threat:
                    # Losses
                    temp_game = game.Game(next_state)
                    temp_game.boom(self.away_recently_moved, self.other)
                    temp_game_state = temp_game.get_game_state()

                    home_l = features.count_pieces(temp_game_state[self.player])
                    away_l = features.count_pieces(temp_game_state[self.other])
                    loss = home_l - away_l

                    # Gains
                    temp_game1 = game.Game(next_state)
                    temp_game1.boom(xy, self.player)
                    temp_game_state = temp_game1.get_game_state()

                    home_g1 = features.count_pieces(temp_game_state[self.player])
                    away_g1 = features.count_pieces(temp_game_state[self.other])
                    gain1 = home_g1 - away_g1

                    gain2 = float("-inf")
                    # Leave one behind and boom there
                    if not temp_game1.board.is_cell_empty(strategy[1]):
                        temp_game2 = game.Game(next_state)
                        temp_game2.boom(strategy[1], self.player)
                        temp_game_state = temp_game2.get_game_state()

                        home_g2 = features.count_pieces(temp_game_state[self.player])
                        away_g2 = features.count_pieces(temp_game_state[self.other])
                        gain2 = home_g2 - away_g2

                    gain = max(gain1, gain2)
                    if gain2 > gain1:
                        temp_game = temp_game2
                    else:
                        temp_game = temp_game1

                    # Same Cluster boomed or Moving to trade is not worth it
                    if gain <= loss:
                        continue
                    else:
                        trade_game = temp_game
                        trade_game.boom(self.away_recently_moved, self.other)
                        trade_game_state = trade_game.get_game_state()

                        home_o = features.count_pieces(trade_game_state[self.player])
                        away_o = features.count_pieces(trade_game_state[self.other])
                        outcome = home_o - away_o

                        run_aways.append((outcome, strategy))

                    # Minimise Losses
                    if loss > potential_diff:
                        run_aways.append((loss, strategy))

            # If can trade for more
            if strategy[2] == "Boom":
                home_a = features.count_pieces(next_state[self.player])
                away_a = features.count_pieces(next_state[self.other])
                diff = home_a - away_a

                if home_a == 0:
                    continue

                if (home_c > away_c and diff > 0) or (home_c <= away_c and diff >= 0):
                    if diff > best_boom_diff:
                        best_boom_diff = diff
                        best_boom = strategy
                        can_boom = True
                else:
                    continue

            if child.n > n_sim:
                n_sim = child.n
                best_strategy = strategy

        if potential_threat:
            best_move_diff = float("-inf")

            # Run away
            if len(run_aways) != 0:
                run_aways = sorted(run_aways, reverse=True)
                best_move = run_aways[0][1]
                best_move_diff = run_aways[0][0]

            # Trade first
            if best_boom_diff > potential_diff and can_boom:
                if best_move_diff > best_boom_diff:
                    return best_move
                return best_boom

        if can_boom:
            return best_boom

        # If nothing is good
        if best_strategy is None:
            print("lose")
            # We lose anyways
            moves = self.available_states(game_state, self.player)
            return random.choice(moves)[0]

        return best_strategy