Exemple #1
0
    def next_move(self, color, board):
        """next_move
        """
        depth, moves, best_move, scores, = self.depth, None, None, {}

        pid = Timer.get_pid(self.search)           # タイムアウト監視用のプロセスID
        Timer.set_deadline(pid, self.search._MIN)  # 探索クラスのタイムアウトを設定

        moves = board.get_legal_moves(color)
        while True:
            moves = self.selector.select_moves(color, board, moves, scores, depth)                          # 次の手の候補を選択
            moves = self.orderer.move_ordering(color=color, board=board, moves=moves, best_move=best_move)  # 次の手の候補を並び替え
            best_move, scores = self.search.get_best_move(color, board, moves, depth, pid)                  # 最善手を取得

            if Timer.is_timeout(pid):  # タイムアウト発生時、処理を抜ける
                break

            if self.limit and depth >= self.limit:  # 限界深さに到達時
                break

            depth += 1  # 読みの深さを増やす

        self.max_depth = depth  # 読んだ深さを記録

        return best_move
Exemple #2
0
    def next_move(self, color, board):
        """
        次の一手
        """
        pid = Timer.get_pid(self)  # タイムアウト監視用のプロセスID
        next_color = 'white' if color == 'black' else 'black'
        moves, max_score = {}, self._MIN

        # 打てる手の中から評価値の最も高い手を選ぶ
        legal_moves = board.get_legal_moves(color)
        for move in legal_moves:
            board.put_disc(color, *move)                                       # 一手打つ
            score = -self.get_score(next_color, board, self.depth-1, pid=pid)  # 評価値を取得
            board.undo()                                                       # 打った手を戻す

            if Timer.is_timeout(pid):       # タイムアウト発生時
                if max_score not in moves:  # 候補がない場合は現在の手を返す
                    return move
                break
            else:
                max_score = max(max_score, score)  # 最大値を選択
                if score not in moves:             # 次の候補を記憶
                    moves[score] = []
                moves[score].append(move)

        return random.choice(moves[max_score])  # 複数候補がある場合はランダムに選ぶ
Exemple #3
0
    def next_move(self, color, board):
        """
        次の一手
        """
        pid = Timer.get_pid(self)  # タイムアウト監視用のプロセスID

        if self.table.size != board.size:  # テーブルサイズの調整
            self.table.set_table(board.size)

        depth, best_move = self.depth, None

        while True:
            moves = list(board.get_legal_moves(color))

            # 前回の最善手を優先的に
            if best_move is not None:
                moves.remove(best_move)
                moves.insert(0, best_move)

            # 4隅はさらに優先的に調べる
            for corner in [(0, 0), (0, board.size - 1), (board.size - 1, 0),
                           (board.size - 1, board.size - 1)]:
                if corner in moves:
                    moves.remove(corner)
                    moves.insert(0, corner)

            best_move = super().get_best_move(color, board, moves, depth,
                                              pid)  # 最善手

            if Timer.is_timeout(pid):  # タイムアウト
                break

            depth += 1  # 次の読みの深さ

        return best_move
Exemple #4
0
    def get_best_move(self, color, board, moves, depth, pid=None):
        """
        最善手を選ぶ
        """
        best_move, alpha, beta, scores = None, self._MIN, self._MAX, {}

        if board.size == 8 and sys.maxsize == MAXSIZE64 and hasattr(
                board, '_black_bitboard'
        ) and not NegaScoutMethods.NEGASCOUT_SIZE8_64BIT_ERROR:
            return NegaScoutMethods.get_best_move(color, board, moves, alpha,
                                                  beta, depth, self.evaluator,
                                                  pid, self.timer,
                                                  self.measure)

        # 打てる手の中から評価値の最も高い手を選ぶ
        for move in moves:
            score = self.get_score(move, color, board, alpha, beta, depth, pid)
            scores[move] = score
            if Timer.is_timeout(pid):
                best_move = move if best_move is None else best_move
                break
            else:
                if score > alpha:  # 最善手を更新
                    alpha = score
                    best_move = move

        return best_move, scores
