Chess-Engine Communication Protocol v2

by H.G.Muller

This document gives a compact description of CECP, a.k.a. WinBoard or Xboard protocol. It lists the commands as they would be given to the engine, with symbolically indicated parts in capitals, followed by a short explanation. Related commands or multiple forms of the same command are discussed together. There is not a very strict separation between engine -> GUI and GUI -> engine commands, to make it possible to discuss commands and the replies to them close together. Many commands are for very specialized tasks that most engines would never want to do, in which case there is no reason for them to implement the command. Such commands are marked as "[advanced]". In any case these would be good candidates to omit if you first implement the protocol.


For the main part CECP is based on exchanging single-line text messages between GUI and engine through 'pipes' set up by the GUI, and connected to the engine's standard input and output, and on MS Windows that would be the only communication channel. But on Linux it is also possible for processes to send 'signals' to each other, which interrupt their normal program flow. And CECP uses this when it sends commands that might have to interrupt the engine's search, so that the engine doesn't have to poll for input, but gets 'forcefully notified' when such input has arrived. Without special code being set up to handle the signal, the latter would terminate the engine process, though, and as Windows does not support signals at all, most engines rely on polling for input anyway, and do not need the signals. Engines can be made immune for the signals, but this does not work when they run as Windows binaries on Linux under Wine, as Wine itself will die by the signal. It is therefore recommended that engines that cannot handle signals during the initial handshake(see below) send 'feature sigint=0 sigterm=0', which should suppress the use of signals by the GUI. Then you don't have to take any special precautions for handling the signals, as you would never get them, and it has the advantage you will still be able to kill the engine with Ctrl-C when running from the command line.



protover N

