Suggestions for XBoard documentation

Discussion of chess software programming and technical issues.

Moderators: hgm, Harvey Williamson, bob

Forum rules
This textbox is used to restore diagrams posted with the [d] tag before the upgrade.
User avatar
sje
Posts: 4675
Joined: Mon Mar 13, 2006 6:43 pm

Suggestions for XBoard documentation

Post by sje » Sun Aug 30, 2015 2:21 pm

Here are some suggestions for XBoard documentation to assist those writing an interface between their program and XBoard.

1) Recommend that directives not needed in protocol two not be supported as they are not necessary if protocol two is being used with SAN and FEN enabled. This eliminates both confusion and effort. When in doubt, dike it out. So, white, black, edit, and maybe several other directives need not be implemented.

2) Establish a minimal set of well-described state variables which the programmer's side of the interface must maintain. For each variable, explicitly state its initial default value. For each directive sent by XBoard, explicitly state which of these variables are changed and how their new values are calculated. Do not allow any single state variable to have more than one meaning; e.g., force mode should be a boolean and be separate from the color played by the program. Parenthetically, the list of state variables should include whatever information is needed for the program to generate PGN tag pairs.

3) To make description of the state variables easier, introduce a Role structure which describes color, name, rating, clock time, and other per-player status. The program should have in its state variable set a two element array of these Role objects, indexed by the constants RoleOppo (the opponent) and RoleSelf (the program).

So, the description for the directive rating would be something like:

RoleVec[RoleSelf].elo = atoi(arg(1));
RoleVec[RoleOppo].elo = atoi(arg(2));

Other directive descriptions like result would be more complex, but that's where the help is most needed.

Symbolic's Role class:

Code: Select all

class RoleDesc
{
public:
  RoleDesc(void) {Reset();}

  void Reset(void);

private:
  friend class XbdCoPro;

  Color       rolecolor;   // Playing color
  bool        rolecomp;    // Computer status
  std::string rolename;    // Name
  ui          rolerating;  // Rating
  Usec        roleusec;    // Remaining microseconds
};
Symbolic's Xboard state variables:

Code: Select all

  ui             gamecount;   // Number of "result" directives received
  bool           oneshot;     // True if one time post connection performed
  std::string    icsname;     // Internet Chess Server name; "-": local
  bool           icslocal;    // ICS local flag

  bool  isforce;   // Forced mode flag
  bool  ishard;    // Ponder enable flag
  bool  ispost;    // Post enable flag
  bool  israndom;  // Random enable flag
  Usec  leviusec;  // Level: increment microseconds
  Usec  levpusec;  // Level: period microseconds
  ui    levtcmc;   // Level: time control move count
  ui    limitsd;   // Depth limit (ply)
  ui    limitst;   // Time limit: seconds

  RoleDesc rolevec[RoleLen];  // Play role indexed data
The above is in addition to the current game/position, also a state variable.

User avatar
hgm
Posts: 22911
Joined: Fri Mar 10, 2006 9:06 am
Location: Amsterdam
Full name: H G Muller
Contact:

Re: Suggestions for XBoard documentation

Post by hgm » Sun Aug 30, 2015 9:26 pm

I think part of that is a bad idea. CECP does not really have a state variable 'force mode'; it just has a state variable 'mode', that can attain the values { force, play white, play black, analyze }. Other state variables are the game state (board, holdings, side to move, castling rights, e.p. rights), and the clock settings {my time, his time }, which are integers, as is the maximum search depth.

There is a quite simple 'model' protocol driver on WinBoard forum that should be pretty clear.

D Sceviour
Posts: 390
Joined: Mon Jul 20, 2015 3:06 pm
Contact:

Re: Suggestions for XBoard documentation

Post by D Sceviour » Sun Aug 30, 2015 9:44 pm

Steven Edwards wrote.
...force mode should be a boolean and be separate from the color played by the program
The documentation states force can be followed by moves to play. It is confusing for the novice. It is not clear if it means:

Code: Select all

force e2e4 e7e5
or

Code: Select all

force		;bool is correct
e2e4
e7e5

D Sceviour
Posts: 390
Joined: Mon Jul 20, 2015 3:06 pm
Contact:

Re: Suggestions for XBoard documentation

