int Pairing P((int nr, int nPlayers, int *w, int *b, int *sync));
FILE *WriteTourneyFile P((char *results, FILE *f));
void DisplayTwoMachinesTitle P(());
+static void ExcludeClick P((int index));
#ifdef WIN32
extern void ConsoleCreate();
static char resetOptions[] =
"-reuse -firstIsUCI false -firstHasOwnBookUCI true -firstTimeOdds 1 "
"-firstInitString \"" INIT_STRING "\" -firstComputerString \"" COMPUTER_STRING "\" "
- "-firstOptions \"\" -firstNPS -1 -fn \"\"";
+ "-firstFeatures \"\" -firstLogo \"\" -firstAccumulateTC 1 "
+ "-firstOptions \"\" -firstNPS -1 -fn \"\" -firstScoreAbs false";
void
FloatToFront(char **list, char *engineLine)
ASSIGN(*list, tidy+1);
}
+char *insert, *wbOptions; // point in ChessProgramNames were we should insert new engine
+
void
Load (ChessProgramState *cps, int i)
{
if(engineLine && engineLine[0]) { // an engine was selected from the combo box
snprintf(buf, MSG_SIZ, "-fcp %s", engineLine);
SwapEngines(i); // kludge to parse -f* / -first* like it is -s* / -second*
- ParseArgsFromString(resetOptions); appData.fenOverride[0] = NULL; appData.pvSAN[0] = FALSE;
+ ParseArgsFromString(resetOptions); appData.pvSAN[0] = FALSE;
+ FREE(appData.fenOverride[0]); appData.fenOverride[0] = NULL;
appData.firstProtocolVersion = PROTOVER;
ParseArgsFromString(buf);
SwapEngines(i);
p = engineName;
while(q = strchr(p, SLASH)) p = q+1;
if(*p== NULLCHAR) { DisplayError(_("You did not specify the engine executable"), 0); return; }
- if(engineDir[0] != NULLCHAR)
- appData.directory[i] = engineDir;
- else if(p != engineName) { // derive directory from engine path, when not given
+ if(engineDir[0] != NULLCHAR) {
+ ASSIGN(appData.directory[i], engineDir);
+ } else if(p != engineName) { // derive directory from engine path, when not given
p[-1] = 0;
- appData.directory[i] = strdup(engineName);
+ ASSIGN(appData.directory[i], engineName);
p[-1] = SLASH;
if(SLASH == '/' && p - engineName > 1) *(p -= 2) = '.'; // for XBoard use ./exeName as command after split!
- } else appData.directory[i] = ".";
+ } else { ASSIGN(appData.directory[i], "."); }
if(params[0]) {
if(strchr(p, ' ') && !strchr(p, '"')) snprintf(buf2, MSG_SIZ, "\"%s\"", p), p = buf2; // quote if it contains spaces
snprintf(command, MSG_SIZ, "%s %s", p, params);
p = command;
}
- appData.chessProgram[i] = strdup(p);
+ ASSIGN(appData.chessProgram[i], p);
appData.isUCI[i] = isUCI;
appData.protocolVersion[i] = v1 ? 1 : PROTOVER;
appData.hasOwnBookUCI[i] = hasBook;
isUCI ? (isUCI == TRUE ? " -fUCI" : gameInfo.variant == VariantShogi ? " -fUSI" : " -fUCCI") : "",
storeVariant ? " -variant " : "",
storeVariant ? VariantName(gameInfo.variant) : "");
+ if(wbOptions && wbOptions[0]) snprintf(buf+strlen(buf)-1, MSG_SIZ-strlen(buf), " %s\n", wbOptions);
firstChessProgramNames = malloc(len = strlen(q) + strlen(buf) + 1);
- snprintf(firstChessProgramNames, len, "%s%s", q, buf);
+ if(insert != q) insert[-1] = NULLCHAR;
+ snprintf(firstChessProgramNames, len, "%s\n%s%s", q, buf, insert);
if(q) free(q);
FloatToFront(&appData.recentEngineList, buf);
}
*start = *end = 0;
return FALSE;
} else if(strstr(buf+lineStart, "exclude:") == buf+lineStart) { // exclude moves clicked
- DisplayNote("Yes!");
- return;
+ ExcludeClick(origIndex - lineStart);
+ return FALSE;
}
ParsePV(buf+startPV, FALSE, gameMode != AnalyzeMode);
*start = startPV; *end = index-1;
}
char exclusionHeader[MSG_SIZ];
-int exCnt, excludePtr, mappedMove = -1;
+int exCnt, excludePtr;
typedef struct { int ff, fr, tf, tr, pc, mark; } Exclusion;
static Exclusion excluTab[200];
static char excludeMap[(BOARD_RANKS*BOARD_FILES*BOARD_RANKS*BOARD_FILES+7)/8]; // [HGM] exclude: bitmap for excluced moves
-void
-ClearMap ()
+static void
+WriteMap (int s)
{
int j;
- mappedMove = -1;
- for(j=0; j<(BOARD_RANKS*BOARD_FILES*BOARD_RANKS*BOARD_FILES+7)/8; j++) excludeMap[j] = 0;
- safeStrCpy(exclusionHeader, "exclude: none best tail \n", MSG_SIZ);
- excludePtr = 24; exCnt = 0;
+ for(j=0; j<(BOARD_RANKS*BOARD_FILES*BOARD_RANKS*BOARD_FILES+7)/8; j++) excludeMap[j] = s;
+ exclusionHeader[19] = s ? '-' : '+'; // update tail state
}
-void
-UpdateExcludeHeader (int fromY, int fromX, int toY, int toX, char promoChar, int incl)
+static void
+ClearMap ()
{
- char buf[2*MOVE_LEN], *p, c = incl ? '+' : '-';
+ safeStrCpy(exclusionHeader, "exclude: none best +tail \n", MSG_SIZ);
+ excludePtr = 24; exCnt = 0;
+ WriteMap(0);
+}
+
+static void
+UpdateExcludeHeader (int fromY, int fromX, int toY, int toX, char promoChar, char state)
+{ // search given move in table of header moves, to know where it is listed (and add if not there), and update state
+ char buf[2*MOVE_LEN], *p;
Exclusion *e = excluTab;
int i;
for(i=0; i<exCnt; i++)
if(i == exCnt) { // was not in exclude list; add it
CoordsToAlgebraic(boards[currentMove], PosFlags(currentMove), fromY, fromX, toY, toX, promoChar, buf);
if(strlen(exclusionHeader + excludePtr) < strlen(buf)) { // no space to write move
- if(c != exclusionHeader[19]) exclusionHeader[19] = '*'; // tail is now in mixed state
+ if(state != exclusionHeader[19]) exclusionHeader[19] = '*'; // tail is now in mixed state
return; // abort
}
e[i].ff = fromX; e[i].fr = fromY; e[i].tf = toX; e[i].tr = toY; e[i].pc = promoChar;
for(p=buf; *p; p++) exclusionHeader[excludePtr++] = *p; // copy move
exCnt++;
}
- exclusionHeader[e[i].mark] = c;
+ exclusionHeader[e[i].mark] = state;
+}
+
+static int
+ExcludeOneMove (int fromY, int fromX, int toY, int toX, signed char promoChar, char state)
+{ // include or exclude the given move, as specified by state ('+' or '-'), or toggle
+ char buf[MSG_SIZ];
+ int j, k;
+ ChessMove moveType;
+ if(promoChar == -1) { // kludge to indicate best move
+ if(!ParseOneMove(lastPV[0], currentMove, &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) // get current best move from last PV
+ return 1; // if unparsable, abort
+ }
+ // update exclusion map (resolving toggle by consulting existing state)
+ k=(BOARD_FILES*fromY+fromX)*BOARD_RANKS*BOARD_FILES + (BOARD_FILES*toY+toX);
+ j = k%8; k >>= 3;
+ if(state == '*') state = (excludeMap[k] & 1<<j ? '+' : '-'); // toggle
+ if(state == '-' && !promoChar) // only non-promotions get marked as excluded, to allow exclusion of under-promotions
+ excludeMap[k] |= 1<<j;
+ else excludeMap[k] &= ~(1<<j);
+ // update header
+ UpdateExcludeHeader(fromY, fromX, toY, toX, promoChar, state);
+ // inform engine
+ snprintf(buf, MSG_SIZ, "%sclude ", state == '+' ? "in" : "ex");
+ CoordsToComputerAlgebraic(fromY, fromX, toY, toX, promoChar, buf+8);
+ SendToProgram(buf, &first);
+ return (state == '+');
+}
+
+static void
+ExcludeClick (int index)
+{
+ int i, j;
+ Exclusion *e = excluTab;
+ if(index < 25) { // none, best or tail clicked
+ if(index < 13) { // none: include all
+ WriteMap(0); // clear map
+ for(i=0; i<exCnt; i++) exclusionHeader[excluTab[i].mark] = '+'; // and moves
+ SendToProgram("include all\n", &first); // and inform engine
+ } else if(index > 18) { // tail
+ if(exclusionHeader[19] == '-') { // tail was excluded
+ SendToProgram("include all\n", &first);
+ WriteMap(0); // clear map completely
+ // now re-exclude selected moves
+ for(i=0; i<exCnt; i++) if(exclusionHeader[e[i].mark] == '-')
+ ExcludeOneMove(e[i].fr, e[i].ff, e[i].tr, e[i].tf, e[i].pc, '-');
+ } else { // tail was included or in mixed state
+ SendToProgram("exclude all\n", &first);
+ WriteMap(0xFF); // fill map completely
+ // now re-include selected moves
+ j = 0; // count them
+ for(i=0; i<exCnt; i++) if(exclusionHeader[e[i].mark] == '+')
+ ExcludeOneMove(e[i].fr, e[i].ff, e[i].tr, e[i].tf, e[i].pc, '+'), j++;
+ if(!j) ExcludeOneMove(0, 0, 0, 0, -1, '+'); // if no moves were selected, keep best
+ }
+ } else { // best
+ ExcludeOneMove(0, 0, 0, 0, -1, '-'); // exclude it
+ }
+ } else {
+ for(i=0; i<exCnt; i++) if(i == exCnt-1 || excluTab[i+1].mark > index) {
+ char *p=exclusionHeader + excluTab[i].mark; // do trust header more than map (promotions!)
+ ExcludeOneMove(e[i].fr, e[i].ff, e[i].tr, e[i].tf, e[i].pc, *p == '+' ? '-' : '+');
+ break;
+ }
+ }
}
ChessSquare
return;
}
- if(doubleClick && (toX == -2 || toY == -2)) { // [HGM] exclude: off-board move means exclude all
- int i; // note that drop moves still have holdings coords as from-square at this point
- ChessMove moveType; char pc;
- for(i=0; i<(BOARD_RANKS*BOARD_FILES*BOARD_RANKS*BOARD_FILES+7)/8; i++) excludeMap[i] = -(toY == -2);
- mappedMove = currentMove;
- if(toY != -2) { SendToProgram("include all\n", &first); return; }
- SendToProgram("exclude all\n", &first);
- i = ParseOneMove(lastPV[0], currentMove, &moveType, &fromX, &fromY, &toX, &toY, &pc);
- ff=fromX, rf=fromY, ft=toX, rt=toY, promoChar = pc; // make copy that will survive drop encoding
- if(!i) return; // kludge: continue with move changed to engine's last-reported best, so it gets included again.
- }
-
if(toX < 0 || toY < 0) return;
pdown = boards[currentMove][fromY][fromX];
pup = boards[currentMove][toY][toX];
}
if(doubleClick) { // [HGM] exclude: move entered with double-click on from square is for exclusion, not playing
- int i=(BOARD_FILES*rf+ff)*BOARD_RANKS*BOARD_FILES + (BOARD_FILES*rt+ft), j;
- char buf[MSG_SIZ];
- if(mappedMove != currentMove) ClearMap();
- j = i%8; i >>= 3;
- snprintf(buf, MSG_SIZ, "%sclude ", excludeMap[i] & 1<<j ? "in" : "ex");
- if(excludeMap[i] & 1<<j) ClearPremoveHighlights();
- else ClearHighlights(), SetPremoveHighlights(ff, rf, ft, rt);
- if(!promoChar) excludeMap[i] ^= 1<<j;
- mappedMove = currentMove;
- CoordsToComputerAlgebraic(fromY, fromX, toY, toX, promoChar, buf+8);
- SendToProgram(buf, &first);
- UpdateExcludeHeader(fromY, fromX, toY, toX, promoChar, buf[0] == 'i');
+ if(ExcludeOneMove(fromY, fromX, toY, toX, promoChar, '*')) // toggle
+ ClearPremoveHighlights(); // was included
+ else ClearHighlights(), SetPremoveHighlights(ff, rf, ft, rt); // exclusion indicated by premove highlights
return;
}
board[fromY][fromX] = CharToPiece(piece < BlackPawn ? ToUpper(promoChar) : ToLower(promoChar)); // S-Chess gating
} else
if(promoChar == '+') {
- /* [HGM] Shogi-style promotions, to piece implied by original (Might overwrite orinary Pawn promotion) */
+ /* [HGM] Shogi-style promotions, to piece implied by original (Might overwrite ordinary Pawn promotion) */
board[toY][toX] = (ChessSquare) (PROMOTED piece);
} else if(!appData.testLegality && promoChar != NULLCHAR && promoChar != '=') { // without legality testing, unconditionally believe promoChar
- board[toY][toX] = CharToPiece(piece < BlackPawn ? ToUpper(promoChar) : ToLower(promoChar));
+ ChessSquare newPiece = CharToPiece(piece < BlackPawn ? ToUpper(promoChar) : ToLower(promoChar));
+ if((newPiece <= WhiteMan || newPiece >= BlackPawn && newPiece <= BlackMan) // unpromoted piece specified
+ && pieceToChar[PROMOTED newPiece] == '~') newPiece = PROMOTED newPiece; // but promoted version available
+ board[toY][toX] = newPiece;
}
if((gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat || gameInfo.variant == VariantGrand)
&& promoChar != NULLCHAR && gameInfo.holdingsSize) {
{
char buf[MSG_SIZ], *p, *q;
int i=1, header, skip, all = !strcmp(group, "all"), depth = 0;
+ insert = names; // afterwards, this global will point just after last retrieved engine line or group end in the 'names'
skip = !all && group[0]; // if group requested, we start in skip mode
for(;*names && depth >= 0 && i < MAXENGINES-1; names = p) {
p = names; q = buf; header = 0;
*q = 0;
if(*p == '\n') p++;
if(buf[0] == '#') {
- if(strstr(buf, "# end") == buf) { depth--; continue; } // leave group, and suppress printing label
+ if(strstr(buf, "# end") == buf) { if(!--depth) insert = p; continue; } // leave group, and suppress printing label
depth++; // we must be entering a new group
if(all) continue; // suppress printing group headers when complete list requested
header = 1;
if(depth != header && !all || skip) continue; // skip contents of group (but print first-level header)
if(engineList[i]) free(engineList[i]);
engineList[i] = strdup(buf);
- if(buf[0] != '#') TidyProgramName(engineList[i], "localhost", buf); // group headers not tidied
+ if(buf[0] != '#') insert = p, TidyProgramName(engineList[i], "localhost", buf); // group headers not tidied
if(engineMnemonic[i]) free(engineMnemonic[i]);
if((q = strstr(engineList[i]+2, "variant")) && q[-2]== ' ' && (q[-1]=='/' || q[-1]=='-') && (q[7]==' ' || q[7]=='=')) {
strcat(buf, " (");
SWAP(pgnName, p)
SWAP(pvSAN, h)
SWAP(engOptions, p)
+ SWAP(engInitString, p)
+ SWAP(computerString, p)
+ SWAP(features, p)
+ SWAP(fenOverride, p)
+ SWAP(NPS, h)
+ SWAP(accumulateTC, h)
+ SWAP(host, p)
}
int
if (appData.noChessProgram) return;
+ if(second.protocolVersion >= 2 && !strstr(second.variants, VariantName(gameInfo.variant))) {
+ DisplayError("second engine does not play this", 0);
+ return;
+ }
+
switch (gameMode) {
case TwoMachinesPlay:
return;
if(sscanf(p, " -check %d", &def) < 1) return FALSE;
opt->value = (def != 0);
opt->type = CheckBox;
- } else if(p = strstr(opt->name, " -combo ")) {
- opt->textValue = (char*) (&cps->comboList[cps->comboCnt]); // cheat with pointer type
+ } else if(p = strstr(opt->name, " -combo ")) {
+ opt->textValue = (char*) (opt->choice = &cps->comboList[cps->comboCnt]); // cheat with pointer type
cps->comboList[cps->comboCnt++] = q = p+8; // holds possible choices
if(*q == '*') cps->comboList[cps->comboCnt-1]++;
opt->value = n = 0;
if (BoolFeature(&p, "memory", &cps->memSize, cps)) continue;
if (BoolFeature(&p, "smp", &cps->maxCores, cps)) continue;
if (StringFeature(&p, "egt", cps->egtFormats, cps)) continue;
- if (StringFeature(&p, "option", cps->option[cps->nrOptions].name, cps)) {
+ if (StringFeature(&p, "option", buf, cps)) {
+ FREE(cps->option[cps->nrOptions].name);
+ cps->option[cps->nrOptions].name = malloc(MSG_SIZ);
+ safeStrCpy(cps->option[cps->nrOptions].name, buf, MSG_SIZ);
if(!ParseOption(&(cps->option[cps->nrOptions++]), cps)) { // [HGM] options: add option feature
snprintf(buf, MSG_SIZ, "rejected option %s\n", cps->option[--cps->nrOptions].name);
SendToProgram(buf, cps);