The GUI always starts sending the 'xboard' command. Multi-protocol engines can use that to switch to XBoard mode. (Be sure that any prompt you might have printed in a previous mode is disarmed by printing a newline, so it won't prefix any subsequent command sent by the engine.) Non-obsolete GUIs immediately follow this by a 'protover' command (N specifying the version of the protocol it supports, which at present must be <= 2). The engine can reply to 'protover 2' with a number of commands of the form:

feature NAME=VALUE

feature NAME1=VALUE1 NAME2=VALUE2 ...

to tell the GUI what parts of the protocol it supports in deviation of the default. Text VALUEs should be surrounded by double quotes. The list of features transferred this way should always end with done=1, at the time when the engine is done initialyzing and is ready for use. It can start with done=0 to warn the GUI that it may take significant time before the done=1 will be emitted, so that it should not get impatient: without that GUIs can assume after a timeout period that no or no more feature commands will be coming.

The individual feature commands will be discussed with the commands of the protocol they affect. Some features, however, do not affect the protocol, but just convey information. One of those is 'feature myname="XXX"', which tells the GUI the name XXX of the engine. With 'feature reuse=0' an engine can indicate it should never be used to play more than a single game, and the GUI should start a new instance of it for every game. There is no limiting list of allowed features; an engine can send features with any (alphabetic) NAME. GUIs will respond to each NAME=VALUE pair in a feature command by one of the two commands:

accepted NAME

rejected NAME

depending on whether they recognize and implement the mentioned feature with that NAME and VALUE, or not. For most features it is not very relevant for the engine whether the GUI supports it; if the features were supposed to enable sending of commands added only in later versions of the protocol, (which could not be sent unconditionally for backward compatibility), it will simply never receive them. If the feature was supposed to disable sending of obsolete, redundant commands, a rejection will mean you get such commands anyway. So to be safe it is best you program the engine to silently ignore such commands in addition to disabling them with a feature.

ping N

Can be sent (with an arbitrary integer N) at any time by the GUI, but only to engines that have sent 'feature ping=1' at startup (which should be considered mandatory to have a properly working engine). GUIs should always support ping, and use it to prevent ambiguity in the engine output due to race conditions (like whether aborted thinking resulted in a move or not). The engine should respond with:

pong N

after having finished processing of all commands received earlier. Note that processing of a command that set the engine 'thinking' (= searching for producing a move) only ends when that move is printed (or the command is aborted so that no move will be coming)!


CECP is based on the design where the engine knows for which side it is playing (if any, or what else is expected of it), and will spontaneously start thinking and produce a move whenever the turn of that side comes up. The engine can thus be in either of four states { PLAY WHITE, PLAY BLACK, FORCE MODE, ANALYZE MODE }. In general the engine will accept commands silently, and should certainly not print a prompt. Some commands can trigger a search, however, and during this search the engine can print 'Thinking Output' informing the GUI on how the search is progressing. And at the end of a search the engine can print the move it has decided to play, possibly accompanied by a draw offer, or it can decide to terminate the game.

All command keywords are case sensitive, and contain only lower-case letters. Below capitals are used to symbolize command parameters that would describe the quantity suggested by the name.


The engine should immediately exit (i.e. terminate all its processes or threads).


Start game of normal chess, setting up the initial position for it. Turn randomization off, revoke any depth limit, and assume the opponent is human. Set engine to use wall-clock time for time measurement. Engine is set to play black, (except when it was in analyze mode, in which case it should stay in analyze mode), but should not start pondering in this position.


Engine is set to play neither side, so it will accept legal moves for both sides without ever starting to think or ponder. The engine can stop thinking immediately, without producing a move, and any move it would produce should be undone afterwards by the GUI (see 'undo' below).


[advanced] Engine is set to play the side currently not on move (and might start pondering as a result). Only sent to engines that have sent 'feature playother=1' at startup.


Engine is set to play for the side that is currently on move (and will start thinking as a result).


usermove MOVE

Feed the MOVE of a side the engine does not play (and which is on move) to the engine. As a result the other side's turn now comes up (which might set the engine thinking). The 'usermove' prefix is only used when the engine has requested it through "feature usermove=1". See appendix A for the MOVE format.

setboard FEN

Let the engine set up the position indicated by the FEN. Only sent to engines that have enabled this through "feature setboard=1" (recommended). Otherwise (or when the setboard feature is rejected) GUIs will use the 'edit' command when they want to set up a position. As in practice most engines do not account for the possibility that setboard will be rejected, supporting setboard=1 should be considered mandatory for any GUI. Variant engines should understand FEN formats in common use for that variant. On receiving a position the engine cannot handle, it is recommended to issue an error message (see below) like "Error (too many knights): setboard"


The opponent offers you a draw. Respond with 'offer draw' if you want to accept it; just ignore it to reject the offer. Do not count on the offer still to be on the table when you accept it. Sending of this command can be suppressed by sending 'feature draw=0', which could be useful if your engine's thinking would be aborted by receiving a 'draw' command during it. GUIs should protect you from this by waiting with relaying the offer only after the recipient moved, but not all GUIs do.

result RESULT


Specifies the game has ended with the mentioned RESULT (1-0, 0-1 or 1/2-1/2, from white point of view), for the given REASON (typically "checkmate", "50-move rule" etc.). In ICS mode RESULT can also be '*', for an unfinished (adjourned) game. It would be a good idea to let the engine stop thinking immediately on reception of this command.



Let the engine take back the last or last two half-moves, respectively. With the latter the same side stays on move, which can prevent an engine starts thinking, as it might do after the first of two 'undo' commands.


The single-character command '?' orders a thinking engine to consider its time up, and move immediately. Like the 'pause' command, this would necessitate the engine to watch for input while it is thinking. CECP allows both implementations that always abort thinking when input arrives, and implementations that only start checking for input after they are done thinking and have moved. In the latter case '?' will have no meaning.


[Obsolete] A multi-line command that is the default method for letting the engine set up a position, used with engines that have not sent 'feature setboard=1'. The command will be terminated by a line consisting of a single period. Until then, each line can contain a piece ID plus square name (like Ng1 or Ke8), which will place the mentioned piece on the mentioned square. A line '#' will clear the board, a line 'c' will switch to placing pieces of the other color (which at the start of the edit command will be set to place white pieces).

There is no way to specify castling or e.p. rights; pieces placed on the same square as they are on in the initial position should be assumed to be virgin, and e.p. capture should be assumed impossible. (It is up to the GUI to devise work-arounds for positions where this is not desired.) The 'edit' command is made obsolete by the setboard command, where the FEN specifies castling and e.p rights without the aid of any kludges.



[Obsolete] Sets the mentioned side to move, and sets engine to play the opposite side. Sending of these commands can be suppressed by sending 'feature colors=0' at startup. Made obsolete by the 'setboard' command, where the FEN already specifies the side to move.


move MOVE

Specifies the move the engine plays for the side it is playing for. See appendix A for the MOVE format.

offer draw

Used to offer, accept or claim draws. The engine should not count on the offer or claim being accepted, and be ready to play on. This command must be used before a move to claim draws that are only claimable after the move, and GUIs should always grant a draw when it gets claimable and a draw offer is pending.



Irreversibly terminates the game, RESULT (1-0, 0-1 or 1/2-1/2, from white POV) being the result according to the engine, for the specified REASON. Must be sent in checkmate, stalemate and insuccicient-mating-material positions, even when the engine is in force mode. A GUI could overrule the result when it knows better (e.g. forfeit the engine for a false claim).


An alternative for terminating the game as a loss, implying the reason.



While searching (and controlled by 'post'/'nopost', see below) the engine can print 'Thinking Output', which should start with at least 4 integer numbers, indicating search depth, score in centi-Pawn, nodes searched, and time searched in centi-seconds. PV is a text string representing the principle variation. SCORE is in centi-Pawn units, and the de-facto standard is to report it from the point of view of the side to move. TIME is in centi-second units. Between NODES and PV the engine can print the maximum depth of its selective search, the number of nodes per second, and the number of tablebase hits, when it uses a Tab character to separate these from the PV; GUIs that are not aware of this protocol extension will consider these extra parameters the initial part of the PV, an print them in the PV field, so be sure to use spaces and tabs to format them nicely.

When the engine is pondering on a speculative move, it should either start the PV with this move between parentheses, or send that move to the GUI before any other ponder output with the command:

Hint: MOVE

This command should also be used when the engine is not pondering, and the user asks it for a hint. (See the 'hint' commnand below.)

Illegal move: MOVE

Illegal move (REASON): MOVE

Reply to reception of a move that is not legal or valid in the current position. (Which by definition is the case if the position itself was invalid or undefined.) The GUI should retract the move it just sent the engine, or forfeit the engine and terminate the game if it thinks the claim is invalid.


Reply to a COMMAND that the engine does not understand. The ERRORTYPE is usually "unknown command", but could also be a specific complaint against command parameters, or the context in which the command is used. Should not be used in response to illegal moves, as it will not make the GUI take the move back. (And GUIs will only send moves to the engine that they approved of and performed themselves, so this would bring GUI and engine out of phase.)


time T

otim T

Specifies the time left on the clock (in centi-seconds) of the side for which the engine has to think up a move next, or on the clock of its opponent, respectively. (Usually sent just before a command that sets the engine thinking). Sending of these commands can be suppressed by sending 'feature time=0' at startup. (Never do it!) If the engine does not use the opponent time, be sure that you ignore the otim command in a way that would not interfere with recognizing an input move that follows it during ponder search as a ponder hit or miss!


st T

Two mutually exclusive commands (erasing each other's effect) for specifying the time control. In 'level' either MOVESPERSESSION equals 0 (for incremental and sudden-death TC), or INCREMENT is 0 (for classical TC). With 'st' the time control will be T seconds per move (not accumulating leftovers). The BASETIME parameter is in minutes, and can have the MIN:SEC notation. INCREMENT and T are in seconds. Engines are encouraged to understand a floating-point number for the INCREMENT, or at least to not choke on it.

sd N

Limits the nominal search depth to N ply, in addition to the time limits specified by 'level' or 'st' commands.


[advanced] Set the engine to use its own node count for any timing decisions, converting nodes to time by dividing the node count by the specified NODERATE. When NODERATE equals zero the engine should use its consumed CPU time to measure time, rather than the 'wall clock'. The specified NODERATE remains in force untile the next 'new' command.



Sets engine thinking on the current position, without ever making a move. In this mode the engine can only get moves, 'new', 'setboard', 'undo', '.', 'include' and 'exclude' commands, and some 'option' commands (in particular for the MultiPV option), and should then automatically switch to analyzing the new positions that come up by this. Sending of this command can be suppressed by sending 'feature analyze=0' at startup, in which case the user cannot use the engine for analysis.


Only sent in analyze mode, to switch back to 'force mode', where the engine plays neither side.


This 1-character command, sent only in analyze mode, requests the engine to immediately tell how far its search has progressed. The engine should either ignore it, or reply with:



which mentions the TIME and NODES used by the search so far, the DEPTH of the iteraton currently in progress, the number of moves still to be searched in the current depth iteration, the total number of moves in the current position, and (optionally) the move that is being considered now.

exclude MOVE

include MOVE

exclude all

include all

[advanced] Only sent in analyze mode, to engines that have sent "feature exclude=1" at startup, to include or exclude the mentioned move from the analysis in the current position. The effect of these commands should be reset any time a new position comes up, or when analyze mode is left.

setscore N

[advanced] Define the score of the current position as N (centi-Pawn from side-to-move POV), to be used whenever an analysis search hits this position. Only set to engines that enabled it through 'feature setscore=1' at startup.


memory N

Specifies the engine can use at most N megabytes of memory. Only sent when the engine requested it through feature memory=1.

cores N

Specifies the engine can use at most N search threads in is parallel search. Only sent when the engine requested it through feature smp=1.

egtpath TYPE PATH

Informs the engine where in the file system it can find end-game tables of the mentioned TYPE. (Common types are nalimov, scorpio, gaviota and syzygy.) Sent for each TYPE that the engine listed in its egt feature (feature egt="TYPE1,TYPE2,..."), or not at all if such a feature was not sent at startup.


Toggles any explicit randomization by the engine on or off.



Switches printing of 'Thinking Output' during the engine search on or off, respectively.



Switches pondering (thinking during the turn of the side the engine does not play) on or off, respectively.


option NAME

Sets the value of an engine-defined option with the specified NAME (which is an arbitrary text string not containing equal signs). The second form is used for 'button' options, to indicate the user pressed the button; other option types will transfer a value. Only sent for options that the engine declared at startup through:

feature option="NAME -spin VALUE MIN MAX"

feature option="NAME -combo TEXT1 /// TEXT2 /// ..."

feature option="NAME -check VALUE"

feature option="NAME -string TEXT"

feature option="NAME -file FILENAME"

feature option="NAME -path FILENAME"

feature option="NAME -button"

feature option="NAME -reset"

feature option="NAME -save"

For -check options the VALUE must be 0 or 1. MIN and MAX indicate the valid range of a -spin option, which has an integer VALUE. The -string, -file and -path options convey an arbitrary text, but the latter two indicate to the GUI that this text represents a file or directory name, so that the appropriate Browse button can be displayed with it in the settings dialog. The -button, -reset and -save options are all instantaneous signals, but -save indicates to the GUI it should first flush all other settings to the engine when the corresponding button is pressed (which otherwise it might only do after the user presses some 'OK' button in the settings dialog), while -reset options will erase the list of options in the GUI as a side effect of their activation, and expect the engine to send a new list ofoptions.

If a GUI wants to reject an option feature (e.g. because the syntax of the string describing it is incorrect), it should say 'rejected option NAME', i.e. include the NAME of the option in the 'rejected' command, as unlike with other features, there can be multiple option features that need to be distinguished. If other features appear in duplicate, only the last one should be obeyed, but for option features the GUI should collect them in a list of available options. This list should be cleared on reception of 'feature done=0'.



When the engine has sent 'feature debug=1' at startup, and the GUI 'accepted' this feature, any line starting with a '#' character is guaranteed to be completely ignored by the GUI. This is the recommended way to have your engine print messages for debugging purposes. Although most GUIs are usually pretty resistant to non-compliant garbage the engine sends them, there is never the guarantee that what you send them will not get a meaning in future protocol extensions, if you don't send it in this way.


Asks the engine to name a good move in the current position (without playing it). The engine should reply with a 'Hint' command. The most common implementation is to send the move on which the engine would ponder.


[advanced] Invites the engine to send a number of text lines starting with a Space or Tab character to the GUI, terminated by an empty line, for display as a table to the user. Intended for book or tablebase moves the engine has available in the current position.


[advanced] Sent to the GUI for presenting the MESSAGE to the user in a dialog to which the user can type a reply. This reply will then be sent to the engine as a line REPLYTAG ANSWER.

telluser MESSAGE

tellusererror MESSAGE

Requests the GUI to presend the specified message to the user, as a 'note' or an 'error', respectively. In the latter case no additional error messages should be given when the engine process suddenly dies.

tellall MESSAGE

tellothers MESSAGE

tellopponent MESSAGE

[advanced] When the opponent is an ICS, these commands can be used to send the specified MESSAGE to selected groups of ICS users: All people watching the game, all except the opponent, or just the opponent, respectively. (This corresponds to the ICS commands 'kibitz', 'whisper' and 'say', respectively.)

tellics COMMAND

tellicsnoalias COMMAND

[advanced] When connected to an ICS, send the specified command to it. In the second case the COMMAND will be prefixed by the character that will prevent the ICS to remap it to some other command defined as an alias.

name NAME

[advanced] Informs the engine about the name of the opponent. Only sent in non-ICS mode when the engine requested it by sending 'feature name=1' at startup, while sending 'feature name=0' there would suppress sending of this command even in ICS mode.


[advanced] Informs the engine on the name of the ICS it is playing on. (When not playing on an ICS, the hyphen character '-' will replace the ICSNAME.) Only sent to engines that have requested it with 'feature ics=1' at startup.


[advanced] Informs the engine of the rating of the players. Usually only used when connected to an ICS, which would provide the ratings.


[advanced] Informs the engine that the opponent is a computer.



[advanced] On receiving 'pause' the engine should suspend all activity, including measuring the progress of time, until it receives a subsequent 'resume' command. Then it should continue with whatever it was doing like nothing happened. Only sent to engines that sent 'feature pause=1' at at startup.


CECP is designed such that the GUI need not have much knowledge of the game rules, and can leave the engine in control: The engine can decide which moves are legal and which are not by rejecting the illegal ones with an 'Illegal move' command, and declare game end with a particular result through the RESULT command. In this section a number of commands are discussed that could make the engine do even more to control the GUI. Like determining the board size, setting up the initial position, highlighting squares where a 'picked-up' piece can move to, etc. But GUIs can have built-in knowledge of one or many variants, and need not follow the engine's lead in that case.

variant VARIANT

Optionally sent to the engine immediately after 'new' for games that use other than FIDE rules. Sets the engine to play mentioned variant, starting in the initial position for it. The engine stays playing for the side that does not have the move, but should not start pondering on this position. Only used with VARIANTs that the engine announced it could play in the 'feature variants="VARIANT1,VARIANT2,..."' command at startup. Not listing "normal" amongst the variants implies the engine is not able to play orthodox Chess. It is up to the GUI to decide what it can request the engine to play if the engine does not emit any variants feature.

setup FEN



This engine-to-GUI command informs the GUI of the start position of the selected variant. The first two forms are used when a standard variant is used for playing another variant (which could be a 'catch-all' for a family of variants like 'fairy', or a precisely defined one with legality checking off). The PIECETOCHAR string is necessary when the FEN contains other pieces than normally used in the specified variant, and informs the GUI what piece image to use for which piece, by specifying the ID letter assigned to each of the GUIs internal piece types. For non-standard variants the third form can specify the PARENTVARIANT from which the rules will be inherited, plus the board (Width x Height) and holdings size (S).


Also in response to the 'variant' command the engine can send a number of 'piece' commands, to define how some pieces move that are specified (in the PIECETOCHAR part of a preceding 'setup' command) or implied to participate in the variant. The ID is the single-letter ID used for the piece in FEN, (so that white and black pieces can be defined separately), but can also be a white (= upper-case) ID followed by an ampersand '&', to indicate the definition applies to pieces of both colors. The MOVEDESCRIPTION specifies how pieces of this type are allowed to move, in XBetza notation. Pieces not defined by a 'piece' command and not naturally appearing in the selected standard variant or the specified parent variant will move according to the GUI's idea of that piece type, which is essentially undefined.



hover SQUARE

[advanced] Informs the engine that the user selected a piece from the mentioned SQUARE, put a previously selected piece on the mentioned SQUARE, or hovers the mouse pointer above the mentioned SQUARE, respectively. Only sent when requested by the engine with feature highlight=1. The engine can reply to these commands with:

highlight COLORFEN

where the COLORFEN is the board part of a FEN where the letters indicate colors, rather than pieces (Red, Yellow, Green, Cyan, Blue, Magenta, White, Dark or Transparent), which the GUI then can use to mark / highlight the corresponding squares in that color. GUIs can use these markings to decide upon legality of moves, where colors indicated in capitals are considered legal destinations. Colors that have been assigned a special meaning when the lifted piece lands on them are Magenta (promotion) and Cyan (non-final leg of a multi-leg move). XBoard emits 'hover' commands when the mouse enters a square marked in red, which could be used by the engine to highlight victims of side-effects captures made on going to that destination. The GUI is expected to reset the color markers when the piece ceases to be selected, or revert to the marking pattern existing when the mouse pointer entered a 'hover' square when it again leaves it.



Specifies to the engine what is in the holdings during bughouse games. WHITE and BLACK are a list of capitalized piece IDs enclsed in brackets, like [PPPRQ] or []. The second form indicates NEWPIECE was just added, where the latter is a 2-letter combination, the first letter a W or B indicating the color, the second the capitalized piece ID.


partner NAME

Tells you who your bughouse partner is on the ICS. The first form means you no longer have a partner.


Your bughouse partner sent you the mentioned MESSAGE through an ICS tell or ptell command.


Moves should be specified in coordinate notation, e.g. e2e4 or g8f6.
For promotions the chosen piece should be indicated by a lower-case suffix, e.g.a7a8q or e2e1n.
Castlings are indicated as the King move, e.g. e1g1 or e8c8. FRC castling: O-O or O-O-O (oh, not zero!).
Crazyhouse/bughouse drops: N@e6, P@e7 (piece always capitalized).
Multi-leg moves separate the legs by commas: e.g. c5d5,d5e4. For engine to GUI this has to be sent as a separate 'move' command for each leg, the non-final legs suffixed with a comma.
Null move: @@@@.

On boards with exactly 10 ranks, rank counting starts at 0. Some GUIs understand SAN, but don't count on it. The engine can request that the GUI sends its moves in SAN too, by sending 'feature san=1' at startup. Use of SAN is not recommended, though.


The following variants are standard variants. They can be used as 'parent variant' for non-standard (= engine-defined) variants, which will then inherit their rules. Except for board/holdings size, participating pieces and initial setup, which will be explicitly specified in the 'setup' command sent by the engine, and possibly piece movement, which can be redefined through 'piece' commands. But that still leaves matters like promotion-zone depth, type of holdings (captured own pieces or color-flipped enemy pieces), promotion procedure (select any participating type, or only from holdings), game result for stalemate and checkmate, whether capture is mandatory or pieces explode on capture.

Note that the user can also redefine participating pieces, initial position and board / holdings size, through command-line options, effectively using the selected variant as parent variant. Such a user-modified variant is announced to the engine with a prefix indicating the board and holdings size, like 7x7+6_shogi, so the engine can distinguish it from the standard variant (and possibly support both at once).

giveaway mandatory capture, no royal, win by being stalemated
crazyhouse captured pieces can be dropped
twokings multiple Kings, one closest to a1 is royal
3check win by giving 3rd check
atomic capturing pieces explode, and destroy all neighnoring non-Pawns
shatranj win by check or stalemate, or baring opponent King
shogi all pieces can promote
knightmate royal piece moves as Knight
berolina Pawns move diagonally, capture straight
cylinder board wraps on left-right edges
super shuffle, promotion only to captured or substituted pieces
great promotion only to captured pieces
chu Many more piece types as in other variants.


The following pseudo-code describes a protocol driver for a full-featured engine, which would even react to commands during thinking, such as "quit" or "?" (move now). Lines that are only needed for supporting a particular feature are clearly indicated, and could be left out in a "mean-and-lean" engine that does not implement that feature.

// some state variables
int stm;          // side to move (WHITE or BLACK), part of game state
int mode;         // side the engine plays, or what else it should do (WHITE, BLACK, FORCE or ANALYZE)
int activity;     // purpose of the search (THINK, PONDER, ANALYZE)
int ponder;       // whether we must think in opponent time (ON or OFF)
int post;         // whether we must print info during search (ON or OFF)
int tcMode;       // type of time control (CLASSICAL, INCREMENTAL, FIXED)
int timerMode;    // how to measure time (WALL_CLOCK, CPU, or derive from node count)

// more time-control parameters and variables
int movesPerSession, baseTime, timePerMove, myTimeLeft, oppoTimeLeft, startTime;

// other stuff (which might not be of much interest to your engine)
int computerOppo;    // whether opponent is a computer (TRUE, FALSE)
String opponentName; // name of opponent
String icsName;      // name of Internet Chess Server

String line; // input buffer
int backloggedCommand = FALSE;

char exclude[MAXMOVECODE]; // exclusion map
Move ponderMove = INVALID; // expected move of opponent

int GetTime()
  if(timerMode == WALL_CLOCK) return GetWallClockTime();
  else if(timerMode == CPU)   return GetUsedCpuTime();
  else                        return nodeCount/timerMode;

int ParseTimeControl(char *s)
  words = Split(s, ' ');                      // split string into words at space boundaries
  parts = Split(words[1], ':');               // split base-time spec into words along colon boundaries
  movesPerSession = StringToNumber(words[0]); // first word: session length of classical TC
  timePerMove     = StringToNumber(words[2]); // third word: Fischer increment
  minutes         = StringToNumber(parts[0]);
  if(parts[1]) seconds = StringToNumber(parts[1]; else seconds = 0; // defaults to 0
  baseTime = 60*minutes + seconds;
  if(movesPerSession != 0) return CLASSICAL; else return INCREMENTAL;

void SetExclusionMap(Move move, int state)
  if(move != INVALID) exclude[move] = state;
  else for(i=0 to MAXMOVECODE) exclude[i] = state; // when move was "all"

int ExecuteOneCommand(int activity)
// returns whether the next command requires aborting the search
  paused = FALSE;
  do {
    if(!backloggedCommand) line = ReadLine(); // assumes end-of-file in ReadLine will make the engine exit
    backloggedCommand = FALSE;
    command = FirstWord(line); params = RemainderOf(line);
    print('# arrival of command: ' + command + '\n'); fflush(stdout);      // example of non-protocol output for debugging purposes
    if(command == "resume") { startTime += GetTime() - time; return FALSE; // optional (pause=1); corrects startTime for pause duration
    if(command == "pause")  { time = GetTime(); paused = TRUE; }           // optional (pause=1); remember when pause started
  } while(paused); // optional (pause=1); loop ignoring all input (which is not supposed to come!) until we receive 'resume'

  // first handle commands that can be executed during search (so they can return FALSE to let the search continue)
  if(command == ".")         { print('stat01 '+ (GetTime() - startTime) + ' ' + nodeCount + ' ' + rootDepth + ' ' +
                                     nrOfRootMovesLeft + ' ' + nrOfRootMoves + ' ' +  rootMove + '\n'); fflush(stdout); return FALSE; }
  if(command == "time")      { myTimeLeft   = StringToNumber(params); return FALSE; }
  if(command == "otim")      { oppoTimeLeft = StringToNumber(params); return FALSE; }
  if(command == "draw")      { drawOfferPending = TRUE; return FALSE; } // or ignore if you don't do draws
  if(command == "post")      { post = ON;    return FALSE; }   // ignoring post/nopost and always prining thinking output ...
  if(command == "nopost")    { post = OFF;   return FALSE; }   // ... is an almost unnoticeable non-compliancy
  if(command == "easy")      { ponder = OFF; return FALSE; }   // or ignore if you cannot ponder
  if(command == "hard")      { ponder = ON;  return FALSE; }   // or ignore if you cannot ponder
  if(command == "ping" && activity != THINK) { // when thinking, pong reply must be postponed to after we move
    nr = StringToNumber(params); print('pong ' + nr + '\n');
    return FALSE;

  // then some command that should abort the search even when we are thinking (and thus return TRUE, or worse)
  if(command == "?")         { return (activity == THINKING); } // ignored (meaningless) during pondering or analysis
  if(command == "quit")      { exit(0); } // always obey quit command
  if(command == "force")     { mode = NONE; return TRUE; }
  if(command == "result")    { Process(params); mode = NONE; return TRUE; } // 'Process' could do learning based on result

  // figure out if we are dealing with move (without 'usermove' prefix this is a bit flaky)
  moveText = NULL;
  if(command == "usermove")  moveText = params;  else    // only when we sent usermove=1
  if(!isalpha(command[1]))   moveText = command; else    // for when usermove=1 was rejected, also handy when playing from command line
  if(isupper(command[0]))    moveText = command;         // optional (san=1) to recognize bare SAN moves
  if(moveText != NULL && activity != IDLE) { // during search we might have to examine move for being ponder hit
    switch(activity) {
      case THINK:   break;               // opponent moved while we are thinking??? Should not happen, but defer processing to main loop
      case ANALYZE: return TRUE;         // during analysis moves always abort search
      case PONDER:  if(ponderMove == ParseMove(moveText)) { // ponder hit
                      myTimeLeft += GetTime() - startTime;  // time gained by pondering (if we don't want to rely on 'time' command)
                      activity = THINK;  // switch from pondering to thinking
                      return FALSE;      // and continue search
                    } else return TRUE;  // ponder miss: abort search

  // do not process remaining commands during search, but do abort ponder or analysis search to do it immediately in main loop
  if(activity != IDLE) { backloggedCommand = TRUE; return (activity != THINKING); }

  postionChanged = TRUE;     // assumption for the eight following commands
  if(command == "undo")      { UnMake(); } else
  if(command == "remove")    { UnMake(); UnMake(); } else
  if(command == "setboard")  { stm = Setup(NULL, params); } else
  if(command == "white")     { mode = BLACK; stm = WHITE; } else
  if(command == "black")     { mode = WHITE; stm = BLACK; } else
  if(command == "exit")      { mode = NONE; } else                  // for analysis support only
  if(command == "new")       { if(mode != ANALYZE) mode = BLACK; stm = WHITE; Setup("normal", FIDE_FEN); moveNr = 0;
                               maxDepth = INFINITE; timerMode = WALL_CLOCK; computerOppo = FALSE; } else
  if(moveText != NULL) {     // input move
    move = ParseMove(moveText);   // in main loop we really do it
    if(MakeMove(move) == ERROR) print('Illegal move: ' + moveText + '\n');
  } else positionChanged = FALSE; // revoke assumption if non of the above

  if(positionChanged) {      // one of the above game-state-changing commands was executed (or we left analysis mode)
    SetExclusionMap(FALSE);  // optional (exclude=1)
    ponderMove = INVALID;    // don't bother with this if the engine cannot ponder
    return WHATEVER;         // no one is looking at this

  if(command == "analyze")   { mode = ANALYZE; } else     // only if you support analysis, which is highly recommended
  if(command == "level")     { tcMode = ParseTimeControl(params); } else
  if(command == "st")        { tcMode = FIXED; timePerMove = StringToNumber(params); } else
  if(command == "sd")        { maxDepth  = StringToNumber(params); } else
  if(command == "nps")       { timerMode = StringToNumber(params); } else // optional (nps=1)
  if(command == "playother") { mode = Opponent(stm); } else               // optional (playother=1)
  if(command == "go")        { mode = stm; } else
  if(command == "include")   { move = ParseMove(params); SetExclusionMap(move, FALSE); } else // optional (exclude=1)
  if(command == "exclude")   { move = ParseMove(params); SetExclusionMap(move, TRUE);  } else // optional (exclude=1)
  if(command == "xboard")    { print("\n"); } else     // make sure next CECP engine->GUI command starts on a fresh line
  if(command == "protover")  {
    print('feature done=0 myname="Example 1.0" ping=1 memory=1 setboard=1 debug=1 sigint=0 sigterm=0\n'); // always support these!
    print('feature name=1 ics=1\n');                   // likely you would not want to support these, and leave out this line
    print('feature usermove=1\n');                     // when you don't trust you can distinguish bare moves from other commands
    print('feature egt="syzygy,scorpio"\n');           // when you support tablebases of the mentioned kind(s)
    print('feature variants="normal,suicide,foo"\n');  // when you support variants other than orthodox chess
    print('feature nps=1\n');                          // when you support node-count-based time controls
    print('feature smp=1\n');                          // when you support multi-threaded parallel search
    print('feature exclude=1\n');                      // when you support analysis with move exclusion
    print('feature option="MultiPV -spin 1 1 100"\n'); // 3 examples of engine-defined options, first sort of standard
    print('feature option="Resign -check 0"\n');
    print('feature option="Clear Hash -button"\n');
    print('feature done=1\n');                         // never forget this one!
  } else
  if(command == "option") { // recognize the options you defined yourself (could do some during search?)
    name = FirstWord(params); value = TextBehindEqualSign(params);
    if(name == "MultiPV") nrOfPV = StringToNumber(value); else
    if(name == "Resign")  resign = StringToNumber(value); else
    if(name == "Clear")   ClearHashTable();
  } else
  if(command == "rejected")  { ; } else // ignore for now
  if(command == "accepted")  { ; } else
  if(command == "memory")    { size = StringToNumber(params); ReallocateHash(size); } else // must-have when your engine has hash table
  if(command == "cores")     { searchThreads = StringToNumber(params); } else // optional (smp=1)
  if(command == "egtpath")   { egtType = FirstWord(params); egtPath[egtType] = RemainderOf(params); } else // optional (egt="...")
  if(command == "variant")   { // optional (variants="...")
    variantName = FirstWord(line+8);
    if(variantName == "suicide") Setup("suicide", FIDE_FEN); else // standard variant, just set up game
    if(variantName == "foo") { // example of an engine-defined variant
      fooFEN = 'rnbfqkfbnr/pppppppppp/10/10/10/10/PPPPPPPPPP/RNBFQKFBNR w KQkq - 0 1';
      print('setup ( 10x8+0_fairy ' + fooFEN + '\n');
      print('piece F& mNcK\n');
      Setup("foo", fooFEN);
  } else
  // The following commands could simply be ignored (even replied to with 'Error') if you are not interested in the info they convey
  if(command == "computer")  { computerOppo = TRUE; } else
  if(command == "rating")    { whiteRating = StringToNumber(params); blackRating = StringToNumber(RemainderOf(params)); } else
  if(command == "ics")       { icsName = params; } else      // optional (ics=1)
  if(command == "name")      { opponentName = params; } else // optional (name=1 or in ICS mode)
  if(command == "bk")        { for(i=0 to NRBOOKMOVES) print(' ' + BookMove(i) + '\n'); print('\n'); } else // if you have a book
  if(command == "") { ; } else
  if(command == "") { ; } else
  printf("Error (unknown command): %s\n", command);

  backloggedCommand = FALSE;
  return WHATEVER; // no one is looking at this

// called regularly during search (e.g. every 10 msec)

void MustStop()
  if(activity == THINKING && GetTime() - startTime > coldTurkeyDeadline)) return TRUE; // time is up
  // When we already backlogged a command that apparently did not abort search we stop checking for new ones
  // (should not happen; the GUI should send only commands that are processible during search, or abort it)
  while(!backloggedCommand && InputPending()) { // execute all pending commands that can be executed
    print("# search interrupted by input\n");   // example of non-protocol output for debugging purposes
    if(ExecuteOneCommand(activity)) abortFlag = TRUE;

// main loop

mode = FORCE; // kludge to prevent engine starts doing anything before receiving any commands

while(1) {

  mustPonder = (ponder == ON && moveNr != 0 && mode == Opponent(stm));
  startTime = GetTime(); nodeCount = abortFlag = 0; // initialize search stats

  if(mode == ANALYZE || mustPonder && ponderMove == INVALID) { // analyze or ponder position (search without moving)

    activity = ANALYZE;

  } else if(mode == stm || mustPonder) { // think or ponder speculative move (search with intention to move)

    if(mustPonder) {        // make the opponent move speculatively
      activity = PONDER;
      MakeMove(ponderMove); // this flips stm, so that it now always is the engine's turn!
      print("Hint: " + MoveToText(ponderMove) + "\n");
    } else activity = THINK;

    score = Search(&bestMove, &bestReply); // sets bestMove and ponderMove

    if(activity == THINK) { // might also be because ponder hit changed activity to THINK!
      ponderMove = bestReply;
      if(score < -600 && resign) print("resign\n"); else {        // resign without making move!
        if(score < 0 && drawOfferPending) printf("offer draw\n"); // accept draw offer
        printf("move %s\n", MoveToText(bestMove));                   // print move
        myTimeLeft -= GetTime() - startTime;                         // if we don't want to rely on 'time' command
        drawOfferPending = FALSE;                                    // offer expires as we move
        result = DetermineResult();
        if(result == Unfinished) continue;  // try if we must search before hanging on input
        print((result == WhiteWins ? "1-0" : result == BlackWins ? "0-1" : "1/2-1/2") + "\n");
      mode = FORCE;
    } else {    // ponder search was aborted (or finished by itself when there was not enough to ponder)
      UnMake(); // take back the speculatively preformed poderMove (true move will be backlogged)


  fflush(stdout); // flush output buffer, to be sure everything is sent to GUI before we hang for input



protover 2
                   feature done=0 ping=1 memory=1 usermove=1 setboard=1 debug=1 sigint=0 sigterm=0
                   feature variants="normal,king-of-the-hill,light-brigade"
                   feature option="resign threshold -spin 0 0 10000"
                   feature done=1
level 40 5 0
memory 64
variant light-brigade
                   setup (P...QKpn...k) 8x8+0_fairy nnnnknnn/pppppppp/8/8/8/8/PPPPPPPP/1Q1QK1Q1 w - - 0 1
ping 1
usermove e2e4
usermove e7e5
usermove d1h5
time 30000
otim 30000
                   pong 1
                   # start searching
                   1 30 0 33 b8c6
                   2 29 0 216 g8f6 h5
                   move g8f6


The following table list all currently defined features with their default value. Most features are 'boolean', and can only have value 0 or 1. In this case the description tells what the option would do when the value is set to 1. A question mark indicates no default value is defined. 'Must haves' are shaded in red; practically useless features (enabling redundant, undesirable or very specialized commands, or disabling commands you might as well ignore) in yellow. Features shaded in green are only useful for engines that play variants.

done=? Tells the GUI no more features will follow (done=1) or (with done=0) that the GUI must wait for done=1 no matter how long that takes. Always use done=1 as the last feature.
sigint=1 Enables the sending of SIGINT to the engine on Linux. Always use sigint=0.
sigterm=1 Enables the sending of SIGTERM to the engine on Linux. Always use sigterm=0.
ping=0 Enables use of the 'ping' command to resolve timing-induced ambiguity in the communication. Always use ping=1.
setboard=0 Requests use of the 'setboard' command instead of the 'edit' command to set up positions. Always use setboard=1.
myname="???" Conveys the name of the engine. Always use.
memory=0 Enables use of the 'memory' command to specify maximum memory usage. Always use memory=1 on engines with hash table.
smp=0 Enables use of the 'cores' command to specify the maximum number of search threads. Always use smp=1 on engines with parallel search.
egt="" Specifies (as a comma-separated list) which end-game tables the engine can use, so that the GUI can send 'egtpath' commands for each type to tell the engine where to find the associated files. Commonly used type are 'scorpio' (bitbases), 'nalimov', 'gaviota' and 'syzygy', but any other name would be allowed too.
reuse=1 Controls if the engine can be used to play multiple games (reuse=1), or whether a new process has to be started for each game (reuse=0).
usermove=0 Enables usage of the "usermove " prefix on moves.
debug=0 Requests the GUI to completely ignore lines starting with a '#' character. Recommended method for printing debug output.
draw=1 Enables use of the 'draw' command for offering draws to the engine.
option="???" Defines an option command the GUI could use later to alter a non-standard engine parameter. Option features accumulate rather than redefine the previous occurrence, and by default the GUI's list of engine-defined options is empty.
pause=0 Enables use of the 'pause' and 'resume' commands for instanly pausing the engine.
nps=1 Enables use of the 'nps' command for selecting node-based time controls.
analyze=1 Enables use of the engine for interactive analysis through the 'analyze' command.
exclude=0 Enables use of the 'include' and 'exclude' commands for excluding moves from analysis.
setscore=0 Enables use of the setscore command for defining the evaluation of the current position.
variants="???" Restricts the GUI's use of the 'variant' command to the mentioned variants. The value is a comma-separated list of variant names, and although no default is defined, most Chess GUIs are likely to assume an engine plays only "normal" (i.e. orthodox Chess).
highlight=0 Enables use of the 'lift', 'put' and 'hover' commands to make the engine aware of user piece manipulation.
playother=0 Enables use of the 'playother' command for setting the engine playing.
ics=0 Enables use of the 'ics' command for telling the engine the name of the ICS it is playing on.
name=? Enables use of the 'name' command for telling the engine the opponent's name. By default the GUI might send the 'name' command only in some cases.
colors=1 Enables use of the 'white' and 'black' commands.
times=1 Enables use of the 'time' and 'otim' commands. (Never disable them!)
san=0 Requests the GUI to send moves in SAN format rather than coordinate notation. Use of SAN is not recommended.