Post by D Sceviour » Sun Aug 30, 2015 10:31 pm

Also note the crafty documentation that states:
"force" command forces the program to make a specific move instead of its last chosen move.

printf("usage: force <move>\n");
Is this correct?

bob
Posts: 20358
Joined: Mon Feb 27, 2006 6:30 pm
Location: Birmingham, AL

Re: Suggestions for XBoard documentation

Post by bob » Sun Aug 30, 2015 11:33 pm

D Sceviour wrote:Also note the crafty documentation that states:
"force" command forces the program to make a specific move instead of its last chosen move.

printf("usage: force <move>\n");
Is this correct?
No. That is a crafty command, not xboard command. If xboard mode is set, that doesn't happen. It is made for console mode where you don't like the move it played and can tell it "force other-move" and it will retract its last move and play that instead.

In xboard mode it simply sets "force = 1" and returns... that prevents searches and it just reads moves in.

User avatar
sje
Posts: 4675
Joined: Mon Mar 13, 2006 6:43 pm

Re: Suggestions for XBoard documentation

Post by sje » Sun Aug 30, 2015 11:34 pm

The ics variable has some ambiguous semantics and should be cleaned up a bit.

First, using the ICS name to simultaneously indicate two separate values (local/remote + identity) is not the best idea. Also, compared to "-", "localhost" is more specific (pingable, too).

Second, the ics directive is sent prior to the actual establishment of a link to an ICS. This means that the program can't be sure if sending a command in the tellics family will work; the program must wait until it is sure of a connection -- and this appears to be only after the program is requested to make a move.

Third, there is no need to repeatedly send ics when it can't change once it's set. All state variables should retain their settings until they are explicitly changed. If a whole bunch of them are reset to some default at the start of a new game or a setboard, then this should be stated in the documentation with details of which variables are affected.

User avatar
hgm
Posts: 22911
Joined: Fri Mar 10, 2006 9:06 am
Location: Amsterdam
Full name: H G Muller
Contact:

Re: Suggestions for XBoard documentation

Post by hgm » Mon Aug 31, 2015 6:46 am

sje wrote:First, using the ICS name to simultaneously indicate two separate values (local/remote + identity) is not the best idea. Also, compared to "-", "localhost" is more specific (pingable, too).
Improving documentation is not the same as altering the protocol, which is what you propose here. And what you suggest is wrong: "localhost" is not the same as "-", but would mean XBoard is running on an ICS that happens to be on the same machine. While "-" means no ICS is involved.
Second, the ics directive is sent prior to the actual establishment of a link to an ICS. This means that the program can't be sure if sending a command in the tellics family will work; the program must wait until it is sure of a connection -- and this appears to be only after the program is requested to make a move.
This is an implementation problem, rather than a protocol or documentation problem. One could argue that sending the 'ics' command should be delayed until after login is completed,and that it is an XBoard bug that it isn't.
Third, there is no need to repeatedly send ics when it can't change once it's set. All state variables should retain their settings until they are explicitly changed. If a whole bunch of them are reset to some default at the start of a new game or a setboard, then this should be stated in the documentation with details of which variables are affected.
Doing pointless things, (like sending the same command it repeatedly,e.g. several 'new' or 'force' in a row), is not forbidden by the current specs. So forbidding it would be protocol change that breaks backward compatibility for basically all existing interfaces.

User avatar
hgm
Posts: 22911
Joined: Fri Mar 10, 2006 9:06 am
Location: Amsterdam
Full name: H G Muller
Contact:

Re: Suggestions for XBoard documentation

Post by hgm » Mon Aug 31, 2015 7:26 am

D Sceviour wrote:The documentation states force can be followed by moves to play. It is confusing for the novice. It is not clear if it means:

Code: Select all

force e2e4 e7e5
or

Code: Select all

force		;bool is correct
e2e4
e7e5
Actually I don't see where the documentation says that:
XBoard protocol specs wrote:force
Set the engine to play neither color ("force mode"). Stop clocks. The engine should check that moves received in force mode are legal and made in the proper turn, but should not think, ponder, or make moves of its own.
I agree that the current protocol specs document leaves much to be desired in terms of clarity and scope. For one, it seems to be a weird mix of protocol specs and peculiarities of the XBoard implementation. And it dwells a lot on issues with commands that should now be considered obsolete.

That the 'force' command should not be followed by moves on the same line should be clear by the syntax description, (which serves as a header to the command description), which says

force

rather than

force MOVES

or

force MOVE1 [MOVE2 ...]

User avatar
hgm
Posts: 22911
Joined: Fri Mar 10, 2006 9:06 am
Location: Amsterdam
Full name: H G Muller
Contact:

Re: Suggestions for XBoard documentation

Post by hgm » Mon Aug 31, 2015 9:46 am

I guess the most compact way to give an unambiguous description is by pseudo-code. Like

Code: Select all

State variables&#58;
mode             &#123; PlayWhite, PlayBlack, PlayNeither, Analyze &#125;
paused           &#123; False, True &#125;
thinking         &#123; False, True &#125;
pondering        &#123; False, True &#125;
sideToMove       &#123; White, Black &#125;
ponder           &#123; On, Off &#125;
printThinking    &#123; On, Off &#125;
randomize        &#123; On, Off &#125;
tcMode           &#123; classical, incremental, fixed &#125;
timeLeft         integer
oppoTimeLeft     integer
tc               integer
timeIncrement    integer
movesPerSession  integer
conversionFactor integer
maxDepth         integer
timerType        &#123; WallClock, CPUtime, Nodes &#125;
compuOpponent    &#123; False, True &#125;
icsOpponent      &#123; False, True &#125;
oppoRating       integer
ownRating        integer
opponent         string
icsName          string
variant          string

Logic&#58;

