UCI Chess Engine Protocol

I really like the UCI protocol.  I know there are diehard fans of Winboard out there, but for me I like the cleanness of UCI.  One of the aim I have for Maverick is to support as much of the protocol as possible.  In Monarch the UCI code was stable but it was a bit messy.  So I put some thought into how I could simplify the implementation.  Here’s what I came up with for Maverick:

There are two threads.  The first thread just listens for user / GUI input.  We’ll call this the listening thread. The second thread runs the engine’s search and reports back with it’s findings.  We’ll call this the thinking thread.  Most, if not all, problems with this type of implementation crop up when one thread gets out of sync with the other thread’s state. This usually results in the thinking thread being sent unexpected commands which are either ignored or processed incorrectly.

The insight I had was by using the UCI protocol there are only two commands which change the state of the thinking thread.  These are “go” and “stop”, (there is also “quit”, which I handle as a special form of “stop”).  So the “go” command needs to change the state of the thinking thread from “waiting” to “thinking”.  And the stop command needs to change the state back from “thinking” to “waiting”.  In Maverick’s implementation I’ve also added “Start_Thinking” as another state for the thinking thread.  This is needed when there is rapid fire changes and the GUI is blasting commands at the engine.  Under the rapid fire scenario the engine can receive a stop command before the engine starts to think.  By having the “Start_Thinking” state the engine can wait for the search to start before taking any more input from the GUI.

Here’s the code:

void uci_stop()
{
uci.stop = TRUE;
while (uci.engine_state != UCI_ENGINE_WAITING)
Sleep(1);
}

void uci_go(char *s)
{
search_start_time = time_now();
last_display_update = search_start_time;
set_uci_level(s, position->to_move);
uci.engine_state = UCI_ENGINE_START_THINKING;
while (uci.engine_state == UCI_ENGINE_START_THINKING)
Sleep(1);
}

It seems to work well and be rock-solid stable!

Maverick is almost ready to start playing chess.  A few more clean-ups needed!

  • A good test of your thread synchronization is to use the Fritz GUI to process a test set (EPD position file). I have this hack in my code. I’ve meant to fix it properly but haven’t gotten to it yet. Mainly because I feel the Fritz GUI is at fault. It should simple send a go movetime command instead of two sets of go and stop commands. Beside, getting the UCI implementation to work reliably in an engine tournament is more important than a test set.

    private void ProcessStopCommand()
    {
    // Fritz GUI sends position, go infinite, and immediate stop command. The resends same position, go infinite, and delayed stop command.
    // This causes a race condition between synchronous and asynchronous threads.
    // Sleep to avoid race condition.
    Thread.Sleep(TimeSpan.FromMilliseconds(100));
    StopCommand objStop = new StopCommand();
    Engine.ReceiveStop(objStop);
    }

  • Steve Maughan

    Thanks Erik – I’ll give it a go. I’m having some problem with the UCI synchronization – see my post on CCC http://www.talkchess.com/forum/viewtopic.php?t=48814