UCI/XBoard engine communication
===============================
The `Universal chess interface (UCI) `_
and `XBoard protocol `_
are standards 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
:func:`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.
The examples also show a synchronous wrapper
:class:`~chess.engine.SimpleEngine` that automatically spawns an event loop
in the background.
:class:`~chess.engine.SimpleEngine` methods block until there is a result.
Playing
-------
Example: Let Stockfish play against itself, 100 milliseconds per move.
.. code-block:: python
:caption: Using synchronous :class:`~chess.engine.SimpleEngine`
import chess
import chess.engine
engine = chess.engine.SimpleEngine.popen_uci(r"C:\Users\xxxxx\Downloads\stockfish_14_win_x64\stockfish_14_win_x64_avx2.exe")
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()
.. code-block:: python
:caption: Using asyncio
import asyncio
import chess
import chess.engine
async def main() -> None:
transport, engine = await chess.engine.popen_uci(r"C:\Users\xxxxx\Downloads\stockfish_14_win_x64\stockfish_14_win_x64_avx2.exe")
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.run(main())
.. autoclass:: chess.engine.Protocol
:members: play
.. autoclass:: chess.engine.Limit
:members:
.. autoclass:: chess.engine.PlayResult
:members:
.. autoclass:: chess.engine.Protocol
:members: send_opponent_information
.. autoclass:: chess.engine.Opponent
:members:
.. autoclass:: chess.engine.Protocol
:members: send_game_result
Analysing and evaluating a position
-----------------------------------
Example:
.. code-block:: python
:caption: Using synchronous :class:`~chess.engine.SimpleEngine`
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: PovScore(Cp(+20), WHITE)
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: PovScore(Mate(+1), WHITE)
engine.quit()
.. code-block:: python
:caption: Using asyncio
import asyncio
import chess
import chess.engine
async def main() -> None:
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: PovScore(Cp(+20), WHITE)
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: PovScore(Mate(+1), WHITE)
await engine.quit()
asyncio.run(main())
.. autoclass:: chess.engine.Protocol
:members: analyse
.. autoclass:: chess.engine.InfoDict
.. autoclass:: chess.engine.PovScore
:members:
.. autoclass:: chess.engine.Score
:members:
.. autoclass:: chess.engine.PovWdl
:members:
.. autoclass:: chess.engine.Wdl
:members:
Indefinite or infinite analysis
-------------------------------
Example: Stream information from the engine and stop on an arbitrary condition.
.. code-block:: python
:caption: Using synchronous :class:`~chess.engine.SimpleEngine`
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()
.. code-block:: python
:caption: Using asyncio
import asyncio
import chess
import chess.engine
async def main() -> None:
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.run(main())
.. autoclass:: chess.engine.Protocol
:members: analysis
.. autoclass:: chess.engine.AnalysisResult
:members:
.. autoclass:: chess.engine.BestMove
:members:
Options
-------
:func:`~chess.Protocol.configure()`,
:func:`~chess.Protocol.play()`,
:func:`~chess.Protocol.analyse()` and
:func:`~chess.Protocol.analysis()` accept a dictionary of options.
.. code-block:: python
:caption: Using synchronous :class:`~chess.engine.SimpleEngine`
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})
# [...]
.. code-block:: python
:caption: Using asyncio
import asyncio
import chess.engine
async def main() -> None:
transport, engine = 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.run(main())
.. autoclass:: chess.engine.Protocol
:members: options, configure
.. autoclass:: chess.engine.Option
:members:
Logging
-------
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.
.. code:: python
import logging
# Enable debug logging.
logging.basicConfig(level=logging.DEBUG)
AsyncSSH
--------
:class:`chess.engine.Protocol` can also be used with
`AsyncSSH `_ (since 1.16.0)
to communicate with an engine on a remote computer.
.. code:: python
import asyncio
import asyncssh
import chess
import chess.engine
async def main() -> None:
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())
Reference
---------
.. autoclass:: chess.engine.EngineError
.. autoclass:: chess.engine.EngineTerminatedError
.. autoclass:: chess.engine.AnalysisComplete
.. autofunction:: chess.engine.popen_uci
.. autofunction:: chess.engine.popen_xboard
.. autoclass:: chess.engine.Protocol
:members: id, returncode, initialize, ping, quit
.. autoclass:: chess.engine.UciProtocol
.. autoclass:: chess.engine.XBoardProtocol
.. autoclass:: chess.engine.SimpleEngine
:members:
.. autoclass:: chess.engine.SimpleAnalysisResult
:members: