Chess Engine Communication Protocol
Last modified on Tue May 13 13:23:42 PDT 1997 by mann
==============================================================================

This document is a set of rough notes on the protocol that xboard and WinBoard
use to communicate with gnuchessx and other chess engines.  These notes may be
useful if you want to connect a different chess program to xboard.

There are two reasons I can imagine someone wanting to do this: (1) You have,
or are developing, a chess program but you don't want to write your own
graphical interface.  (2) You have, or are developing, a chess program, and you
want to interface it to the Internet Chess Server.

In case (2), if you are using xboard, you will need to configure the "Zippy"
code into it.  WinBoard includes this code already. See the file zippy.README
in the xboard or WinBoard distribution for more information.

These notes are unpolished, but I've attempted to make them complete in this
release.  If you notice any errors, omissions, or misleading statements, let me
know.

* * *

Basically, xboard is just trying to talk to the existing command-line
interface of gnuchess, designed for people to type commands to.  So the
communication protocol is very ad-hoc.  (The reason why there is a gnuchessx
that's different from gnuchessr is buried in the mists of time, before I
started working on xboard, but I think it was due to someone working around a
stupid bug in xboard by changing gnuchess instead of fixing the bug.  The
differences are tiny.)  It's now tough to change the interface, because xboard
and gnuchess are separate programs, and I don't want to force people to
upgrade them together to versions that match.

- At the beginning of each game, xboard sends an initialization string.  This
is currently always "new\nbeep\nrandom\nhard\n" unless you change it with the
-initString option.  Note \n is a newline character.

- After the initString, xboard expects your program to be ready to play Black.
That is, if xboard sends your program a move for White, your program should
respond.  If your program is to play White, xboard will send "white\ngo\n" to
request the program's first move.

- xboard will send an interrupt signal (SIGINT) at certain times when it
believes your program may not be listening to user input.  The SIGINTs are
basically tailored to the needs of GNU Chess on systems where its input polling
code is broken or disabled.  If xboard needs to send a command when it is the
chess program's move (such as before the "?" command), it sends a SIGINT first.
Because doing this causes GNU Chess to make an immediate move, xboard avoids it
as much as possible.  If xboard needs to send commands when it is not the chess
program's move, but the chess program may be pondering (thinking on its
opponent's time) or analyzing (analysis or analyze file mode), xboard sends a
SIGINT before the first such command only.  Another SIGINT is not sent until
another move is made, even if xboard issues more commands.  This behavior is
necessary for GNU Chess.  The first SIGINT stops it from pondering until the
next move, and on some systems, GNU Chess will die if it receives a SIGINT when
not actually thinking or pondering.

WinBoard wants to send SIGINT or its Windows equivalent (CTRL_C_EVENT), but
I've had problems trying to use GenerateConsoleCtrlEvent in Win32.  Currently,
WinBoard does send SIGINT if your chess engine is running remotely on Unix via
rsh.  (See the -firstHost and -remoteShell options in the WinBoard help file.)
But if your chess engine is running locally, WinBoard sends nothing.  My port
of GNU Chess to Win32 does polling on its input (if the input is a pipe), so no
CTRL_C_EVENT is needed.  The GNU Chess port would accept a CTRL_C_EVENT if I
could manage to send one, and I hope to do that someday.

