UCI engine communication

The Universal Chess Interface is a protocol for communicating with engines.

chess.uci.popen_engine(command, engine_cls=<class 'chess.uci.Engine'>)

Opens a local chess engine process.

No initialization commands are sent, so do not forget to send the mandatory uci command.

>>> engine = chess.uci.popen_engine("/usr/games/stockfish")
>>> engine.uci()
>>> engine.name
'Stockfish 230814 64'
>>> engine.author
'Tord Romstad, Marco Costalba and Joona Kiiski'

The input and input streams will be linebuffered and able both Windows and Unix newlines.

chess.uci.spur_spawn_engine(shell, command, engine_cls=<class 'chess.uci.Engine'>)

Spwans a remote engine using a Spur shell.

>>> import spur
>>> shell = spur.SshShell(hostname="localhost", username="username", password="pw")
>>> engine = chess.uci.spur_spwan_engine(shell, ["/usr/games/stockfish"])
>>> engine.uci()
class chess.uci.Engine(process_cls, args)
process

The underlying operating system process.

name

The name of the engine. Conforming engines should send this as id name when they receive the initial uci command.

author

The author, as sent via id author. Just like the name.

options

A case insensitive dictionary of Options. The engine should send available options when it receives the initial uci command.

uciok

threading.Event() that will be set as soon as uciok was received. By then name, author and options should be available.

return_code

The return code of the operating system process.

terminated

threading.Event() that will be set as soon as the underyling operating system process is terminated and the return_code is available.

terminate(async=None)

Terminate the engine.

This is not an UCI command. It instead tries to terminate the engine on operating system level, for example by sending SIGTERM on Unix systems. If possible, first try the quit command.

Returns:The return code of the engine process.
kill(async=False)

Kill the engine.

Forcefully kill the engine process, for example by sending SIGKILL.

Returns:The return code of the engine process.
is_alive()

Poll the engine process to check if it is alive.

UCI commands

class chess.uci.Engine(process_cls, args)
uci(async_callback=None)

Tells the engine to use the UCI interface.

This is mandatory before any other command. A conforming engine will send its name, authors and available options.

Returns:Nothing
debug(on, async_callback=None)

Switch the debug mode on or off.

In debug mode the engine should send additional infos to the GUI to help debugging. This mode should be switched off by default.

Parameters:on – bool
Returns:Nothing
isready(async_callback=None)

Command used to synchronize with the engine.

The engine will respond as soon as it has handled all other queued commands.

Returns:Nothing
setoption(options, async_callback=None)

Set a values for the engines available options.

Parameters:options – A dictionary with option names as keys.
Returns:Nothing
ucinewgame(async_callback=None)

Tell the engine that the next search will be from a different game.

This can be a new game the engine should play or if the engine should analyse a position from a different game. Using this command is recommended but not required.

Returns:Nothing
position(board, async_callback=None)

Set up a given position.

Instead of just the final FEN, the initial FEN and all moves leading up to the position will be sent, so that the engine can detect repititions.

If the position is from a new game it is recommended to use the ucinewgame command before the position command.

Parameters:board – A chess.Bitboard.
Returns:Nothing
go(searchmoves=None, ponder=False, wtime=None, btime=None, winc=None, binc=None, movestogo=None, depth=None, nodes=None, mate=None, movetime=None, infinite=False, async_callback=None)

Start calculating on the current position.

All parameters are optional, but there should be at least one of depth, nodes, mate, infinite or some time control settings, so that the engine knows how long to calculate.

Parameters:
  • searchmoves – Restrict search to moves in this list.
  • ponder – Bool to enable pondering mode.
  • wtime – Integer of milliseconds white has left on the clock.
  • btime – Integer of milliseconds black has left on the clock.
  • winc – Integer of white Fisher increment.
  • binc – Integer of black Fisher increment.
  • movestogo – Number of moves to the next time control. If this is not set, but wtime or btime are, then it is sudden death.
  • depth – Search depth ply only.
  • nodes – Search so many nodes only.
  • mate – Search for a mate in mate moves.
  • movetime – Integer. Search exactly movetime milliseconds.
  • infinite – Search in the backgorund until a stop command is received.
Returns:

In normal search mode a tuple of two elements. The first is the best move according to the engine. The second is the ponder move. This is the reply expected by the engine. Either of the elements may be None. In infinite search mode there is no result. See stop instead.

stop(async_callback=None)

Stop calculating as soon as possible.

Returns:A tuple of the latest best move and the ponder move. See the go command. Results of infinite searches will also be available here.
ponderhit(async_callback=None)

May be sent if the expected ponder move has been played.

The engine should continue searching but should switch from pondering to normal search.

Returns:Nothing
quit(async_callback=None)

Quit the engine as soon as possible.

Returns:The return code of the engine process.

Asynchronous communication