loop &#123;

  if&#40;paused == False&#41; &#123;
    if&#40;mode == PlayBlack && sideToMove == Black ||
       mode == PlayWhite && sideToMove == White   ) &#123;
      thinking = True;
      move = Think&#40;sideToMove, maxDepth, tcMode, timeLeft&#41;; // terminates on timeout or reception of "?" command
      thinking = False;
      sideToMove = MakeMove&#40;move&#41;;
      printf&#40;"move %s\n", move&#41;;
    &#125; else if&#40;mode == Analyze&#41; &#123;
      Think&#40;sideToMove, INF, -, INF&#41;; // replies to "." command; aborts on other input
    &#125; else if&#40;&#40;mode == PlayBlack || mode == PlayWhite&#41; && ponder && plyNr != 1&#41; &#123;
      pondering =True;
      // ponder
      pondering = False;
    &#125;
  &#125;

  fflush&#40;stdout&#41;;               // flush any output that might be buffered!
  command = ReadLine&#40;OLD&#41;;      // fetch backlogged command, or read new one

  if&#40;command == "resume")       &#123; paused = False; continue; &#125;
  if&#40;paused&#41; continue;

  if&#40;command == "pause")        &#123; paused = True; continue; &#125;
  if&#40;command == "quit")         &#123; break; &#125;
  if&#40;command == "new")          &#123; 
    mode = PlayBlack;
    variant=normal; sideToMove = White; Setup&#40;FIDE_STARTPOS&#41;;
    maxDepth = INF; timeLeft = InitClock&#40;tcMode, mps, );
    timerType = WallClock; randomize = Off;
    RemoveFromExclusionList&#40;"all");
    continue;
  &#125;
  if&#40;command == "force")        &#123; mode = PlayNeither; continue; &#125;
  if&#40;command == "go")           &#123; if&#40;sideToMove == White&#41; mode = PlayWhite; else mode = PlayBlack; continue; &#125;
  if&#40;command == "playother")    &#123; if&#40;sideToMove == White&#41; mode = PlayBlack; else mode = PlayWhite; continue; &#125;
  if&#40;command == "setboard FEN") &#123; sideToMove = Setup&#40;FEN&#41;; RemoveFromExclusionList&#40;"all"); continue; &#125;
  if&#40;command == "white")        &#123; sideToMove = White; mode = PlayBlack; continue; &#125;
  if&#40;command == "black")        &#123; sideToMove = Black; mode = PlayWhite; continue; &#125;
  if&#40;command == "MOVE" ||
     command == "usermove MOVE")&#123;
    if&#40;IsLegal&#40;sideToMove, MOVE&#41;) sideToMove = MakeMove&#40;MOVE&#41;;
    else printf&#40;"Illegal move&#58; MOVE\n");
    RemoveFromExclusionList&#40;"all");
    continue;
  &#125;
  if&#40;command == "variant VARI") &#123; variant = VARI; Setup&#40;VARI_STARTPOS&#41;; continue; &#125;
  if&#40;command == "easy")         &#123; ponder = Off; continue; &#125;
  if&#40;command == "hard")         &#123; ponder = On;  continue; &#125;
  if&#40;command == "post")         &#123; printThinking = On;  continue; &#125;
  if&#40;command == "nopost")       &#123; printThinking = Off; continue; &#125;
  if&#40;command == "st TIME")      &#123; tcMode = Fixed; timeLeft = tc = 1000*TIME; continue; &#125;
  if&#40;command == "sd DEPTH")     &#123; maxDepth = DEPTH; continue; &#125;
  if&#40;command == "time TIME")    &#123; timeLeft = 10*TIME; continue; &#125;
  if&#40;command == "otim TIME")    &#123; oppoTimeLeft = 10*TIME; continue; &#125;
  if&#40;command == "nps RATE")     &#123; if&#40;RATE == 0&#41; timerType = CPUtime; else &#123; timerType = Nodes; conversionFactor = RATE; &#125; continue; &#125;
  if&#40;command == "xboard")       &#123; continue; &#125; //
  if&#40;command == "protover N")   &#123;
    if&#40;N < 2&#41; continue;
    printf&#40;"feature setboard=1 ping=1 memory=1 cores=1 ...\n");
    ...
    printf&#40;"feature done=1\n");
    continue;
  &#125;
  if&#40;command == "accepted XXX") &#123; continue; &#125;
  if&#40;command == "rejected XXX") &#123; ???; continue; &#125;
  if&#40;command == "random")       &#123; if&#40;randomize == On&#41; randomize = Off; else randomize = On; continue; &#125;
  if&#40;command == "level N T I")  &#123;
    timeLeft = oppoTimeLeft = tc = T*60000; // T in minutes; also parse notation min&#58;sec !
    if&#40;N == 0&#41; &#123; tcMode = Incremental; timeIncrement = 1000*I; &#125;
    else       &#123; tcMode = Classical; movesPerSession = N; &#125;
    continue;
  &#125;
  if&#40;command == "ping N")       &#123; printf&#40;"pong N\n"); continue; &#125;
  if&#40;command == "result RES &#123;COMMENT&#125;" ||
     command == "result RES")   &#123; mode = PlayNeither; ProcessResult&#40;RES, COMMENT&#41;; continue; &#125;
  if&#40;command == "undo")         &#123; sideToMove = TakeBack&#40;1&#41;; continue; &#125;
  if&#40;command == "remove")       &#123; TakeBack&#40;2&#41;; continue; &#125;
  if&#40;command == "analyze")      &#123; mode = Analyze; continue; &#125;
  if&#40;command == "exit")         &#123; if&#40;mode == Analyze&#41; mode = PlayNeither; continue; &#125;
  if&#40;command == "name NAME")    &#123; opponent = NAME; continue; &#125;
  if&#40;command == "rating N M")   &#123; ownRating = N; oppoRating = M;  continue; &#125;
  if&#40;command == "ics ICS")      &#123; if&#40;NAME != "-") icsOpponent = False; else &#123; icsOpponent = True; icsName = ICS; &#125; continue; &#125;
  if&#40;command == "computer")     &#123; compuOpponent = True; continue; &#125;
  if&#40;command == "memory N")     &#123; LimitMemoryUsage&#40;N&#41;; continue; &#125;
  if&#40;command == "cores N")      &#123; LimitThreadsUsage&#40;N&#41;; continue; &#125;
  if&#40;command == "egtpath TYPE PATH") &#123; SetEgtPath&#40;TYPE, PATH&#41;; continue; &#125;
  if&#40;command == "include MOVE") &#123; RemoveFromExclusionList&#40;MOVE&#41;; continue; &#125;
  if&#40;command == "exclude MOVE") &#123; AddToExclusionList&#40;MOVE&#41;; continue; &#125;
  if&#40;command == "setscore N")   &#123; ???; continue; &#125;
  if&#40;command == "option NAME=VAL") &#123; ???; continue; &#125;
  if&#40;command == "hint")         &#123; ???; continue; &#125;
  if&#40;command == "?")            &#123; ; continue; &#125;
  if&#40;command == ".")            &#123; ; continue; &#125;
  printf&#40;"Error&#58; &#40;unknown command&#41;&#58; COMMAND\n");