You can either (1) catch SIGINT in your program and use it the same way GNU
Chess does, (2) have your program ignore it by calling signal(SIGINT, SIG_IGN),
or (3) tell xboard not to send it, by including --disable-sigint on the
configure command line (for example: "./configure --enable-zippy
--disable-sigint; make").  If you use option (2) or (3), your program must be
able to poll for input while thinking or pondering.  I recommend option (2) for
new programs.

The most portable way to poll for input on Unix is with select().  On Win32 it
is less clear how best to do it, as due to Microsoft brain damage, select works
only on sockets.  PeekNamedPipe works on pipes (even nameless ones), such as
WinBoard uses to talk to the chess engine, but it does not work if your chess
engine is running in text mode in an MSDOS Prompt box.  WaitForSingleObject
might work more generally, but I haven't tried it.  Threads work fine on Win32,
so you can delegate reading input to a separate thread from your main chess
engine, but programming with threads is tricky; I don't recommend it to the
uninitiated.  I use PeekNamedPipe in my port of GNU Chess to Win32.  I use
threads in WinBoard itself and in my ports of timestamp and timeseal.

- If the user selects Move Now from the menu, and it is your program's move,
xboard/WinBoard will send your program the command "?\n" (possibly preceded by
a SIGINT; see above).

- When xboard sends your program a move, it always sends coordinate algebraic
notation, like this:

normal moves:    e2e4
pawn promotion:  e7e8q
castling:        e1g1

If your program can't handle this kind of output, change the routine
SendMoveToProgram in backend.c as needed.  In particular, the code below
causes standard abbreviated algebratic to be sent if GAZEBO is defined to 1.
(For example: e4, Nf3, exd5.)

void SendMoveToProgram(moveType, fromX, fromY, toX, toY, programPR, sendTime)
     ChessMove moveType;
     int fromX, fromY, toX, toY;
     ProcRef programPR;
     int sendTime;
{
    char user_move[MSG_SIZ];
    char promoChar = ToLower(PieceToChar(PromoPiece(moveType)));

#if GAZEBO
    (void) CoordsToAlgebraic(boards[forwardMostMove - 1],
			     FakeFlags(forwardMostMove - 1), EP_UNKNOWN,
			     fromY, fromX, toY, toX, promoChar, user_move);
#else
    if (promoChar == '.')
      sprintf(user_move, "%c%c%c%c\n",
              'a' + fromX, '1' + fromY, 'a' + toX, '1' + toY);
    else
      sprintf(user_move, "%c%c%c%c%c\n",
              'a' + fromX, '1' + fromY, 'a' + toX, '1' + toY,
              promoChar);
#endif /*!GAZEBO*/
    
    if (sendTime)
      SendTimeRemaining(programPR);
    SendToProgram(user_move, programPR);
    strcpy(moveList[forwardMostMove - 1], user_move);  /* note side-effect */
}

- When your program sends out a move, xboard looks for a format like this: 

1. ... e2e4

xboard requires the "..." even if your program is playing White.  This seems
illogical, but unfortunately it is an ingrained feature of the xboard/gnuchess
interface now.  I believe it was originally a bug in the earliest versions of
xboard, before I started working on it, which someone "fixed" by creating a
special version of gnuchess (gnuchessx) instead of changing xboard.

For the actual move text from your chess program (in place of e2e4 above),
xboard will accept any kind of unambiguous algebraic format.  You don't have to
send the pure coordinate notation that xboard sends to your program; xboard
parses the output with its general-purpose move parser, which was built to
extract human-typed game scores from netnews messages.  For example, the
following will all work:

    e2e4
    e4
    Nf3
    ed
    exd
    exd5
    Nxd5
    Nfd3
    e8q
    e8Q
    e8=q
    e8(Q)
    e7e8q
    o-o
    O-O
    0-0

and many more.

- xboard sends "time nnn" and "otim nnn" commands to set the chess program's
clocks, where "nnn" is the amount of time remaining in 1/100's of a second.
If you can't handle these commands, you can ignore them; or better, send back
"Illegal move: time" the first time you see "time", and xboard will realize
you don't implement the command.

- When the game is over, your program should output the string "White mates",
"Black mates", or "Draw" so that xboard knows the game is ended.

- "force" is sent when xboard goes into EditGame mode; it causes the chess
program to accept moves for both sides and make no moves of its own.  The chess
program continues to check the moves for legality.  xboard avoids sending
"force" when it believes your program is already in force mode, because the
"force" command is actually a toggle in GNU Chess (ugh).  The "new" command
should clear force mode.

- "black\ngo\n" is sent to get the chess program to play Black when it was
previously in force mode or playing White. "white\ngo\n" is sent to get the
chess program to play White when it was previously in force mode or playing
Black.

- Historically, xboard did not send the "white" or "black" command without a
"go" following, as the exact meaning of doing so has varied among different
versions of GNU Chess.  However, it will now do this in analyze mode.  The
"white" command means white is now on move.  The "black" command means black is
now on move.  (With GNU Chess 4.0.77, the commands also cancel force mode and
cause GNU Chess to begin playing the color that is not on move, which is rather
inconvenient.  It is OK if your program does not attach this extra meaning.)
The "go" command should cause your program to take the side of the color that
is currently on move and (after thinking) make a move.

- There is an "edit" command used for setting up the board when an EditPosition
is complete.  "edit" puts the chess program into a special mode, where it
accepts the following commands: "c" = change current piece color, initially
white; "pa4" (for example) = place pawn of current color on a4; "xa4" (for
example) = empty the square a4 (not used by xboard); "#" = clear board; "." =
leave edit mode.  "edit" does not change the side to move.  xboard sets up a
black-to-move position with a sequence like this: new, ..., force, a2a3, edit.

- xboard normally reuses the same chess process for multiple games.  At the end
of a game, xboard may send a SIGINT and the "force" command to make sure your
program stops thinking about the current position.  (Like the others, this
SIGINT is disabled by configuring xboard with --disable-sigint.)  It will later
send the initString again to start a new game.  If your program can't play
multiple games, give xboard the -xreuse command line option to disable reuse.
xboard will then kill off the process after each game and start a new one for
the next.

- xboard send the command "quit\n" to kill off the chess process.  This occurs
when xboard is exiting itself, and also between games if reuse is disabled.

- To diagnose problems, use the -debug flag to xboard to see the messages that
are being exchanged.  In WinBoard, these messages are written to the file
WinBoard.debug instead of going to the screen.

- You could also look at the file doc/CHESSTOOL in the gnuchess distribution, 
but it is so sketchy and out of date as to be pretty useless.

- Information about ICS time control:

Every ICS game has two parameters: (base, inc).  At the start of the game,
each player's clock is set to base minutes.  Immediately after a player makes
a move, inc seconds are added to his clock.  A player's clock counts down
while it is his turn.  Your flag can be called whenever your clock is zero or
negative.  (Your clock can go negative and then become positive again because
of the increment.)  A special rule: if you ask for a game with base=0, the
clocks really start at 10 seconds instead of 0.

ICS also has time odds games.  With time odds, each player has his own (base,
inc) pair, but otherwise things work the same as in normal games.  The Zippy
xboard accepts time odds games but ignores the fact that the opponent's
parameters are different; this is perhaps not quite the right thing to do, but
gnuchess doesn't understand time odds.  Time odds games are always unrated.

See the various help files on ICS for more details about what all the ICS
commands do, etc.  (See especially "help programmer" and "help style12".)

The Zippy xboard obtains the time control parameters from ICS when a new game
starts and passes them on to gnuchess using the "level" command.  gnuchess
understands ICS-style time controls.  They are specified with a command like:

    level 0 2 12

Here the 0 means "play the whole game in this time control period", the 2
means "base=2 minutes", and the 12 means "inc=12 seconds".

xboard also uses the gnuchess time and otim commands to keep the clocks more
closely synchronized.  They specify the computer's and opponent's current
clock time in units of 1/100 second.  For example,

    time 12345
    otim 23456

sets gnuchess's clock to 123.45 seconds and its opponent's clock to 234.56
seconds.

xboard always gives time and otim commands just before passing in a move from
the user.  This is done because giving any command to gnuchess makes it stop
its search.  xboard assumes your program will add the increment to the
opponent's clock after receiving the opponent's move, so it subtracts the
increment from the otim before sending it, to compensate.

- If you turn on -autoflag in xboard, xboard will automatically issue an ICS
"flag" command whenever it thinks your opponent's flag is down but yours is
not.  Due to netlag, xboard can be wrong about this.  If both flags are down,
calling them would result in a draw, so xboard plays on instead.

- xboard obtains the name of its opponent from ICS when a game starts and
stores it in the gameInfo struct.  (It's either gameInfo.white or
gameInfo.black depending on which color the opponent is playing.)  Zippy now
passes the name on to the chess program with the name command; for example,

    name mann

- xboard now obtains the ICS opponent's rating from the "Creating:" message
that appears before each game.  At this writing, only the ICC (chessclub.com)
generates this message, but it will appear on fics.onenet.net very soon, and on
other FICS sites whenever their administrators get around to picking up the
code change.  Zippy sends the ratings to the chess program using the "rating"
command.  The chess program's own rating comes first, and if either opponent is
not rated, his rating is given as 0.  For example,

    rating 2600 1500

- Zippy blindly issues an accept command for every challenge it gets, without
remembering anything about the challenge afterwards. This means that often it
will get several challenges very close together and try to accept them all!
ICS gives an error message for every accept command after the one that actually
starts a match, but xboard just happily ignores the message.  Zippy doesn't
actually start the chess program playing until the first board image comes in
from ICS.

- If the chess program sends "tellics foo" for any string foo, xboard
sends "foo\n" to ICS.  

- For compatibility with existing versions of Crafty, xboard also passes
through to ICS any line that begins "kibitz", "whisper", "tell", "draw", or
"resign".  Note therefore that "draw" from your program *offers* a draw by
agreement (and currently works only with ICS), while "Draw" declares the game
is definitely a draw.  This is unquestionably ugly.

- If your ICS opponent offers a draw by agreement, xboard sends "draw" to
your program.  You can reply with "draw" or ignore the offer.  "tellics draw"
or "tellics decline draw" also work.

- If the chess program sends "telluser foo" for any string foo, xboard pops up
a (possibly modal) information dialog that says "foo".

- If the chess program sends "tellusererror foo" for any string foo, xboard
pops up a non-modal error dialog that says "foo".

- If the chess program sends "askuser reptag foo" for any strings
reptag and foo, where reptag contains no whitespace, xboard pops up a
modal question dialog that says "foo" and has a typein box.  If the
user types in "bar", xboard sends "reptag bar".  The user can cancel
the dialog and send nothing.

- For debugging, you can send a command directly to the chess engine by
pressing Shift+1 (xboard) or Ctrl+Alt+1 (WinBoard).  This brings up the
askuser dialog with an empty reptag.  Press Shift+2 (Ctrl+Alt+2) instead
to send to the second chess engine (white) in Two Machines mode.

- xboard doesn't reliably detect illegal moves, because it does not keep track
of castling unavailablity due to king or rook moves, or en passant
availability.  If xboard sends an illegal move, send back "Illegal move" so
that xboard can retract it and inform the user.  It's generally okay to send
this message if xboard sends a command you don't understand, but it's best if
you include the command in the message; for example, "Illegal move (bogus)".

- xboard sometimes sends the command "bogus" to make sure GNU Chess
sends output that ends with a newline.  You can ignore this command or
just send a newline back.  Your output should always end with a
newline, or xboard will hang.

- If the user asks for a hint, xboard sends your program the command "hint".
Your program should respond with "Hint: xxx", where xxx is a suggested move.
If there is no move to suggest, you can ignore the hint command.

- If the user asks your program to "show thinking", xboard sends your program
the "post" command.  It sends "nopost" to turn thinking off.  The thinking
output should look like the output from GNU Chess or Crafty to be recognized.
Here is an example from GNU Chess:

 2.      4    0       88   e7e5  e2e4 
 3&     18    0      187   e7e5  e2e4  d7d5 
 3&     37    0      336   d7d5  d2d4  e7e6 
 3.     37    0      410   d7d5  d2d4  e7e6 
 4&      5    0      619   d7d5  d2d4  e7e6  e2e3 
 4&     10    0     1609   b8c6  e2e4  d7d5  b1c3 
 4.     10    0     2369   b8c6  e2e4  d7d5  b1c3 
 5&     15    0     3659   b8c6  e2e4  d7d5  b1c3  g8f6 
 5&     22    0     4930   d7d5  d2d4  e7e6  c2c3  b8c6 
 5.     22    1     9806   d7d5  d2d4  e7e6  c2c3  b8c6 
 6&      4    2    16403   d7d5  d2d4  e7e6  e2e3  b8c6  f1b5 
 6&     11    3    23320   b8c6  b1c3  g8f6  d2d4  e7e5  g1f3 
 6.     11    4    31322   b8c6  b1c3  g8f6  d2d4  e7e5  g1f3 

The first column gives the ply number.  xboard ignores the character after the
number.  The second column is the current score in centipawns.  The third
column is the time spent so far in seconds (ignored by xboard).  The fourth
column is the number of nodes visited (currently shown only in the Analysis
window).  Following that are the moves, which xboard displays verbatim.  It's
best to put only one space between them to conserve screen real estate.

If your program is pondering (thinking on its opponent's time) in post mode, it
can show its thinking then too.  In this case your program may omit the hint
move (the move it is assuming its opponent will make) from the thinking lines
*if and only if* it sends xboard the move in the usual "Hint: xxx" format
before sending the first line.

- If the user selects "Book" from the xboard menu, xboard will send your
program the command "bk".  You can send any text you like as the response, as
long as each line begins with a blank space or tab (\t) character, and you send
an empty line at the end.  The text pops up in a modal information dialog.

- If the user asks to back up one move, xboard will send you the "undo"
command.  xboard will not send this command without putting you in "force" mode
first, so you don't have to worry about what should happen if the user asks to
undo a move your program made.  (GNU Chess actually switches to playing the
opposite color in this case.)

- If the user asks to retract a move, xboard will send you the "remove"
command.  It sends this only when the user is on move.  Your program should
undo the last two moves and continue playing the same color.

- To enter analyze mode (currently supported only by Crafty), xboard sends the
command sequence "post", "white" or "black", "noise 1000", and "analyze".
Analyze mode in your program should be similar to force mode, except that your
program thinks about what move it would make next if it were on move.  To leave
analyze mode, xboard sends "exit".  In analysis mode with Periodic Updates
turned on, xboard periodically sends the chess engine the "." command.  This
should cause you to print a new line of thinking output, or you can ignore it.