Exemple #5
0
    def get_score(self, color, board, depth):
        """
        評価値の取得
        """
        # ゲーム終了 or 最大深さに到達
        legal_moves_b = board.get_legal_moves('black')
        legal_moves_w = board.get_legal_moves('white')
        is_game_end = True if not legal_moves_b and not legal_moves_w else False

        if is_game_end or depth <= 0:
            return self.evaluate(color, board, legal_moves_b, legal_moves_w)

        # パスの場合
        legal_moves = legal_moves_b if color == 'black' else legal_moves_w
        next_color = 'white' if color == 'black' else 'black'

        if not legal_moves:
            return -self.get_score(next_color, board, depth)

        # 評価値を算出
        max_score = self._MIN

        for move in legal_moves:
            board.put_disc(color, *move)
            score = -self.get_score(next_color, board, depth - 1)
            board.undo()

            if Timer.is_timeout(self):
                break
            else:
                max_score = max(max_score, score)  # 最大値を選択

        return max_score
Exemple #6
0
    def _get_score(self, color, board, alpha, beta, depth, pid=None):
        """
        評価値の取得
        """
        # ゲーム終了 or 最大深さに到達
        legal_moves_b = board.get_legal_moves('black')
        legal_moves_w = board.get_legal_moves('white')
        is_game_end = True if not legal_moves_b and not legal_moves_w else False

        if is_game_end or depth <= 0:
            return self.evaluate(color, board, legal_moves_b, legal_moves_w)

        # パスの場合
        legal_moves = legal_moves_b if color == 'black' else legal_moves_w
        next_color = 'white' if color == 'black' else 'black'

        if not legal_moves:
            return -self._get_score(next_color, board, -beta, -alpha, depth)

        # 評価値を算出
        for move in legal_moves:
            board.put_disc(color, *move)
            score = -self._get_score(next_color, board, -beta, -alpha,
                                     depth - 1)
            board.undo()

            if Timer.is_timeout(pid):
                break
            else:
                alpha = max(alpha, score)  # 最大値を選択
                if alpha >= beta:  # 枝刈り
                    break

        return alpha
Exemple #7
0
 def next_move(self, color, board):
     """
     次の一手
     """
     pid = Timer.get_pid(self)  # タイムアウト監視用のプロセスID
     if board.size == 8 and sys.maxsize == MAXSIZE64 and hasattr(board, '_black_bitboard') and not EndGameMethods.ENDGAME_SIZE8_64BIT_ERROR:
         return EndGameMethods.next_move(color, board, self.depth, pid, self.timer, self.measure)
     return self.alphabeta_n.next_move(color, board)
Exemple #8
0
    def next_move(self, color, board):
        """
        次の一手
        """
        pid = Timer.get_pid(self)  # タイムアウト監視用のプロセスID
        moves = board.get_legal_moves(color)  # 手の候補
        best_move = self.get_best_move(color, board, moves, self.depth, pid)

        return best_move
Exemple #9
0
    def next_move(self, color, board):
        """
        次の一手
        """
        pid = Timer.get_pid(self)  # タイムアウト監視用のプロセスID
        moves = board.get_legal_moves(color)  # 手の候補を取得
        scores = [0 for _ in range(len(moves))]  # スコアの初期化

        for _ in range(self.count):
            for i, move in enumerate(moves):
                scores[i] += self._playout(color, board, move,
                                           pid=pid)  # この手を選んだ時の勝敗を取得

                if Timer.is_timeout(pid):
                    break
            if Timer.is_timeout(pid):
                break

        best_score = max(scores)  # ベストスコアを取得
        best_moves = [
            move for i, move in enumerate(moves) if scores[i] == best_score
        ]  # ベストスコアの手を選ぶ

        return random.choice(best_moves)  # 複数ある場合はランダムに選ぶ