By default all operations are executed synchronously and their result is returned. For example

>>> engine.go(movetime=2000)
(Move.from_uci('e2e4'), None)

will take about 2000 milliseconds. All UCI commands have an optional async_callback argument. They will then immediately return information about the command and continue.

>>> command = engine.go(movetime=2000, async_callback=True)
>>> command.is_done()
False
>>> command.result
None
>>> command.wait() # Synchronously wait for the command to finish
>>> command.is_done()
True
>>> command.result
(Move.from_uci('e2e4'), None)

Instead of just passing async_callback=True a callback function may be passed. It will be invoked on a different thread as soon as the command is completed. Make sure the number of arguments exactly matches the expected result.

>>> def on_go_finished(bestmove, pondermove):
...     # Will be executed on a different thread.
...     pass
...
>>> command = engine.go(movetime=2000, async_callback=on_go_finished)

All commands are queued and executed in FIFO order (regardless if asynchronous or not).

class chess.uci.Command(callback=None)

Information about the state of a command.

result

The result if the command has been completed or None.

wait(timeout=None)

Wait for the command to be completed.

This may wait forever unless a floating point number of seconds is specified as the timeout. Raises CommandTimeoutException if a timeout is indeed encountered.

is_done()

Checks if the command has been completed yet.

Info handler

Chess engines may send information about their calculations with the info command. You can register info handlers to be asynchronously notified whenever the engine sends more information. You would usually subclass the InfoHandler class.

class chess.uci.Score

A centipawns or mate score sent by an UCI engine.

cp

Evaluation in centipawns or None.

mate

Mate in x or None. Negative if the engine thinks it is going to be mated.

lowerbound

If the score is not exact but only a lowerbound.

upperbound

If the score is only an upperbound.

class chess.uci.InfoHandler
info

The default implementation stores all received information in this dictionary. To get a consistent snapshot use the object as if it were a threading.Lock().

>>> # Register the handler.
>>> handler = InfoHandler()
>>> engine.info_handlers.append(handler)
>>> # Start thinking.
>>> engine.go(infinite=True)
>>> # Wait a moment, then access a consistent snapshot.
>>> time.sleep(3)
>>> with handler:
...     if "score" in handler.info:
...         print("Score: ", handler.info["score"].cp)
...         print("Mate: ", handler.info["score"].mate)
Score: 34
Mate: None
depth(x)

Received search depth in plies.

seldepth(x)

Received selective search depth in plies.

time(x)

Received new time searched in milliseconds.

nodes(x)

Received number of nodes searched.

pv(moves)

Received the principal variation as a list of moves.

In MultiPV mode this is related to the most recent multipv number sent by the engine.

multipv(num)

Received a new multipv number, starting at 1.

score(cp, mate, lowerbound, upperbound)

Received a new evaluation in centipawns or a mate score.

cp may be None if no score in centipawns is available.

mate may be None if no forced mate has been found. A negative numbers means the engine thinks it will get mated.

lowerbound and upperbound are usually False. If True, the sent score are just a lowerbound or upperbound.

currmove(move)

Received a move the engine is currently thinking about.

currmovenumber(x)

Received a new currmovenumber.

hashfull(x)

Received new information about the hashtable.

The hashtable is x permill full.

nps(x)

Received new nodes per second statistic.

tbhits(x)

Received new information about the number of table base hits.

cpuload(x)

Received new cpuload information in permill.

string(string)

Received a string the engine wants to display.

refutation(move, refuted_by)

Received a new refutation of a move.

refuted_by may be a list of moves representing the mainline of the refutation or None if no refutation has been found.

Engines should only send refutations if the UCI_ShowRefutations option has been enabled.

currline(cpunr, moves)

Received a new snapshot of a line a specific CPU is calculating.

cpunr is an integer representing a specific CPU. moves is a list of moves.

pre_info(line)

Received a new info line about to be processed.

When subclassing remember to call this method of the parent class in order to keep the locking in tact.

post_info()

Processing of a new info line has been finished.

When subclassing remember to call this method of the parent class in order to keep the locking in tact.

pre_bestmove(line)

A new bestmove command is about to be processed.

on_bestmove(bestmove, ponder)

A new bestmove and pondermove have been received.

post_bestmove()

A new bestmove command was processed.

Since this indicates that the current search has been finished the dictionary with the current information will be cleared.

Options

class chess.uci.Option

Information about an available option for an UCI engine.

name

The name of the option.

type

The type of the option.

Officially documented types are check for a boolean value, spin for an integer value between a minimum and a maximum, combo for an enumeration of predefined string values (one of which can be selected), button for an action and string for a textfield.

default

The default value of the option.

There is no need to send a setoption command with the defaut value.

min

The minimum integer value of a spin option.

max

The maximum integer value of a spin option.

var

A list of allows string values for a combo option.