&#125;

Command processing during search&#58;

loop &#123;

  command = ReadLine&#40;NEW&#41;;              // read command from input

  if&#40;command == "pause") &#123;
    do &#123;
      command = ReadLine&#40;NEW&#41;;              // read command from input
      if&#40;command != "resume") printf&#40;"Error &#40;command while paused&#41;&#58; COMMAND\n");
    &#125; while&#40;command != "resume");
  &#125;

  if&#40;thinking == True&#41; &#123;
    if&#40;command == "?")                  &#123; ForceTimeout&#40;); break; &#125;
    AddToListOfBackloggedCommands&#40;command&#41;;
    break;
  &#125;
  if&#40;mode == Analyze && command == ".") &#123; printf&#40;"stat01 ....\n"); break; &#125;
  if&#40;pondering == True&#41; &#123;
    if&#40;command == "time TIME")          &#123; timeLeft = 10*TIME; continue; &#125;
    if&#40;command == "otim TIME")          &#123; oppoTimeLeft = 10*TIME; continue; &#125;
    if&#40;command == "MOVE" || command == "usermove MOVE") &#123;
      if&#40;PonderHit&#40;)) &#123;                 // judge ponder hit, a hit implies the move is legal
        pondering = False; thinking = True;
        StartClock&#40;timeLeft&#41;;           // set our time running
        break;
      &#125;
      ForceTimeout&#40;);                   // aborts ponder search
    &#125;
  &#125;
  AddToListOfBackloggedCommands&#40;command&#41;;
  break;
&#125;

User avatar
sje
Posts: 4675
Joined: Mon Mar 13, 2006 6:43 pm

Overloaded force mode

Post by sje » Mon Aug 31, 2015 11:35 am

Overloaded force mode

Force mode should not include color assignment nor rely upon an implied color assignment.

Every time a program gets its color re-assigned, implicitly or explicitly, it also has to adjust various secondary items. These include PGN tags for White, Black, WhiteElo, BlackElo, and maybe some others.

Further there may be the case where the program is playing both colors for some interpretations of analysis.

It seems like there is some kind of super-mode enumeration type which combines the various combinations of force, analysis, edit, and normal play. Maybe a few other things as well. I think that this can be cleaned up, but that and other improvements can't be made if there isn't a willingness to drop support for prior protocol version directives; at least when a program requests such.

There is a real opportunity here to greatly simplify the task of interfacing a program to XBoard. A separate section of the documentation -- a rather short section -- could be made with a description of the state variables and the directive semantics. The number of directives could be drastically cut just by introducing a single set directive:

Code: Select all

set <StateVariableName> <new-value>
Further ideas:

1) Combine move, draw offers, and draw claims in a single, atomic response.

2) Combine all opponent information into a single directive.

3) Avoid sending unneeded state variable updates.

4) When the interface wants a move from the program, indicate this with an explicit go directive (which also causes the program to play the move); use a separate search/hint directive when just an unplayed move is wanted. Thus, the need for force and analyze is dropped for a big gain in clarity -- just like setboard eliminates the need for edit and its sub-directives.

5) Allow a program to query an ICS to retrieve opponent information and perhaps other ICS data (seek ads, resume games, etc.).

6) Issue a new directive if and only if a new game is about to start.

7) Replace sd, sd, nps, and level with a unified limit directive. Use decimal seconds for all time values, not a mixture of hours, minutes, and integer seconds.

8) Replace otim and time with a single setclock directive, and use decimal seconds instead of centiseconds.

9) Formalize analysis response formats; in particular use decimal seconds, decimal pawns, and symbolic scores for forced mate/loses.

10) Have all of the above activated by a program sending something like:

Code: Select all

feature version="2015.08.31"
in response to the xboard directive. This would inform XBoard to send only directives in force at the given date -- and no directives which were superseded at that date.

Post Reply