Note that engines tend to play rather deterministic, so usually engine tourneys force game diversity by starting the games from a forced opening line from a book. Even if it is just a book of random moves for lack of any real theory. But in the case of Musketeer Chess that would mean the prelude will always be played from the book, and never by the engines themselves. If the book in this respect was just randomly picking pieces and gating squares, the whole point of taking turns on this evaporates, and the complex negociation procedure might as well have been replaced by just loading preselected initial positions from after the prelude. (Something that all GUIs and engines already support...)
That being said, it seemed an interesting challenge to support such preludes in WinBoard through the existing protocol commands. This required only minimal changes in the way WinBoard handles these commands. So rather than accepting only a single board position (from which the game would then start) from the engine once a variant gets selected, the engine can now send as many board as it wants, by specifying parent variant 'prelude' in the 'setup' commands. When finally a 'setup' command arrives that specifies another parent variant than 'prelude', the board in it will be used as start position. The preceding boards can then all be 'graphical menus' for selecting something.
I modified my engine KingSlayer to announce that it plays variant 'musketeer', and implemented the Musketeer Chess prelude in it for selecting pieces and gating squares. (After that it stops, as it was lying about being abe to play Musketeer Chess; it just goes on as a game of normal Chess without ever gating any of the selected Musketeer pieces onto the board.) The first board it shows after 'New Game' is
from which white can then select the first Musketeer piece by clicking on it. After two more boards for selecting the gating squares (which at the same time show what the opponent has chosen in terms of pieces and gating squares), the negociated initial position of the game appears:
(BTW, screenshots are from XBoard, not WinBoard.)
The code in the engine that does this is:
Code: Select all
int engineSide=NONE; // side played by engine
char inBuf[80];
int prelude[6], preptr, lastPrinted = -1, variant;
char *whitePieces = "ACDEF00/0HLMSU";
char *blackPieces = "acdef00/0hlmsu";
char *ptc = "PNBRQ.EA...C..F.MHDSULKpnbrq.ea...c..f.mhdsulk";
int
Prelude ()
{
char buf1[80], buf2[80], buf3[80], w, b; int i;
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) 8x8+0_prelude 8/8/8/0%s00/8/8/8 w - - 0 1\n", ptc, buf1);
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) 8x8+0_prelude 8/8/8/0%s00/8/8/8 w - - 0 1\n", ptc, buf1);
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) 8x8+0_prelude %s/8/8/8/8/8/8/%s w - - 0 1\n", ptc, buf2, buf1);
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) 8x8+0_prelude %s/8/8/8/8/8/8/%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) 8x8+0_prelude %s/8/8/8/8/8/8/%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 + 32; 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) 8x8+0_prelude %s/8/8/8/8/8/8/%s w - - 0 1\n", ptc, buf2, buf1);
break;
case 6:
strcpy(buf3, ptc);
for(i=5; i<22; i++) if(ptc[i] != w && ptc[i] != b - 32) buf3[i] = buf3[i +23] = '.';
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_fairy %s/rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR/%s w KQkq - 0 1\n", buf3, buf2, buf1);
}
lastPrinted = preptr;
return (preptr < 6);
}
(Not yet quite what a real Musketeer engine would need, as it finally ends up in parent variant 'fairy', which doesn't allow for any gating.) This routine Prelude() is called at the point where the engine would normally decide whether it would have to start thinking (based on engineSide and the side to move), but if it returns 1 to indicate we are still in the prelude, this thinking will be suppressed. When it is the engine's turn to pick something, it picks randomly here; of course a special search could be preformed here in order to attempt making the best choice. (I cannot really imagine that any advantage can be derived from chosing the piece types, but perhaps choosing a gating square does matter.)
This whole negociation procedure is basically invisible to WinBoard: the various choices are not counted as moves, and the game acts like it was started from the resulting position. E.g. when it is later saved as PGN it gives:
Code: Select all
[Event "Computer Chess Game"]
[Site "hgm-xboard"]
[Date "2019.12.15"]
[Round "-"]
[White "hgm"]
[Black "Simple 0.00"]
[Result "*"]
[TimeControl "40/600"]
[Variant "musketeer"]
[FEN "ml******/rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR/**L*M*** w KQkq - 0 1"]
[SetUp "1"]
{--------------
m l . . . . . .
r n b q k b n r
p p p p p p p p
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
P P P P P P P P
R N B Q K B N R
. . L . M . . .
white to play
--------------}
1. d4 d5 {-0.04/13 16}
*