A short update on WinBoard / CECP support for Musketeer Chess:
Bug Fixes
The
WinBoard-AA package now contains a WinBoard version (4.9.2020.402) that fixes some problems with loading games or FENs in engine-defined variants. It should now be possible to save and re-load PGN files with Musketeer Chess games without any problems. Castling in saved PGN should now be written as the normal SAN O-O or O-O-O, rather than as a King move like Kg1. It should also be possible to run matches and tournaments from FENs supplied in a position file, even if the different start positions use different Musketeer pieces.
Recording the prelude in PGN
A new feature is that I introduced a Prelude tag in PGN. This tag can be added on engine's request: when an engine at the start of a game (i.e. before the first move on the board) sends
tellothers prelude <any text>
the PGN for that game will include the tag
[Prelude "<any text>"]
WinBoard already had an option
-autoComment true|false that would make it store texts sent by the engine through tellothers commands as comments on the current move, but the presence of the word 'prelude' in such a command at the start of the game will overrule this, and promote the text to a PGN tag even when the -autoComment option is switched off.
The purpose of this feature is that engines who have conducted a dialog with the user to negociate a start position, by selecting piece types and gating squares, can have the history of this prelude be recorded in the PGN. For Musketeer Chess only the order of the piece-type choices has to be recorded; together with the FEN of the position after the prelude (which is always included in engine-defined variants) the order in which the various gating squares were chosen follows from this.
KingSlayer-Aramis (also included in the WinBoard-AA package) now uses this to add a Prelude tag with value
white: <ID1>, black: <ID2>
whith the single-letter IDs of the Musketeer pieces that are also used to describe them in the FEN tag. WinBoard doesn't ever interpret such a tag, so the format of the text is not critical. But in the future other GUIs might want to interpret it, so I think it would be a good thing to agree on some standard for the description of a Musketeer prelude, which all engines that are able to conduct a prelude dialog then should use. When the engine did not conduct a prelude dialog (e.g. because it was started from a FEN of a position after the prelude), the Prelude tag should be omitted (i.e. the engine would not have to send a 'tellothers' command).
Conducting a prelude from the engine
The code KingSlayer-Aramis currently uses for conducting the prelude (and which I hereby place in the public domain) is:
Code: Select all
void MusketeerInit (int color, int type1, int type2, int file1, int file2);
char *whitePieces = "ACDEF00/0HLMSU";
char *blackPieces = "acdef00/0hlmsu";
char *ptc = "PNBRQ.E....C.AF.MH.SU........D............LKpnbrq.e....c.af.mh.su........d............lk";
int prelude[6], preptr, lastPrinted = -1, computer;
int
Prelude ()
{
char buf1[80], buf2[80], buf3[12050], w, b; int i;
static didPrelude;
if(preptr < 6 && engineSide == (preptr & 1 ? BLACK : WHITE)) { // engine's turn to pick something
do {
prelude[preptr] = (preptr < 2 ? 10 : 8) * (rand() & 0xFFF) >> 12; // pick it
} while(preptr == 1 ? prelude[0] == prelude[1] // retry if same piece
: prelude[preptr] == 4 || // or gating under King
preptr == 5 && prelude[5] == prelude[3]); // or on same square
preptr++;
} else if(preptr == lastPrinted) return (preptr < 6);
// now it is always the user's turn to pick something (or move)
w = whitePieces[prelude[0] + 4*(prelude[0] > 4)];
b = blackPieces[prelude[1] + 4*(prelude[1] > 4)];
switch(preptr) {
case 0: // present all white pieces
strcpy(buf1, whitePieces);
printf("setup (%s) 8x10+0_!seirawan 8/8/8/8/0%s00/8/8/8/8 w - - 0 1\n", ptc, buf1);
printf("piece K& KisO2\n");
didPrelude = 0;
break;
case 1: // present all black pieces except the white choice
strcpy(buf1, blackPieces);
buf1[prelude[0] + 4*(prelude[0] > 4)] = w;
printf("setup (%s) 8x10+0_!seirawan 8/8/8/8/0%s00/8/8/8/8 w - - 0 1\n", ptc, buf1);
didPrelude = 1;
break;
case 2: // white choice fills 1st rank, black choice fills 8th
for(i=0; i<8; i++) buf1[i] = w, buf2[i] = b; buf1[8] = buf2[8] = 0;
printf("setup (%s) 8x10+0_!seirawan %s/rnbqkbnr/pppppppp/8/8/8/8/pppppppp/rnbqkbnr/%s w - - 0 1\n", ptc, buf2, buf1);
didPrelude = 1;
break;
case 3: // white choice on its gating square, black choice fills 8th rank
for(i=0; i<8; i++) buf1[i] = '0', buf2[i] = w + 32; buf1[8] = buf2[8] = 0;
buf1[prelude[2]] = w;
printf("setup (%s) 8x10+0_!seirawan %s/RNBQKBNR/PPPPPPPP/8/8/8/8/PPPPPPPP/RNBQKBNR/%s w - - 0 1\n", ptc, buf2, buf1);
break;
case 4: // whitened black choice on remaining 5-7 1st-rank squares, blackened white choice on its gating square
for(i=0; i<8; i++) buf1[i] = b - 32, buf2[i] = '0'; buf1[8] = buf2[8] = 0;
buf2[prelude[3]] = w + 32; buf1[prelude[2]] = w + 32;
if(prelude[2] == 4) buf1[0] = buf1[7] = '0'; if(prelude[2] == 0 || prelude[2] == 7) buf1[4] = '0'; // no K & R
printf("setup (%s) 8x10+0_!seirawan %s/rnbqkbnr/pppppppp/8/8/8/8/pppppppp/rnbqkbnr/%s w - - 0 1\n", ptc, buf2, buf1);
break;
case 5: // both whitened pieces on their gating squares, blackened white choice on 7, 6 or 5 remaining 8th-rank squares
for(i=0; i<8; i++) buf1[i] = '0', buf2[i] = b; buf1[8] = buf2[8] = 0;
buf2[prelude[3]] = w; buf1[prelude[2]] = w; buf1[prelude[4]] = b - 32;
if(prelude[3] == 4) buf2[0] = buf2[7] = '0'; if(prelude[3] == 0 || prelude[3] == 7) buf2[4] = '0'; // no K & R
printf("setup (%s) 8x10+0_!seirawan %s/RNBQKBNR/PPPPPPPP/8/8/8/8/PPPPPPPP/RNBQKBNR/%s w - - 0 1\n", ptc, buf2, buf1);
break;
case 6:
strcpy(buf3, ptc);
for(i=5; i<43; i++) if(ptc[i] != w && ptc[i] != b - 32) buf3[i] = buf3[i+44] = '.';
for(i=0; i<8; i++) buf1[i] = '*', buf2[i] = '*'; buf1[8] = buf2[8] = 0;
buf2[prelude[3]] = w + 32; buf2[prelude[5]] = b; buf1[prelude[2]] = w; buf1[prelude[4]] = b - 32;
printf("setup (%s) 8x10+0_seirawan %s/rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR/%s w KQBCDFGkqbcdfg - 0 1\n", buf3, buf2, buf1);
printf("piece K& KisO2\n"); // to make castling work despite extra ranks
stepPtr = 31; nBound = 0; discoMask[WHITE] = discoMask[BLACK] = 0;
MusketeerInit(WHITE, prelude[0], prelude[1], prelude[2], prelude[4]); // This sends CECP 'piece' command for 1st piece
MusketeerInit(BLACK, prelude[1], prelude[0], prelude[5], prelude[3]); // KLUDGE: swap piece order
if(didPrelude) printf("tellothers prelude white: %c, black: %c\n", typeID[4+prelude[0]], typeID[4+prelude[1]]); // for in the PGN
}
lastPrinted = preptr;
return (preptr < 6);
}
(
Warning to GUI developers: this code can generates FENs that can contain '0' (zero), which WinBoard interprets as 'a single empty square', but unlike '1', which also means that, will not coalesce with any following digits. So "00" means two empty squares, while "11" would have meant eleven empty squares. This convention was adopted to make it easier for engines to clear squares in a FEN.) The Prelude() code is used by prefixing the test that would normally decide whether the engine should start thinking by
(where 'musketeer' indicates we are playing Musketeer Chess). This suppresses any thinking about board moves until the prelude has been completed. Note the presented code makes the engine's own choices at random; other engines could of course have special routines to determine the best choices, or take those from an internal 'book'.
The dialog is conducted by presenting the user with boards (through non-final 'setup' commands) on which he can indicate his choices by clicking on a piece of his color. To become aware of these clicks the engine should have sent
feature highlight=1 at startup, and interpret the 'lift' commands that WinBoard will then use to relay the clicks, through the code
Code: Select all
if(!strcmp(command, "lift")) { // interpret "lift <SQUARE>"
int f = inBuf[5] - 'a', r = inBuf[6] - '1'; // file and rank of clciked square
if(r == 3) f += 4; else if(r == 4) f--; // piece-type selection, translate square to type
if(variant && preptr < 6) prelude[preptr++] = f; // record prelude history
return 1;
}
Set-up positions
On receiving a 'setboard' command for setting up a position the prelude (which always will have commenced automatically at the start of a new game by presenting the first 'menu board' for the white player, which also informs the GUI about how to represent the complete set of Musketeer pieces) must be aborted. But the last step of it, which includes the initialization of the engine for the participating pieces, and the sending of the final 'setup' command to the GUI, will always have to be done. This is achieved by setting preptr to 6,
after the FEN reader deduced from the FEN what the prelude history could have been (filling the array prelude[] with the codes for the piece types and file number of the gating squares as it encounters them on rank 0 and 9):
Code: Select all
if(!strcmp(command, "setboard")){ engineSide = NONE; stm = Setup(inBuf+9); preptr = 6; return 1; }
On receiving a 'new' command preptr should be reset to 0, and lastPrinted to -1, so that the new game will again start with a prelude.