23
loading...
This website collects cookies to deliver better user experience
The starting position:
rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1
score
for a position. These scores are measured in centipawns, which is 1/100th of a pawn.#
. #1
means that White has a single move that will checkmate Black. #-3
means that Black can checkmate White within the next three moves, no matter what White does.which
to find it:$ which stockfish
/usr/local/bin/stockfish
$ mkdir chess-api
$ python3 -m venv venv
$ source venv/bin/activate
(venv) $ pip install python-chess
import chess
import chess.engine
# Change this if stockfish is somewhere else
engine = chess.engine.SimpleEngine.popen_uci("/usr/local/bin/stockfish")
# The position represented in FEN
board = chess.Board("5Q2/5K1k/8/8/8/8/8/8 w - - 0 1")
# Limit our search so it doesn't run forever
info = engine.analyse(board, chess.engine.Limit(depth=20))
{
'score': PovScore(Mate(+1), WHITE),
'pv': [Move.from_uci('f8g7')],
}
score
tells us what Stockfish thinks of the position, which is a mate in one.pv
(short for principal variation) tells us the sequence of moves that the engine expects to be played. In this case, it's saying that it expects White to move their queen on f8 to g7, which is checkmate.analyse
function has a few more options we haven't used, and one important one is multipv
. multipv=3
tells the engine to return the top 3 best moves, instead of just the best move.# ...same as before
board = chess.Board("5Q2/5K1k/8/8/8/8/8/8 w - - 0 1")
# Get the 3 best moves
info = engine.analyse(board, chess.engine.Limit(depth=20), multipv=3)
# Info is now an array with at most 3 elements
# If there aren't 3 valid moves, the array would have less than 3 elements
print(info[0])
print(info[1])
print(info[2])
import chess
import chess.engine
# Change this if stockfish is somewhere else
engine = chess.engine.SimpleEngine.popen_uci("/usr/local/bin/stockfish")
def analyze_position(fen, num_moves_to_return=1, depth_limit=None, time_limit=None):
search_limit = chess.engine.Limit(depth=depth_limit, time=time_limit)
board = chess.Board(fen)
infos = engine.analyse(board, search_limit, multipv=num_moves_to_return)
return [format_info(info) for info in infos]
def format_info(info):
# Normalize by always looking from White's perspective
score = info["score"].white()
# Split up the score into a mate score and a centipawn score
mate_score = score.mate()
centipawn_score = score.score()
return {
"mate_score": mate_score,
"centipawn_score": centipawn_score,
"pv": format_moves(info["pv"]),
}
# Convert the move class to a standard string
def format_moves(pv):
return [move.uci() for move in pv]
print(analyze_position("8/8/6P1/4R3/8/6k1/2r5/6K1 b - - 0 1", num_moves_to_return=3, depth_limit=20))
[
{
'mate_score': -2,
'centipawn_score': None,
'pv': ['c2c1', 'e5e1', 'c1e1']
},
{
'mate_score': None,
'centipawn_score': 0,
'pv': ['c2c8', 'e5e3', 'g3h4', 'e3e7', 'c8c6', ...]
},
{
'mate_score': None,
'centipawn_score': 0,
'pv': ['g3f4', 'e5e7', 'c2c8', 'g6g7', 'c8g8', ...]
}
]
centipawn_score
of 0). The engine then shows a long sequence of moves that it considers equal.