如何在 python-chess 中获得白棋的获胜机会
How to get the winning chances of white in python-chess
所以我开始制作一款多人国际象棋游戏。现在,因为它是一个GUI,所以我在左边有棋盘,在右边,有一个分析区。
现在,我希望我的分析区域显示用户的获胜机会。
假设有一个方形块,里面有两个矩形。现在,它们都代表了 50-50 的机会。每次移动后,我想用当前的机会更新它。那么我怎样才能得到一个呢?
我可以返回 Cp
class,但它显示了机会的准确性(我猜)。
如果有人玩过chess.com,我想你应该明白我想得到什么吧:D
(1) 对于支持 wdl 信息的引擎,您可以试试这个。
"""
* Install python
* Install python-chess
pip install chess
"""
import chess
import chess.engine
def analyze(engine_file, threads, hash_mb, fen, movetime_sec, max_depth):
"""
Analyze position fen with the engine_file and stream the winning chances of both sides.
The engine should support the wdl info.
"""
engine = chess.engine.SimpleEngine.popen_uci(engine_file)
# Set threads and hash engine options.
engine.configure({'Threads': threads})
engine.configure({'Hash': hash_mb})
limit = chess.engine.Limit(time=movetime_sec, depth=max_depth)
board = chess.Board(fen, chess960=False)
stm = board.turn # stm is Side To Move
# Get engine analysis info while it is analyzing the position.
with engine.analysis(board, limit=limit) as analysis:
for info in analysis:
eng_score = info.get("score")
if eng_score is not None:
wdl = eng_score.wdl() # win/draw/loss info point of view is stm
wins, draws, losses = wdl[0], wdl[1], wdl[2]
score = wins + draws/2
total = wins + draws + losses
score_rate = score / total
win_rate = wins / total
draw_rate = draws / total
loss_rate = losses / total
white_winning_chances = win_rate if stm==chess.WHITE else loss_rate
black_winning_chances = win_rate if stm==chess.BLACK else loss_rate
white_score_rate = score_rate if stm==chess.WHITE else 1 - score_rate
black_score_rate = score_rate if stm==chess.BLACK else 1 - score_rate
# Show info.
print(f'white_winning_chances: {100 * white_winning_chances:0.2f}%, white_score_rate: {100 * white_score_rate:0.2f}%, white_draw_rate: {100 * draw_rate:0.2f}%')
print(f'black_winning_chances: {100 * black_winning_chances:0.2f}%, black_score_rate: {100 * black_score_rate:0.2f}%, black_draw_rate: {100 * draw_rate:0.2f}%')
engine.quit()
def main():
engine_file = 'F:/Chess/Engines/stockfish/sf14/sf14.exe'
fen = 'rnbqkb1r/1p2pppp/p2p1n2/8/3NP3/2N5/PPP2PPP/R1BQKB1R w KQkq - 0 6' # sicilian opening
movetime_sec = 5
max_depth = 24
threads = 1
hash_mb = 64
analyze(engine_file, threads, hash_mb, fen, movetime_sec, max_depth)
if __name__ == "__main__":
main()
输出:
...
white_winning_chances: 18.20%, white_score_rate: 57.25%, white_draw_rate: 78.10%
black_winning_chances: 3.70%, black_score_rate: 42.75%, black_draw_rate: 78.10%
white_winning_chances: 20.60%, white_score_rate: 58.70%, white_draw_rate: 76.20%
black_winning_chances: 3.20%, black_score_rate: 41.30%, black_draw_rate: 76.20%
white_winning_chances: 19.10%, white_score_rate: 57.80%, white_draw_rate: 77.40%
black_winning_chances: 3.50%, black_score_rate: 42.20%, black_draw_rate: 77.40%
white_winning_chances: 19.10%, white_score_rate: 57.80%, white_draw_rate: 77.40%
black_winning_chances: 3.50%, black_score_rate: 42.20%, black_draw_rate: 77.40%
(2)对于不支持wdl的引擎或者你想用分数来计算其获胜概率。
import chess
import chess.engine
MATE_SCORE = 32000
def winning_probability(cp):
"""
Use a sigmoid or logistic function to map the engine score cp into [0 to 1] winning probability.
Technically winning probability is like score rate because the game of chess has draw results.
So winning probability is like (num_wins + num_draw/2)/total_games.
cp is a score in centipawn unit.
Ref: https://www.chessprogramming.org/Pawn_Advantage,_Win_Percentage,_and_Elo
"""
# K is a factor to scale the winning probability, if K is low the winning probability is high.
# Stronger engine generally has a lower K value that is if stronger engine has an advantage of
# 75 centipawn, while a weaker engine has an advantage of 75 centipawn too, it is the stronger
# engine that has a higher chance to win the game.
K = 4
p = cp/100 # convert centipawn to pawn unit
return 1 / (1 + 10 ** (-p/K))
def analyze(engine_file, threads, hash_mb, fen, movetime_sec, max_depth):
"""
Analyze position fen with the engine_file and stream the score and rate.
"""
engine = chess.engine.SimpleEngine.popen_uci(engine_file)
# Set threads and hash engine options.
engine.configure({'Threads': threads})
engine.configure({'Hash': hash_mb})
limit = chess.engine.Limit(time=movetime_sec, depth=max_depth)
board = chess.Board(fen, chess960=False)
# Get engine analysis info while it is analyzing the position.
with engine.analysis(board, limit=limit) as analysis:
for info in analysis:
eng_score = info.get("score")
if eng_score is not None:
score = eng_score.relative.score(mate_score=MATE_SCORE) # score is in centipawn with side POV(Point Of View)
print(f'score: {score}, side to move score rate: {100 * winning_probability(score):0.2f}%')
engine.quit()
def main():
engine_file = 'F:/Chess/Engines/stockfish/sf14/sf14.exe'
fen = 'rnbqkb1r/1p2pppp/p2p1n2/8/3NP3/2N5/PPP2PPP/R1BQKB1R w KQkq - 0 6' # sicilian opening
movetime_sec = 5
max_depth = 24
threads = 1
hash_mb = 64
analyze(engine_file, threads, hash_mb, fen, movetime_sec, max_depth)
if __name__ == "__main__":
main()
输出:
score: 41, side to move score rate: 55.87%
score: 45, side to move score rate: 56.44%
score: 53, side to move score rate: 57.57%
score: 48, side to move score rate: 56.86%
score: 48, side to move score rate: 56.86%
所以我开始制作一款多人国际象棋游戏。现在,因为它是一个GUI,所以我在左边有棋盘,在右边,有一个分析区。
现在,我希望我的分析区域显示用户的获胜机会。
假设有一个方形块,里面有两个矩形。现在,它们都代表了 50-50 的机会。每次移动后,我想用当前的机会更新它。那么我怎样才能得到一个呢?
我可以返回 Cp
class,但它显示了机会的准确性(我猜)。
如果有人玩过chess.com,我想你应该明白我想得到什么吧:D
(1) 对于支持 wdl 信息的引擎,您可以试试这个。
"""
* Install python
* Install python-chess
pip install chess
"""
import chess
import chess.engine
def analyze(engine_file, threads, hash_mb, fen, movetime_sec, max_depth):
"""
Analyze position fen with the engine_file and stream the winning chances of both sides.
The engine should support the wdl info.
"""
engine = chess.engine.SimpleEngine.popen_uci(engine_file)
# Set threads and hash engine options.
engine.configure({'Threads': threads})
engine.configure({'Hash': hash_mb})
limit = chess.engine.Limit(time=movetime_sec, depth=max_depth)
board = chess.Board(fen, chess960=False)
stm = board.turn # stm is Side To Move
# Get engine analysis info while it is analyzing the position.
with engine.analysis(board, limit=limit) as analysis:
for info in analysis:
eng_score = info.get("score")
if eng_score is not None:
wdl = eng_score.wdl() # win/draw/loss info point of view is stm
wins, draws, losses = wdl[0], wdl[1], wdl[2]
score = wins + draws/2
total = wins + draws + losses
score_rate = score / total
win_rate = wins / total
draw_rate = draws / total
loss_rate = losses / total
white_winning_chances = win_rate if stm==chess.WHITE else loss_rate
black_winning_chances = win_rate if stm==chess.BLACK else loss_rate
white_score_rate = score_rate if stm==chess.WHITE else 1 - score_rate
black_score_rate = score_rate if stm==chess.BLACK else 1 - score_rate
# Show info.
print(f'white_winning_chances: {100 * white_winning_chances:0.2f}%, white_score_rate: {100 * white_score_rate:0.2f}%, white_draw_rate: {100 * draw_rate:0.2f}%')
print(f'black_winning_chances: {100 * black_winning_chances:0.2f}%, black_score_rate: {100 * black_score_rate:0.2f}%, black_draw_rate: {100 * draw_rate:0.2f}%')
engine.quit()
def main():
engine_file = 'F:/Chess/Engines/stockfish/sf14/sf14.exe'
fen = 'rnbqkb1r/1p2pppp/p2p1n2/8/3NP3/2N5/PPP2PPP/R1BQKB1R w KQkq - 0 6' # sicilian opening
movetime_sec = 5
max_depth = 24
threads = 1
hash_mb = 64
analyze(engine_file, threads, hash_mb, fen, movetime_sec, max_depth)
if __name__ == "__main__":
main()
输出:
...
white_winning_chances: 18.20%, white_score_rate: 57.25%, white_draw_rate: 78.10%
black_winning_chances: 3.70%, black_score_rate: 42.75%, black_draw_rate: 78.10%
white_winning_chances: 20.60%, white_score_rate: 58.70%, white_draw_rate: 76.20%
black_winning_chances: 3.20%, black_score_rate: 41.30%, black_draw_rate: 76.20%
white_winning_chances: 19.10%, white_score_rate: 57.80%, white_draw_rate: 77.40%
black_winning_chances: 3.50%, black_score_rate: 42.20%, black_draw_rate: 77.40%
white_winning_chances: 19.10%, white_score_rate: 57.80%, white_draw_rate: 77.40%
black_winning_chances: 3.50%, black_score_rate: 42.20%, black_draw_rate: 77.40%
(2)对于不支持wdl的引擎或者你想用分数来计算其获胜概率。
import chess
import chess.engine
MATE_SCORE = 32000
def winning_probability(cp):
"""
Use a sigmoid or logistic function to map the engine score cp into [0 to 1] winning probability.
Technically winning probability is like score rate because the game of chess has draw results.
So winning probability is like (num_wins + num_draw/2)/total_games.
cp is a score in centipawn unit.
Ref: https://www.chessprogramming.org/Pawn_Advantage,_Win_Percentage,_and_Elo
"""
# K is a factor to scale the winning probability, if K is low the winning probability is high.
# Stronger engine generally has a lower K value that is if stronger engine has an advantage of
# 75 centipawn, while a weaker engine has an advantage of 75 centipawn too, it is the stronger
# engine that has a higher chance to win the game.
K = 4
p = cp/100 # convert centipawn to pawn unit
return 1 / (1 + 10 ** (-p/K))
def analyze(engine_file, threads, hash_mb, fen, movetime_sec, max_depth):
"""
Analyze position fen with the engine_file and stream the score and rate.
"""
engine = chess.engine.SimpleEngine.popen_uci(engine_file)
# Set threads and hash engine options.
engine.configure({'Threads': threads})
engine.configure({'Hash': hash_mb})
limit = chess.engine.Limit(time=movetime_sec, depth=max_depth)
board = chess.Board(fen, chess960=False)
# Get engine analysis info while it is analyzing the position.
with engine.analysis(board, limit=limit) as analysis:
for info in analysis:
eng_score = info.get("score")
if eng_score is not None:
score = eng_score.relative.score(mate_score=MATE_SCORE) # score is in centipawn with side POV(Point Of View)
print(f'score: {score}, side to move score rate: {100 * winning_probability(score):0.2f}%')
engine.quit()
def main():
engine_file = 'F:/Chess/Engines/stockfish/sf14/sf14.exe'
fen = 'rnbqkb1r/1p2pppp/p2p1n2/8/3NP3/2N5/PPP2PPP/R1BQKB1R w KQkq - 0 6' # sicilian opening
movetime_sec = 5
max_depth = 24
threads = 1
hash_mb = 64
analyze(engine_file, threads, hash_mb, fen, movetime_sec, max_depth)
if __name__ == "__main__":
main()
输出:
score: 41, side to move score rate: 55.87%
score: 45, side to move score rate: 56.44%
score: 53, side to move score rate: 57.57%
score: 48, side to move score rate: 56.86%
score: 48, side to move score rate: 56.86%