Exemple #10
0
    def next_move(self, color, board):
        """
        次の一手
        """
        pid = Timer.get_pid(self)  # タイムアウト監視用のプロセスID

        if board.size == 8 and sys.maxsize == MAXSIZE64 and hasattr(
                board, '_black_bitboard'
        ) and not NegaScoutMethods.NEGASCOUT_SIZE8_64BIT_ERROR:
            return NegaScoutMethods.next_move(color, board, self._MIN,
                                              self._MAX, self.depth,
                                              self.evaluator, pid, self.timer,
                                              self.measure)

        moves = board.get_legal_moves(color)  # 手の候補
        best_move, _ = self.get_best_move(color, board, moves, self.depth, pid)

        return best_move
Exemple #11
0
    def get_score(self, color, board, depth, pid=None):
        """
        評価値の取得
        """
        # ゲーム終了 or 最大深さに到達
        legal_moves_b_bits = board.get_legal_moves_bits('black')
        legal_moves_w_bits = board.get_legal_moves_bits('white')
        is_game_end = True if not legal_moves_b_bits and not legal_moves_w_bits else False
        if is_game_end or depth <= 0:
            sign = 1 if color == 'black' else -1
            return self.evaluator.evaluate(color=color, board=board, possibility_b=board.get_bit_count(legal_moves_b_bits), possibility_w=board.get_bit_count(legal_moves_w_bits)) * sign  # noqa: E501

        # パスの場合
        legal_moves_bits = legal_moves_b_bits if color == 'black' else legal_moves_w_bits
        next_color = 'white' if color == 'black' else 'black'

        if not legal_moves_bits:
            return -self.get_score(next_color, board, depth, pid=pid)

        # 評価値を算出
        max_score = self._MIN
        size = board.size
        mask = 1 << ((size**2)-1)
        for y in range(size):
            skip = False
            for x in range(size):
                if legal_moves_bits & mask:
                    board.put_disc(color, x, y)
                    score = -self.get_score(next_color, board, depth-1, pid=pid)
                    board.undo()

                    if Timer.is_timeout(pid):
                        skip = True
                        break
                    else:
                        max_score = max(max_score, score)  # 最大値を選択

                mask >>= 1

            if skip:
                break

        return max_score
Exemple #12
0
    def get_best_move(self, color, board, moves, depth, pid=None):
        """
        最善手を選ぶ
        """
        best_move, alpha, beta = None, self._MIN, self._MAX

        # 打てる手の中から評価値の最も高い手を選ぶ
        for move in moves:
            score = self.get_score(move, color, board, alpha, beta, depth, pid)

            if Timer.is_timeout(pid):
                best_move = move if best_move is None else best_move
                break
            else:
                if score > alpha:  # 最善手を更新
                    alpha = score
                    best_move = move

        return best_move
Exemple #13
0
def _get_score(func, alphabeta, color, board, alpha, beta, depth, pid):
    """_get_score
    """
    # ゲーム終了 or 最大深さに到達
    legal_moves_b_bits = board.get_legal_moves_bits('black')
    legal_moves_w_bits = board.get_legal_moves_bits('white')
    is_game_end = True if not legal_moves_b_bits and not legal_moves_w_bits else False
    if is_game_end or depth <= 0:
        sign = 1 if color == 'black' else -1
        return alphabeta.evaluator.evaluate(color=color, board=board, possibility_b=board.get_bit_count(legal_moves_b_bits), possibility_w=board.get_bit_count(legal_moves_w_bits)) * sign  # noqa: E501

    # パスの場合
    legal_moves_bits = legal_moves_b_bits if color == 'black' else legal_moves_w_bits
    next_color = 'white' if color == 'black' else 'black'
    if not legal_moves_bits:
        return -func(func, alphabeta, next_color, board, -beta, -alpha, depth, pid)

    # 評価値を算出
    size = board.size
    mask = 1 << ((size**2)-1)
    for y in range(size):
        skip = False
        for x in range(size):
            if legal_moves_bits & mask:
                board.put_disc(color, x, y)
                score = -func(func, alphabeta, next_color, board, -beta, -alpha, depth-1, pid)
                board.undo()

                if Timer.is_timeout(pid):
                    return alpha

                alpha = max(alpha, score)  # 最大値を選択
                if alpha >= beta:  # 枝刈り
                    skip = True
                    break
            mask >>= 1

        if skip:
            break

    return alpha
