ASSIGN(*list, tidy+1);
}
-char *insert, *wbOptions; // point in ChessProgramNames were we should insert new engine
+char *insert, *wbOptions, *currentEngine[2]; // point in ChessProgramNames were we should insert new engine
void
Load (ChessProgramState *cps, int i)
{
char *p, *q, buf[MSG_SIZ], command[MSG_SIZ], buf2[MSG_SIZ], buf3[MSG_SIZ], jar;
if(engineLine && engineLine[0]) { // an engine was selected from the combo box
+ ASSIGN(currentEngine[i], engineLine);
snprintf(buf, MSG_SIZ, "-fcp %s", engineLine);
SwapEngines(i); // kludge to parse -f* / -first* like it is -s* / -second*
ParseArgsFromString(resetOptions); appData.pvSAN[0] = FALSE;
q = firstChessProgramNames;
if(nickName[0]) snprintf(buf, MSG_SIZ, "\"%s\" -fcp ", nickName); else buf[0] = NULLCHAR;
quote = strchr(p, '"') ? '\'' : '"'; // use single quotes around engine command if it contains double quotes
- snprintf(buf+strlen(buf), MSG_SIZ-strlen(buf), "%c%s%c -fd \"%s\"%s%s%s%s%s%s%s%s\n",
+ snprintf(buf+strlen(buf), MSG_SIZ-strlen(buf), "%c%s%c -fd \"%s\"%s%s%s%s%s%s%s%s",
quote, p, quote, appData.directory[i],
useNick ? " -fn \"" : "",
useNick ? nickName : "",
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);
+ if(wbOptions && wbOptions[0]) snprintf(buf+strlen(buf), MSG_SIZ-strlen(buf), " %s", wbOptions);
+ firstChessProgramNames = malloc(len = strlen(q) + strlen(buf) + 2);
if(insert != q) insert[-1] = NULLCHAR;
- snprintf(firstChessProgramNames, len, "%s\n%s%s", q, buf, insert);
+ snprintf(firstChessProgramNames, len, "%s\n%s\n%s", q, buf, insert);
if(q) free(q);
FloatToFront(&appData.recentEngineList, buf);
+ ASSIGN(currentEngine[i], buf);
}
ReplaceEngine(cps, i);
}
NextMatchGame();
}
-char *comboLine = NULL; // [HGM] recent: WinBoard's first-engine combobox line
-
void
InitBackEnd3 P((void))
{
char buf[MSG_SIZ];
int err, len;
+ ParseFeatures(appData.features[0], &first);
if(!appData.icsActive && !appData.noChessProgram && !appData.matchMode && // mode involves only first engine
!strcmp(appData.variant, "normal") && // no explicit variant request
appData.NrRanks == -1 && appData.NrFiles == -1 && appData.holdingsSize == -1 && // no size overrides requested
free(programVersion);
programVersion = (char*) malloc(8 + strlen(PACKAGE_STRING) + strlen(first.tidy));
sprintf(programVersion, "%s + %s", PACKAGE_STRING, first.tidy);
- FloatToFront(&appData.recentEngineList, comboLine ? comboLine : appData.firstChessProgram);
+ FloatToFront(&appData.recentEngineList, currentEngine[0] ? currentEngine[0] : appData.firstChessProgram);
}
if (appData.icsActive) {
} else if (count == 0) {
RemoveInputSource(isr);
- DisplayFatalError(_("Connection closed by ICS"), 0, 0);
+ DisplayFatalError(_("Connection closed by ICS"), 0, 6666);
} else {
DisplayFatalError(_("Error reading from ICS"), error, 1);
}
* deprecated "black" command.
*/
if (!WhiteOnMove(moveNum)) // [HGM] but better a deprecated command than an illegal move...
- SendToProgram(boards[0][1][BOARD_LEFT] == WhitePawn ? "a2a3\n" : "black\n", cps);
+ SendToProgram(boards[0][1][BOARD_LEFT] == WhitePawn && !pieceDesc[WhitePawn] ? "a2a3\n" : "black\nforce\n", cps);
if(!cps->extendedEdit) left = BOARD_LEFT, right = BOARD_RGHT; // only board proper
void
ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board)
{
- ChessSquare captured = board[toY][toX], piece, pawn, king, killed, killed2; int p, rookX, oldEP, epRank, berolina = 0;
+ ChessSquare captured = board[toY][toX], piece, pawn, king, killed, killed2; int p, rookX, oldEP, epRank, epFile, lastFile, lastRank, berolina = 0;
int promoRank = gameInfo.variant == VariantMakruk || gameInfo.variant == VariantGrand || gameInfo.variant == VariantChuChess ? 3 : 1;
/* [HGM] compute & store e.p. status and castling rights for new position */
/* we can always do that 'in place', now pointers to these rights are passed to ApplyMove */
if(gameInfo.variant == VariantBerolina) berolina = EP_BEROLIN_A;
- oldEP = (signed char)board[EP_FILE]; epRank = board[EP_RANK];
+ oldEP = (signed char)board[EP_STATUS]; epRank = board[EP_RANK]; epFile = board[EP_FILE]; lastFile = board[LAST_FILE],lastRank = board[LAST_RANK];
board[EP_STATUS] = EP_NONE;
- board[EP_FILE] = board[EP_RANK] = 100;
+ board[EP_FILE] = board[EP_RANK] = board[LAST_FILE] = board[LAST_RANK] = 100;
if (fromY == DROP_RANK) {
/* must be first */
}
pawn = board[fromY][fromX];
+ if(pieceDesc[pawn] && strchr(pieceDesc[pawn], 'e')) { // piece with user-defined e.p. capture
+ if(captured == EmptySquare && toX == epFile && (toY == (epRank & 127) || toY + (pawn < BlackPawn ? -1 : 1) == epRank - 128)) {
+ captured = board[lastRank][lastFile]; // remove victim
+ board[lastRank][lastFile] = EmptySquare;
+ pawn = EmptySquare; // kludge to suppress old e.p. code
+ }
+ }
if( pawn == WhiteLance || pawn == BlackLance ) {
if( gameInfo.variant != VariantSuper && gameInfo.variant != VariantChu ) {
if(gameInfo.variant == VariantSpartan) board[EP_STATUS] = EP_PAWN_MOVE; // in Spartan no e.p. rights must be set
if(toX<BOARD_RGHT-1 && board[toY][toX+1] == BlackPawn &&
gameInfo.variant != VariantBerolina || toX > fromX)
board[EP_STATUS] = toX;
+ board[LAST_FILE] = toX; board[LAST_RANK] = toY;
}
} else
if( pawn == BlackPawn ) {
if(toX<BOARD_RGHT-1 && board[toY][toX+1] == WhitePawn &&
gameInfo.variant != VariantBerolina || toX > fromX)
board[EP_STATUS] = toX;
+ board[LAST_FILE] = toX; board[LAST_RANK] = toY;
}
}
board[toY][toX] = (ChessSquare) (PROMOTED(board[toY][toX]));
board[fromY][fromX] = EmptySquare;
} else if ((fromY >= BOARD_HEIGHT>>1)
- && (oldEP == toX || oldEP == EP_UNKNOWN || appData.testLegality || abs(toX - fromX) > 4)
+ && (epFile == toX || oldEP == EP_UNKNOWN || appData.testLegality || abs(toX - fromX) > 4)
&& (toX != fromX)
&& gameInfo.variant != VariantXiangqi
&& gameInfo.variant != VariantBerolina
&& (pawn == WhitePawn)
&& (board[toY][toX] == EmptySquare)) {
+ if(lastFile == 100) lastFile = (board[fromY][toX] == BlackPawn ? toX : fromX), lastRank = fromY; // assume FIDE e.p. if victim present
board[fromY][fromX] = EmptySquare;
board[toY][toX] = piece;
- if(toY == epRank - 128 + 1)
- captured = board[toY - 2][toX], board[toY - 2][toX] = EmptySquare;
- else
- captured = board[toY - 1][toX], board[toY - 1][toX] = EmptySquare;
+ captured = board[lastRank][lastFile], board[lastRank][lastFile] = EmptySquare;
} else if ((fromY == BOARD_HEIGHT-4)
&& (toX == fromX)
&& gameInfo.variant == VariantBerolina
board[toY][toX] = (ChessSquare) (PROMOTED(board[toY][toX]));
board[fromY][fromX] = EmptySquare;
} else if ((fromY < BOARD_HEIGHT>>1)
- && (oldEP == toX || oldEP == EP_UNKNOWN || appData.testLegality || abs(toX - fromX) > 4)
+ && (epFile == toX && epRank == toY || oldEP == EP_UNKNOWN || appData.testLegality || abs(toX - fromX) > 4)
&& (toX != fromX)
&& gameInfo.variant != VariantXiangqi
&& gameInfo.variant != VariantBerolina
&& (pawn == BlackPawn)
&& (board[toY][toX] == EmptySquare)) {
+ if(lastFile == 100) lastFile = (board[fromY][toX] == WhitePawn ? toX : fromX), lastRank = fromY;
board[fromY][fromX] = EmptySquare;
board[toY][toX] = piece;
- if(toY == epRank - 128 - 1)
- captured = board[toY + 2][toX], board[toY + 2][toX] = EmptySquare;
- else
- captured = board[toY + 1][toX], board[toY + 1][toX] = EmptySquare;
+ captured = board[lastRank][lastFile], board[lastRank][lastFile] = EmptySquare;
} else if ((fromY == 3)
&& (toX == fromX)
&& gameInfo.variant == VariantBerolina
}
-void
-ResendOptions (ChessProgramState *cps)
+char *
+ResendOptions (ChessProgramState *cps, int toEngine)
{ // send the stored value of the options
int i;
- char buf[MSG_SIZ];
+ static char buf2[MSG_SIZ*10];
+ char buf[MSG_SIZ], *p = buf2;
Option *opt = cps->option;
+ *p = NULLCHAR;
for(i=0; i<cps->nrOptions; i++, opt++) {
+ *buf = NULLCHAR;
switch(opt->type) {
case Spin:
case Slider:
case CheckBox:
- snprintf(buf, MSG_SIZ, "option %s=%d\n", opt->name, opt->value);
+ if(opt->value != *(int*) (opt->name + MSG_SIZ - 104))
+ snprintf(buf, MSG_SIZ, "%s=%d", opt->name, opt->value);
break;
case ComboBox:
- snprintf(buf, MSG_SIZ, "option %s=%s\n", opt->name, opt->choice[opt->value]);
+ if(opt->value != *(int*) (opt->name + MSG_SIZ - 104))
+ snprintf(buf, MSG_SIZ, "%s=%s", opt->name, opt->choice[opt->value]);
break;
default:
- snprintf(buf, MSG_SIZ, "option %s=%s\n", opt->name, opt->textValue);
+ if(strcmp(opt->textValue, opt->name + MSG_SIZ - 100))
+ snprintf(buf, MSG_SIZ, "%s=%s", opt->name, opt->textValue);
break;
case Button:
case SaveButton:
continue;
}
- SendToProgram(buf, cps);
+ if(*buf) {
+ if(toEngine) {
+ snprintf(buf2, MSG_SIZ, "option %s\n", buf);
+ SendToProgram(buf2, cps);
+ } else {
+ if(p != buf2) *p++ = ',';
+ strncpy(p, buf, 10*MSG_SIZ-1 - (p - buf2));
+ while(*p) p++;
+ }
+ }
}
+ return buf2;
}
void
cps->comboCnt = 0; // and values of combo boxes
}
SendToProgram(buf, cps);
- if(cps->reload) ResendOptions(cps);
+ if(cps->reload) ResendOptions(cps, TRUE);
} else {
SendToProgram("xboard\n", cps);
}
return i;
}
+void
+SaveEngineSettings (int n)
+{
+ int len; char *p, *q, *s, buf[MSG_SIZ], *optionSettings;
+ if(!currentEngine[n] || !currentEngine[n][0]) { DisplayMessage("saving failed: engine not from list", ""); return; } // no engine from list is loaded
+ p = strstr(firstChessProgramNames, currentEngine[n]);
+ if(!p) { DisplayMessage("saving failed: engine not found in list", ""); return; } // sanity check; engine could be deleted from list after loading
+ optionSettings = ResendOptions(n ? &second : &first, FALSE);
+ len = strlen(currentEngine[n]);
+ q = p + len; *p = 0; // cut list into head and tail piece
+ s = strstr(currentEngine[n], "firstOptions");
+ if(s && (s[-1] == '-' || s[-1] == '/') && (s[12] == ' ' || s[12] == '=') && (s[13] == '"' || s[13] == '\'')) {
+ char *r = s + 14;
+ while(*r && *r != s[13]) r++;
+ s[14] = 0; // cut currentEngine into head and tail part, removing old settings
+ snprintf(buf, MSG_SIZ, "%s%s%s", currentEngine[n], optionSettings, *r ? r : "\""); // synthesize new engine line
+ } else if(*optionSettings) {
+ snprintf(buf, MSG_SIZ, "%s -firstOptions \"%s\"", currentEngine[n], optionSettings);
+ }
+ ASSIGN(currentEngine[n], buf); // updated engine line
+ len = p - firstChessProgramNames + strlen(q) + strlen(currentEngine[n]) + 1;
+ s = malloc(len);
+ snprintf(s, len, "%s%s%s", firstChessProgramNames, currentEngine[n], q);
+ FREE(firstChessProgramNames); firstChessProgramNames = s; // new list
+}
+
// following implemented as macro to avoid type limitations
#define SWAP(item, temp) temp = appData.item[0]; appData.item[0] = appData.item[n]; appData.item[n] = temp;
if(n == 1) SwapEngines(n);
ParseArgsFromString(buf);
if(n == 1) SwapEngines(n);
+ if(n < 2) { ASSIGN(currentEngine[n], command[i]); }
if(n == 0 && *appData.secondChessProgram == NULLCHAR) {
SwapEngines(1); // set second same as first if not yet set (to suppress WB startup dialog)
ParseArgsFromString(buf);
&& result != GameIsDrawn)
{ int i, j, k=0, oppoKings = 0, color = (result==WhiteWins ? (int)WhitePawn : (int)BlackPawn);
for(j=BOARD_LEFT; j<BOARD_RGHT; j++) for(i=0; i<BOARD_HEIGHT; i++) {
- int p = (signed char)boards[forwardMostMove][i][j] - color;
+ int p = (int)boards[forwardMostMove][i][j] - color;
if(p >= 0 && p <= (int)WhiteKing) k++;
oppoKings += (p + color == WhiteKing + BlackPawn - color);
}
safeStrCpy(bookMove, "move ", sizeof(bookMove)/sizeof(bookMove[0]));
strcat(bookMove, bookHit);
- HandleMachineMove(bookMove, &first);
+ savedMessage = bookMove; // args for deferred call
+ savedState = &first;
+ ScheduleDelayedEvent(DeferredBookMove, 1);
}
}
safeStrCpy(bookMove, "move ", sizeof(bookMove)/sizeof(bookMove[0]));
strcat(bookMove, bookHit);
- HandleMachineMove(bookMove, &first);
+ savedMessage = bookMove; // args for deferred call
+ savedState = &first;
+ ScheduleDelayedEvent(DeferredBookMove, 1);
}
}
void
TwoMachinesEvent P((void))
{
- int i;
+ int i, move = forwardMostMove;
char buf[MSG_SIZ];
ChessProgramState *onmove;
char *bookHit = NULL;
}
}
- ResetClocks();
- if (!first.sendTime || !second.sendTime) {
+ if (!first.sendTime || !second.sendTime || move == 0) { // [HGM] first engine changed sides from Reset, so recalc time odds
+ ResetClocks();
timeRemaining[0][forwardMostMove] = whiteTimeRemaining;
timeRemaining[1][forwardMostMove] = blackTimeRemaining;
}
if (strncmp((*p), name, len) == 0
&& (*p)[len] == '=' && (*p)[len+1] == '\"') {
(*p) += len + 2;
- ASSIGN(*loc, *p); // kludge alert: assign rest of line just to be sure allocation is large enough so that sscanf below always fits
- sscanf(*p, "%[^\"]", *loc);
+ len = strlen(*p) + 1; if(len < MSG_SIZ && !strcmp(name, "option")) len = MSG_SIZ; // make sure string options have enough space to change their value
+ FREE(*loc); *loc = malloc(len);
+ strncpy(*loc, *p, len);
+ sscanf(*p, "%[^\"]", *loc); // should always fit, because we allocated at least strlen(*p)
while (**p && **p != '\"') (*p)++;
if (**p == '\"') (*p)++;
snprintf(buf, MSG_SIZ, "accepted %s\n", name);
strcat(buf, "\n");
SendToProgram(buf, cps);
}
+ *(int*) (opt->name + MSG_SIZ - 104) = opt->value; // hide default values somewhere
+ if(opt->target == &opt->textValue) strncpy(opt->name + MSG_SIZ - 100, opt->textValue, 99);
return TRUE;
}
if (StringFeature(&p, "egt", &cps->egtFormats, cps)) continue;
if (StringFeature(&p, "option", &q, cps)) { // read to freshly allocated temp buffer first
if(cps->reload) { FREE(q); q = NULL; continue; } // we are reloading because of xreuse
+ if(cps->nrOptions == 0) { ASSIGN(cps->option[0].name, _("Make Persistent -save")); ParseOption(&(cps->option[cps->nrOptions++]), cps); }
FREE(cps->option[cps->nrOptions].name);
cps->option[cps->nrOptions].name = q; q = NULL;
if(!ParseOption(&(cps->option[cps->nrOptions++]), cps)) { // [HGM] options: add option feature