为什么chess.engine在同一个脚本中多次分析同一个位置会有不同的分数?
Why are there different scores when chess.engine analyses a position multiple times in the same script?
此处为初学者,正在尝试制作一款可检测错误的应用,并希望了解有关 chess.engine 库的更多信息。
我假设使用分析函数是一个独立的、独立的过程,不依赖于引擎之前调用的缓存或内存或类似的东西。
如果是这样,为什么我在脚本中多次调用分析时得到多个不同的评估:
import chess
import chess.engine
import os
# Loads board and engine
board = chess.Board("3r3k/pp5p/n1p2rp1/2P2p1n/1P2p3/PBNqP2P/5PP1/R2RB1K1 w - - 0 26")
engine = chess.engine.SimpleEngine.popen_uci(os.getcwd()+'/static/'+'stockfish')
#Sets engine depth constant
engine_depth = 10
# I'm assuming this is how to find the best move - arbitrary as I could have chosen any move in board.legal_moves
best_move = engine.play(board, chess.engine.Limit(depth=engine_depth)).move
# I'm assuming this is how to evaluate the score after a specific move from this position
info = engine.analyse(board, chess.engine.Limit(depth=engine_depth), root_moves=[best_move])
print(info["score"])
# Repeating the analysis call and printing the score 3 more times
info = engine.analyse(board, chess.engine.Limit(depth=engine_depth), root_moves=[best_move])
print(info["score"])
info = engine.analyse(board, chess.engine.Limit(depth=engine_depth), root_moves=[best_move])
print(info["score"])
info = engine.analyse(board, chess.engine.Limit(depth=engine_depth), root_moves=[best_move])
print(info["score"])
engine.quit()
输出:
PovScore(Cp(-21), WHITE)
PovScore(Cp(+2), WHITE)
PovScore(Cp(+19), WHITE)
PovScore(Cp(+63), WHITE)
对于超过一定水平的各种引擎深度,它会发生,但不会在深度较低时发生,例如最多 3.
它以时间而不是深度为限制发生。
它发生在多个不同的起始位置。
它发生在多个不同的动作中。
即使我在每次调用之间有 engine.quit()
和 engine = chess.engine.SimpleEngine.popen_uci(os.getcwd()+'/static/'+'stockfish')
,它也会发生。
分析函数不能有随机元素,因为当我再次 运行 整个脚本时,我得到完全相同的分数。只是当它在同一个脚本中多次调用同一个位置时,它每次都会给出不同的分数,就好像它在使用某种缓存或者每次看起来更深。
那么我对它的工作原理的理解哪里出错了?
编辑:
如果我删除 root_moves
参数(只是为了简化事情)然后替换:
print(info["score"])
与:
for k, v in info.items():
print(k, v)
我得到以下输出:
string NNUE evaluation using nn-82215d0fd0df.nnue enabled
depth 10
seldepth 13
multipv 1
score PovScore(Cp(+25), WHITE)
nodes 4396
nps 439600
tbhits 0
time 0.01
pv [Move.from_uci('d1d3'), Move.from_uci('e4d3'), Move.from_uci('a1d1'), Move.from_uci('a6c7'), Move.from_uci('c3a4'), Move.from_uci('f6f8'), Move.from_uci('a4b2'), Move.from_uci('h5f6'), Move.from_uci('b2d3'), Move.from_uci('d8d7')]
string NNUE evaluation using nn-82215d0fd0df.nnue enabled
depth 10
seldepth 15
multipv 1
score PovScore(Cp(+55), WHITE)
nodes 4072
nps 290857
tbhits 0
time 0.014
pv [Move.from_uci('d1d3'), Move.from_uci('e4d3'), Move.from_uci('a1d1'), Move.from_uci('a6c7'), Move.from_uci('c3a4'), Move.from_uci('c7b5'), Move.from_uci('a4b2'), Move.from_uci('b5a3'), Move.from_uci('b2d3'), Move.from_uci('a3b5')]
string NNUE evaluation using nn-82215d0fd0df.nnue enabled
depth 10
seldepth 16
multipv 1
score PovScore(Cp(+26), WHITE)
nodes 4514
nps 282125
tbhits 0
time 0.016
pv [Move.from_uci('d1d3'), Move.from_uci('e4d3'), Move.from_uci('a1d1'), Move.from_uci('a6c7'), Move.from_uci('c3a4'), Move.from_uci('c7b5'), Move.from_uci('a4b2'), Move.from_uci('h8g7'), Move.from_uci('b2d3'), Move.from_uci('f6f8')]
string NNUE evaluation using nn-82215d0fd0df.nnue enabled
depth 10
seldepth 12
multipv 1
score PovScore(Cp(+164), WHITE)
nodes 2018
nps 252250
tbhits 0
time 0.008
pv [Move.from_uci('d1d3'), Move.from_uci('d8d3'), Move.from_uci('b3c4'), Move.from_uci('d3d8'), Move.from_uci('c3e2'), Move.from_uci('h8g7'), Move.from_uci('e1c3')]
看来我每次都变得不同 'seldepths'。 seldepth 到底是什么?我在文档中找不到足够的信息。
Stockfish 对于 单线程 分析具有确定性 node 和 depth 限制,状态和选项相等。
引擎状态主要是 哈希表(以及可能加载的 Syzygy 表库)。因此,您的第二个分析将利用第一个 运行 的哈希表。第三个 运行 将重用第二个 运行 的哈希表结果,依此类推。
通常重用哈希表结果是可取的并提高强度,但有一种方法可以重置引擎状态,清除哈希表。
在 UCI 协议中,这是通过发送提示来完成的:
ucinewgame
在 python-chess 中,这是使用 game
键自动管理的。
engine.analyse(..., game="key1")
engine.analyse(..., game="key1")
engine.analyse(..., game="key2") # will clear previous state
engine.play(..., game="key2")
engine.analyse(..., game="key1") # will clear previous state
键可以是任何东西,特别是 object()
不等于任何其他对象,因此总是会清除哈希表。
import chess
import chess.engine
board = chess.Board("3r3k/pp5p/n1p2rp1/2P2p1n/1P2p3/PBNqP2P/5PP1/R2RB1K1 w - - 0 26")
engine = chess.engine.SimpleEngine.popen_uci("stockfish")
engine_depth = 10
best_move = engine.play(board, chess.engine.Limit(depth=engine_depth), game=object()).move
info = engine.analyse(board, chess.engine.Limit(depth=engine_depth), game=object(), root_moves=[best_move])
print(info["score"])
info = engine.analyse(board, chess.engine.Limit(depth=engine_depth), game=object(), root_moves=[best_move])
print(info["score"])
info = engine.analyse(board, chess.engine.Limit(depth=engine_depth), game=object(), root_moves=[best_move])
print(info["score"])
info = engine.analyse(board, chess.engine.Limit(depth=engine_depth), game=object(), root_moves=[best_move])
print(info["score"])
engine.quit()
PovScore(Cp(+35), WHITE)
PovScore(Cp(+35), WHITE)
PovScore(Cp(+35), WHITE)
PovScore(Cp(+35), WHITE)
此处为初学者,正在尝试制作一款可检测错误的应用,并希望了解有关 chess.engine 库的更多信息。
我假设使用分析函数是一个独立的、独立的过程,不依赖于引擎之前调用的缓存或内存或类似的东西。
如果是这样,为什么我在脚本中多次调用分析时得到多个不同的评估:
import chess
import chess.engine
import os
# Loads board and engine
board = chess.Board("3r3k/pp5p/n1p2rp1/2P2p1n/1P2p3/PBNqP2P/5PP1/R2RB1K1 w - - 0 26")
engine = chess.engine.SimpleEngine.popen_uci(os.getcwd()+'/static/'+'stockfish')
#Sets engine depth constant
engine_depth = 10
# I'm assuming this is how to find the best move - arbitrary as I could have chosen any move in board.legal_moves
best_move = engine.play(board, chess.engine.Limit(depth=engine_depth)).move
# I'm assuming this is how to evaluate the score after a specific move from this position
info = engine.analyse(board, chess.engine.Limit(depth=engine_depth), root_moves=[best_move])
print(info["score"])
# Repeating the analysis call and printing the score 3 more times
info = engine.analyse(board, chess.engine.Limit(depth=engine_depth), root_moves=[best_move])
print(info["score"])
info = engine.analyse(board, chess.engine.Limit(depth=engine_depth), root_moves=[best_move])
print(info["score"])
info = engine.analyse(board, chess.engine.Limit(depth=engine_depth), root_moves=[best_move])
print(info["score"])
engine.quit()
输出:
PovScore(Cp(-21), WHITE)
PovScore(Cp(+2), WHITE)
PovScore(Cp(+19), WHITE)
PovScore(Cp(+63), WHITE)
对于超过一定水平的各种引擎深度,它会发生,但不会在深度较低时发生,例如最多 3.
它以时间而不是深度为限制发生。
它发生在多个不同的起始位置。
它发生在多个不同的动作中。
即使我在每次调用之间有 engine.quit()
和 engine = chess.engine.SimpleEngine.popen_uci(os.getcwd()+'/static/'+'stockfish')
,它也会发生。
分析函数不能有随机元素,因为当我再次 运行 整个脚本时,我得到完全相同的分数。只是当它在同一个脚本中多次调用同一个位置时,它每次都会给出不同的分数,就好像它在使用某种缓存或者每次看起来更深。
那么我对它的工作原理的理解哪里出错了?
编辑:
如果我删除 root_moves
参数(只是为了简化事情)然后替换:
print(info["score"])
与:
for k, v in info.items():
print(k, v)
我得到以下输出:
string NNUE evaluation using nn-82215d0fd0df.nnue enabled
depth 10
seldepth 13
multipv 1
score PovScore(Cp(+25), WHITE)
nodes 4396
nps 439600
tbhits 0
time 0.01
pv [Move.from_uci('d1d3'), Move.from_uci('e4d3'), Move.from_uci('a1d1'), Move.from_uci('a6c7'), Move.from_uci('c3a4'), Move.from_uci('f6f8'), Move.from_uci('a4b2'), Move.from_uci('h5f6'), Move.from_uci('b2d3'), Move.from_uci('d8d7')]
string NNUE evaluation using nn-82215d0fd0df.nnue enabled
depth 10
seldepth 15
multipv 1
score PovScore(Cp(+55), WHITE)
nodes 4072
nps 290857
tbhits 0
time 0.014
pv [Move.from_uci('d1d3'), Move.from_uci('e4d3'), Move.from_uci('a1d1'), Move.from_uci('a6c7'), Move.from_uci('c3a4'), Move.from_uci('c7b5'), Move.from_uci('a4b2'), Move.from_uci('b5a3'), Move.from_uci('b2d3'), Move.from_uci('a3b5')]
string NNUE evaluation using nn-82215d0fd0df.nnue enabled
depth 10
seldepth 16
multipv 1
score PovScore(Cp(+26), WHITE)
nodes 4514
nps 282125
tbhits 0
time 0.016
pv [Move.from_uci('d1d3'), Move.from_uci('e4d3'), Move.from_uci('a1d1'), Move.from_uci('a6c7'), Move.from_uci('c3a4'), Move.from_uci('c7b5'), Move.from_uci('a4b2'), Move.from_uci('h8g7'), Move.from_uci('b2d3'), Move.from_uci('f6f8')]
string NNUE evaluation using nn-82215d0fd0df.nnue enabled
depth 10
seldepth 12
multipv 1
score PovScore(Cp(+164), WHITE)
nodes 2018
nps 252250
tbhits 0
time 0.008
pv [Move.from_uci('d1d3'), Move.from_uci('d8d3'), Move.from_uci('b3c4'), Move.from_uci('d3d8'), Move.from_uci('c3e2'), Move.from_uci('h8g7'), Move.from_uci('e1c3')]
看来我每次都变得不同 'seldepths'。 seldepth 到底是什么?我在文档中找不到足够的信息。
Stockfish 对于 单线程 分析具有确定性 node 和 depth 限制,状态和选项相等。
引擎状态主要是 哈希表(以及可能加载的 Syzygy 表库)。因此,您的第二个分析将利用第一个 运行 的哈希表。第三个 运行 将重用第二个 运行 的哈希表结果,依此类推。
通常重用哈希表结果是可取的并提高强度,但有一种方法可以重置引擎状态,清除哈希表。
在 UCI 协议中,这是通过发送提示来完成的:
ucinewgame
在 python-chess 中,这是使用 game
键自动管理的。
engine.analyse(..., game="key1")
engine.analyse(..., game="key1")
engine.analyse(..., game="key2") # will clear previous state
engine.play(..., game="key2")
engine.analyse(..., game="key1") # will clear previous state
键可以是任何东西,特别是 object()
不等于任何其他对象,因此总是会清除哈希表。
import chess
import chess.engine
board = chess.Board("3r3k/pp5p/n1p2rp1/2P2p1n/1P2p3/PBNqP2P/5PP1/R2RB1K1 w - - 0 26")
engine = chess.engine.SimpleEngine.popen_uci("stockfish")
engine_depth = 10
best_move = engine.play(board, chess.engine.Limit(depth=engine_depth), game=object()).move
info = engine.analyse(board, chess.engine.Limit(depth=engine_depth), game=object(), root_moves=[best_move])
print(info["score"])
info = engine.analyse(board, chess.engine.Limit(depth=engine_depth), game=object(), root_moves=[best_move])
print(info["score"])
info = engine.analyse(board, chess.engine.Limit(depth=engine_depth), game=object(), root_moves=[best_move])
print(info["score"])
info = engine.analyse(board, chess.engine.Limit(depth=engine_depth), game=object(), root_moves=[best_move])
print(info["score"])
engine.quit()
PovScore(Cp(+35), WHITE)
PovScore(Cp(+35), WHITE)
PovScore(Cp(+35), WHITE)
PovScore(Cp(+35), WHITE)