Exemple #14
0
    def next_move(self, color, board):
        """next_move
        """
        pid = Timer.get_pid(self)  # タイムアウト監視用のプロセスID

        # select best move
        next_color = 'white' if color == 'black' else 'black'
        next_moves = {}
        best_score = self._MIN if color == 'black' else self._MAX
        legal_moves = board.get_legal_moves(color)
        for move in legal_moves:
            board.put_disc(color, *move)
            score = self.get_score(next_color, board, self.depth-1, pid=pid)
            board.undo()
            best_score = max(best_score, score) if color == 'black' else min(best_score, score)

            # memorize next moves
            if score not in next_moves:
                next_moves[score] = []
            next_moves[score].append(move)

        return random.choice(next_moves[best_score])  # random choice if many best scores
Exemple #15
0
def _get_score(func, negascout, color, board, alpha, beta, depth, pid):
    """_get_score
    """
    # ゲーム終了 or 最大深さに到達
    legal_moves_b_bits = board.get_legal_moves_bits('black')
    legal_moves_w_bits = board.get_legal_moves_bits('white')
    is_game_end = True if not legal_moves_b_bits and not legal_moves_w_bits else False
    sign = 1 if color == 'black' else -1
    if is_game_end or depth <= 0:
        return negascout.evaluator.evaluate(
            color=color,
            board=board,
            possibility_b=board.get_bit_count(legal_moves_b_bits),
            possibility_w=board.get_bit_count(
                legal_moves_w_bits)) * sign  # noqa: E501

    # パスの場合
    legal_moves_bits = legal_moves_b_bits if color == 'black' else legal_moves_w_bits
    next_color = 'white' if color == 'black' else 'black'

    if not legal_moves_bits:
        return -func(
            func, negascout, next_color, board, -beta, -alpha, depth, pid=pid)

    # 着手可能数に応じて手を並び替え
    tmp = []
    size = board.size
    mask = 1 << ((size**2) - 1)
    for y in range(size):
        for x in range(size):
            if legal_moves_bits & mask:
                board.put_disc(color, x, y)
                possibility_b = board.get_bit_count(
                    board.get_legal_moves_bits('black'))
                possibility_w = board.get_bit_count(
                    board.get_legal_moves_bits('white'))
                tmp += [((x, y), (possibility_b - possibility_w) * sign)]
                board.undo()
            mask >>= 1

    next_moves = [i[0] for i in sorted(tmp, reverse=True, key=lambda x: x[1])]

    # NegaScout法
    tmp, null_window, index = None, beta, 0
    for move in next_moves:
        if alpha < beta:
            board.put_disc(color, *move)
            tmp = -func(func,
                        negascout,
                        next_color,
                        board,
                        -null_window,
                        -alpha,
                        depth - 1,
                        pid=pid)
            board.undo()

            if alpha < tmp:
                if tmp <= null_window and index:
                    board.put_disc(color, *move)
                    alpha = -func(func,
                                  negascout,
                                  next_color,
                                  board,
                                  -beta,
                                  -tmp,
                                  depth - 1,
                                  pid=pid)
                    board.undo()

                    if Timer.is_timeout(pid):
                        return alpha
                else:
                    alpha = tmp

            null_window = alpha + 1
        else:
            break

        index += 1

    return alpha