UCI and XBoard are protocols for communicating with chess engines. This module implements an abstraction for playing moves and analysing positions with both kinds of engines.
Warning
Many popular chess engines make no guarantees, not even memory safety, when parameters and positions are not completely valid <chess.Board.is_valid()>
. This module tries to deal with benign misbehaving engines, but ultimately they are executables running on your system.
The preferred way to use the API is with an asyncio event loop (examples show usage with Python 3.7 or later). The examples also show a synchronous wrapper ~chess.engine.SimpleEngine
that automatically spawns an event loop in the background.
Example: Let Stockfish play against itself, 100 milliseconds per move.
import chess
import chess.engine
engine = chess.engine.SimpleEngine.popen_uci("/usr/bin/stockfish")
board = chess.Board()
while not board.is_game_over():
result = engine.play(board, chess.engine.Limit(time=0.1))
board.push(result.move)
engine.quit()
import asyncio
import chess
import chess.engine
async def main():
transport, engine = await chess.engine.popen_uci("/usr/bin/stockfish")
board = chess.Board()
while not board.is_game_over():
result = await engine.play(board, chess.engine.Limit(time=0.1))
board.push(result.move)
await engine.quit()
asyncio.set_event_loop_policy(chess.engine.EventLoopPolicy())
asyncio.run(main())
chess.engine.EngineProtocol
chess.engine.Limit
chess.engine.PlayResult
Example:
import chess
import chess.engine
engine = chess.engine.SimpleEngine.popen_uci("/usr/bin/stockfish")
board = chess.Board()
info = engine.analyse(board, chess.engine.Limit(time=0.1))
print("Score:", info["score"])
# Score: +20
board = chess.Board("r1bqkbnr/p1pp1ppp/1pn5/4p3/2B1P3/5Q2/PPPP1PPP/RNB1K1NR w KQkq - 2 4")
info = engine.analyse(board, chess.engine.Limit(depth=20))
print("Score:", info["score"])
# Score: #+1
engine.quit()
import asyncio
import chess
import chess.engine
async def main():
transport, engine = await chess.engine.popen_uci("/usr/bin/stockfish")
board = chess.Board()
info = await engine.analyse(board, chess.engine.Limit(time=0.1))
print(info["score"])
# Score: +20
board = chess.Board("r1bqkbnr/p1pp1ppp/1pn5/4p3/2B1P3/5Q2/PPPP1PPP/RNB1K1NR w KQkq - 2 4")
info = await engine.analyse(board, chess.engine.Limit(depth=20))
print(info["score"])
# Score: #+1
await engine.quit()
asyncio.set_event_loop_policy(chess.engine.EventLoopPolicy())
asyncio.run(main())
chess.engine.EngineProtocol
chess.engine.PovScore
chess.engine.Score
Example: Stream information from the engine and stop on an arbitrary condition.
import chess
import chess.engine
engine = chess.engine.SimpleEngine.popen_uci("/usr/bin/stockfish")
with engine.analysis(chess.Board()) as analysis:
for info in analysis:
print(info.get("score"), info.get("pv"))
# Arbitrary stop condition.
if info.get("seldepth", 0) > 20:
break
engine.quit()
import asyncio
import chess
import chess.engine
async def main():
transport, engine = await chess.engine.popen_uci("/usr/bin/stockfish")
with await engine.analysis(chess.Board()) as analysis:
async for info in analysis:
print(info.get("score"), info.get("pv"))
# Arbitrary stop condition.
if info.get("seldepth", 0) > 20:
break
await engine.quit()
asyncio.set_event_loop_policy(chess.engine.EventLoopPolicy())
asyncio.run(main())
chess.engine.EngineProtocol
chess.engine.AnalysisResult
chess.engine.BestMove
~chess.EngineProtocol.configure()
, ~chess.EngineProtocol.play()
, ~chess.EngineProtocol.analyse()
and ~chess.EngineProtocol.analysis()
accept a dictionary of options.
>>> import chess.engine >>> >>> engine = chess.engine.SimpleEngine.popen_uci("/usr/bin/stockfish") >>> >>> # Check available options. >>> engine.options["Hash"] Option(name='Hash', type='spin', default=16, min=1, max=131072, var=[]) >>> >>> # Set an option. >>> engine.configure({"Hash": 32}) >>> >>> # [...]
import asyncio
import chess.engine
async def main():
transport, protocol = await chess.engine.popen_uci("/usr/bin/stockfish")
# Check available options.
print(engine.options["Hash"])
# Option(name='Hash', type='spin', default=16, min=1, max=131072, var=[])
# Set an option.
await engine.configure({"Hash": 32})
# [...]
asyncio.set_event_loop_policy(chess.engine.EventLoopPolicy())
asyncio.run(main())
chess.engine.EngineProtocol
chess.engine.Option
Communication is logged with debug level on a logger named chess.engine
. Debug logs are useful while troubleshooting. Please also provide them when submitting bug reports.
import logging
# Enable debug logging.
logging.basicConfig(level=logging.DEBUG)
~chess.engine.EngineProtocol
can also be used with AsyncSSH (since 1.16.0) to communicate with an engine on a remote computer.
import asyncio
import asyncssh
import chess
import chess.engine
async def main():
async with asyncssh.connect("localhost") as conn:
channel, engine = await conn.create_subprocess(chess.engine.UciProtocol, "/usr/bin/stockfish")
await engine.initialize()
# Play, analyse, ...
await engine.ping()
asyncio.run(main())
chess.engine.EngineError
chess.engine.EngineTerminatedError
chess.engine.AnalysisComplete
chess.engine.popen_uci
chess.engine.popen_xboard
chess.engine.EngineProtocol
chess.engine.UciProtocol
chess.engine.XBoardProtocol
chess.engine.SimpleEngine
chess.engine.SimpleAnalysisResult
chess.engine.EventLoopPolicy