繁体   English   中英

我如何在 Python 中使用 Stockfish,以便像 chess.com 中那样不断更新评估,而不是在给定的时间内计算?

[英]How can I use Stockfish in Python so that the evaluation is continuously updated like in chess.com, instead of computed for a given amount of time?

我在python中使用的是stockfish 3.23 package。为了得到国际象棋position的评价,我使用以下代码:

self.stockfish = Stockfish(path="stockfish\\stockfish", depth=18, parameters={"Threads": 2, "Minimum Thinking Time": 1000})
self.stockfish.set_fen_position(fen)
evaluationValue = self.stockfish.get_evaluation()['value']

这很好用。 但是,我希望 stockfish 不断评估 position,并在需要时给我当前评估,而不是等待评估结果的预定时间。

这可能吗?

非常感谢,约斯特

我假设解决它的一种方法是在从 1 到 maxDepth 的循环中进行调用,然后打印循环中每个深度的结果。

我不确定 Stockfish package 是如何工作的,但 Stockfish 使用某种迭代加深,这意味着如果它搜索深度 18,它将执行上述循环。 我只是不知道如何使用该库打印内置循环的结果,也许有比我建议的更好的方法。

在鳕鱼 package 中, get_evaluation function 通过评估当前 position 中的顶部移动来工作,分数是 centipawn 或 mate。 在评估时,鳕鱼将在每个深度 output 顶部移动,但 package 将等到评估完成。

我创建了一个添加generate_top_moves方法的拉取请求,该方法返回一个生成器,该生成器在每个深度产生 position 中的顶部移动。 这是想法,您可以在 PR 中阅读更多相关信息:


class TopMove:
    def __init__(self, line: str) -> None:
        splits = line.split(" ")
        pv_index = splits.index("pv")
        self.move = splits[pv_index + 1]
        self.line = splits[pv_index + 1 :]
        self.depth = int(splits[splits.index("depth") + 1])
        self.seldepth = int(splits[splits.index("seldepth") + 1])

        self.cp = None
        self.mate = None

        try:
            self.cp = int(splits[splits.index("cp") + 1])
        except ValueError:
            self.mate = int(splits[splits.index("mate") + 1])

    def dict(self) -> dict:
        return {
            "move": self.move,
            "depth": self.depth,
            "seldepth": self.seldepth,
            "line": self.line,
            "cp": self.cp,
            "mate": self.mate,
        }

    # compare if this move is better than the other move
    def __gt__(self, other: Stockfish.TopMove) -> bool:

        if other.mate is None:
            # this move is mate and the other is not
            if self.mate is not None:
                # a negative mate value is a losing move
                return self.mate < 0

            # both moves has no mate, compare the depth first than centipawn
            if self.depth == other.depth:
                if self.cp == other.cp:
                    return self.seldepth > other.seldepth
                else:
                    return self.cp > other.cp
            else:
                return self.depth > other.depth

        else:
            # both this move and other move is mate
            if self.mate is not None:
                # both losing move, which takes more moves is better
                # both winning move, which takes less move is better
                if (
                    self.mate < 0
                    and other.mate < 0
                    or self.mate > 0
                    and other.mate > 0
                ):
                    return self.mate < other.mate
                else:
                    # comparing a losing move with a winning move, positive mate score is winning
                    return self.mate > other.mate
            else:
                return other.mate < 0

    # the oposite of __gt__
    def __lt__(self, other: Stockfish.TopMove) -> bool:
        return not self.__gt__(other)

    # equal move, by "move", not by score/evaluation
    def __eq__(self, other: Stockfish.TopMove) -> bool:
        return self.move == other.move

def generate_top_moves(
    self, num_top_moves: int = 5
) -> Generator[List[TopMove], None, None]:
    """Returns a generator that yields top moves in the position at each depth

    Args:
        num_top_moves:
            The number of moves to return info on, assuming there are at least
            those many legal moves.

    Returns:
        A generator that yields top moves in the position at each depth.

        The evaluation could be stopped early by calling Generator.close();
            this however will take some time for stockfish to stop.

        Unlike `get_top_moves` - which returns a list of dict, this will yield
        a list of `Stockfish.TopMove` instead, and the score (cp/mate) is relative
        to which side is playing instead of absolute like `get_top_moves`.

        The score is either `cp` or `mate`; a higher `cp` is better, positive `mate`
        is winning and vice versa.

        If there are no moves in the position, an empty list is returned.
    """

    if num_top_moves <= 0:
        raise ValueError("num_top_moves is not a positive number.")

    old_MultiPV_value = self._parameters["MultiPV"]
    if num_top_moves != self._parameters["MultiPV"]:
        self._set_option("MultiPV", num_top_moves)
        self._parameters.update({"MultiPV": num_top_moves})

    foundBestMove = False

    try:
        self._go()

        top_moves: List[Stockfish.TopMove] = []
        current_depth = 1

        while True:
            line = self._read_line()

            if "multipv" in line and "depth" in line:
                move = Stockfish.TopMove(line)

                # try to find the move in the list, if it exists then update it, else append to the list
                try:
                    idx = top_moves.index(move)

                    # don't update if the new move has a smaller depth than the one in the list
                    if move.depth >= top_moves[idx].depth:
                        top_moves[idx] = move

                except ValueError:
                    top_moves.append(move)

                # yield the top moves once the current depth changed, the current depth might be smaller than the old depth
                if move.depth != current_depth:
                    current_depth = move.depth
                    top_moves.sort(reverse=True)
                    yield top_moves[:num_top_moves]

            elif line.startswith("bestmove"):
                foundBestMove = True
                best_move = line.split(" ")[1]

                
                # no more moves, the game is ended
                if best_move == "(none)":
                    yield []
                else:
                    # sort the list once again
                    top_moves.sort(reverse=True)

                    # if the move at index 0 is not the best move returned by stockfish
                    if best_move != top_moves[0].move:
                        for move in top_moves:
                            if best_move == move.move:
                                top_moves.remove(move)
                                top_moves.insert(0, move)
                                break
                        else:
                            raise ValueError(f"Stockfish returned the best move: {best_move}, but it's not in the list")
                        

                    yield top_moves[:num_top_moves]

                break

    except BaseException as e:
        raise e from e

    finally:
        # stockfish has not returned the best move, but the generator was signaled to close
        if not foundBestMove:
            self._put("stop")
            while not self._read_line().startswith("bestmove"):
                pass

        if old_MultiPV_value != self._parameters["MultiPV"]:
            self._set_option("MultiPV", old_MultiPV_value)
            self._parameters.update({"MultiPV": old_MultiPV_value})

要评估 position,您可以得到最上面的着法,那么得分将是最佳着法的matecp (centipawn):

for top_moves in stockfish.generate_top_moves():
    best_move = top_moves[0]
    print(f"Evaluation at depth {best_move.depth}: {best_move.cp}")

output为起始position:

Evaluation at depth 2: 141
Evaluation at depth 3: 127
Evaluation at depth 4: 77
Evaluation at depth 5: 70
Evaluation at depth 6: 69
Evaluation at depth 7: 77
Evaluation at depth 8: 77
Evaluation at depth 9: 83
Evaluation at depth 10: 83
Evaluation at depth 11: 63
Evaluation at depth 12: 63
Evaluation at depth 13: 70
Evaluation at depth 14: 56
Evaluation at depth 15: 56
Evaluation at depth 16: 56
Evaluation at depth 17: 56
Evaluation at depth 18: 49
Evaluation at depth 18: 49

加上这个简单的方法,你就可以做一些了不起的事情,左边的评估栏是在python中计算的:

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM