ChessProgramState *savCps;
-GameMode oldMode;
+GameMode oldMode, tryNr;
+
+extern char *engineName, *engineDir, *engineChoice, *engineLine, *nickName, *params;
+extern Boolean isUCI, hasBook, storeVariant, v1, addToList, useNick;
+char *insert, *wbOptions, *currentEngine[2]; // point in ChessProgramNames were we should insert new engine
+static char newEngineCommand[MSG_SIZ];
+
+void
+FloatToFront(char **list, char *engineLine)
+{
+ char buf[MSG_SIZ], tidy[MSG_SIZ], *p = buf, *q, *r = buf;
+ int i=0;
+ if(appData.recentEngines <= 0) return;
+ TidyProgramName(engineLine, "localhost", tidy+1);
+ tidy[0] = buf[0] = '\n'; strcat(tidy, "\n");
+ strncpy(buf+1, *list, MSG_SIZ-50);
+ if(p = strstr(buf, tidy)) { // tidy name appears in list
+ q = strchr(++p, '\n'); if(q == NULL) return; // malformed, don't touch
+ while(*p++ = *++q); // squeeze out
+ }
+ strcat(tidy, buf+1); // put list behind tidy name
+ p = tidy + 1; while(q = strchr(p, '\n')) i++, r = p, p = q + 1; // count entries in new list
+ if(i > appData.recentEngines) *r = NULLCHAR; // if maximum rached, strip off last
+ ASSIGN(*list, tidy+1);
+}
+
+void
+SaveEngineList ()
+{
+ FILE *f;
+ if(*engineListFile && (f = fopen(engineListFile, "w"))) {
+ fprintf(f, "-firstChessProgramNames {%s}\n", firstChessProgramNames);
+ fclose(f);
+ }
+}
+
+void
+AddToEngineList (int i)
+{
+ int len;
+ char quote, buf[MSG_SIZ];
+ char *q = firstChessProgramNames, *p = newEngineCommand;
+ 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",
+ quote, p, quote, appData.directory[i],
+ useNick ? " -fn \"" : "",
+ useNick ? nickName : "",
+ useNick ? "\"" : "",
+ v1 ? " -firstProtocolVersion 1" : "",
+ hasBook ? "" : " -fNoOwnBookUCI",
+ isUCI ? (isUCI == TRUE ? " -fUCI" : gameInfo.variant == VariantShogi ? " -fUSI" : " -fUCCI") : "",
+ storeVariant ? " -variant " : "",
+ storeVariant ? VariantName(gameInfo.variant) : "");
+ 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\n%s", q, buf, insert);
+ if(q) free(q);
+ SaveEngineList();
+ FloatToFront(&appData.recentEngineList, buf);
+ ASSIGN(currentEngine[i], buf);
+}
void
LoadEngine ()
{
int i;
if(WaitForEngine(savCps, LoadEngine)) return;
+ if(tryNr == 1 && !isUCI) { SendToProgram("uci\n", savCps); tryNr = 2; ScheduleDelayedEvent(LoadEngine, FEATURE_TIMEOUT); return; }
+ if(tryNr) v1 = (tryNr == 2), tryNr = 0, AddToEngineList(0); // deferred to after protocol determination
CommonEngineInit(); // recalculate time odds
if(gameInfo.variant != StringToVariant(appData.variant)) {
// we changed variant when loading the engine; this forces us to reset
LoadEngine();
}
-extern char *engineName, *engineDir, *engineChoice, *engineLine, *nickName, *params;
-extern Boolean isUCI, hasBook, storeVariant, v1, addToList, useNick;
-
static char resetOptions[] =
"-reuse -firstIsUCI false -firstHasOwnBookUCI true -firstTimeOdds 1 "
"-firstInitString \"" INIT_STRING "\" -firstComputerString \"" COMPUTER_STRING "\" "
"-firstOptions \"\" -firstNPS -1 -fn \"\" -firstScoreAbs false";
void
-FloatToFront(char **list, char *engineLine)
-{
- char buf[MSG_SIZ], tidy[MSG_SIZ], *p = buf, *q, *r = buf;
- int i=0;
- if(appData.recentEngines <= 0) return;
- TidyProgramName(engineLine, "localhost", tidy+1);
- tidy[0] = buf[0] = '\n'; strcat(tidy, "\n");
- strncpy(buf+1, *list, MSG_SIZ-50);
- if(p = strstr(buf, tidy)) { // tidy name appears in list
- q = strchr(++p, '\n'); if(q == NULL) return; // malformed, don't touch
- while(*p++ = *++q); // squeeze out
- }
- strcat(tidy, buf+1); // put list behind tidy name
- p = tidy + 1; while(q = strchr(p, '\n')) i++, r = p, p = q + 1; // count entries in new list
- if(i > appData.recentEngines) *r = NULLCHAR; // if maximum rached, strip off last
- ASSIGN(*list, tidy+1);
-}
-
-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(jar) { snprintf(buf3, MSG_SIZ, "java -jar %s", p); p = buf3; }
ASSIGN(appData.chessProgram[i], p);
+ if(isUCI == 3) tryNr = 1, isUCI = 0; // auto-detect
appData.isUCI[i] = isUCI;
appData.protocolVersion[i] = v1 ? 1 : PROTOVER;
appData.hasOwnBookUCI[i] = hasBook;
if(!nickName[0]) useNick = FALSE;
if(useNick) ASSIGN(appData.pgnName[i], nickName);
- if(addToList) {
- int len;
- char quote;
- 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",
- quote, p, quote, appData.directory[i],
- useNick ? " -fn \"" : "",
- useNick ? nickName : "",
- useNick ? "\"" : "",
- v1 ? " -firstProtocolVersion 1" : "",
- hasBook ? "" : " -fNoOwnBookUCI",
- isUCI ? (isUCI == TRUE ? " -fUCI" : gameInfo.variant == VariantShogi ? " -fUSI" : " -fUCCI") : "",
- storeVariant ? " -variant " : "",
- storeVariant ? VariantName(gameInfo.variant) : "");
- 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\n%s", q, buf, insert);
- if(q) free(q);
- FloatToFront(&appData.recentEngineList, buf);
- ASSIGN(currentEngine[i], buf);
- }
+ safeStrCpy(newEngineCommand, p, MSG_SIZ);
ReplaceEngine(cps, i);
}
boards[moveNum][EP_STATUS] = EP_NONE;
if(str[0] == 'P') boards[moveNum][EP_STATUS] = EP_PAWN_MOVE;
if(strchr(move_str, 'x')) boards[moveNum][EP_STATUS] = EP_CAPTURE;
- if(double_push != -1) boards[moveNum][EP_STATUS] = double_push + BOARD_LEFT;
+ if(double_push != -1) {
+ int dir = WhiteOnMove(moveNum) ? 1 : -1, last = BOARD_HEIGHT-1;
+ boards[moveNum][EP_FILE] = // also set new e.p. variables
+ boards[moveNum][EP_STATUS] = double_push + BOARD_LEFT;
+ boards[moveNum][EP_RANK] = (last + 3*dir)/2;
+ boards[moveNum][LAST_TO] = 128*(last + dir) + boards[moveNum][EP_FILE];
+ } else boards[moveNum][EP_FILE] = boards[moveNum][EP_RANK] = 100;
if (ics_getting_history == H_GOT_REQ_HEADER ||
shuffleOpenings = 1;
break;
case VariantNoCastle:
- pieces = FIDEArray;
- nrCastlingRights = 0;
/* !!?unconstrained back-rank shuffle */
shuffleOpenings = 1;
+ case VariantSuicide:
+ pieces = FIDEArray;
+ nrCastlingRights = 0;
break;
}
Boolean right; // instructs front-end to use button-1 events as if they were button 3
Boolean deferChoice;
+int createX = -1, createY = -1; // square where we last created a piece in EditPosition mode
void
LeftClick (ClickType clickType, int xPix, int yPix)
} else if(y == BOARD_HEIGHT-1) { handOffsets &= ~2; DrawPosition(TRUE, boards[currentMove]); return; }
}
- if(appData.monoMouse && gameMode == EditPosition && fromX < 0 && clickType == Press && boards[currentMove][y][x] == EmptySquare) {
+ if(appData.monoMouse && gameMode == EditPosition && fromX < 0 && clickType == Press &&
+ (boards[currentMove][y][x] == EmptySquare || x == createX && y == createY) ) {
static int dummy;
RightClick(clickType, xPix, yPix, &dummy, &dummy);
right = TRUE;
return;
}
+ createX = createY = -1;
+
if(SeekGraphClick(clickType, xPix, yPix, 0)) return;
prevClickTime = lastClickTime; GetTimeMark(&lastClickTime);
if (xSqr == BOARD_LEFT-1 || xSqr == BOARD_RGHT) return -1;
if (xSqr < 0 || ySqr < 0) return -1;
if(appData.pieceMenu) { whichMenu = 0; break; } // edit-position menu
+ if(flipView) xSqr = BOARD_WIDTH - 1 - xSqr; else ySqr = BOARD_HEIGHT - 1 - ySqr;
+ if(xSqr == createX && ySqr == createY && xSqr != BOARD_LEFT-2 && xSqr != BOARD_RGHT+1) {
+ ChessSquare p = boards[currentMove][ySqr][xSqr];
+ do { if(++p == EmptySquare) p = WhitePawn; } while(PieceToChar(p) == '.');
+ boards[currentMove][ySqr][xSqr] = p; DrawPosition(FALSE, boards[currentMove]);
+ return -2;
+ }
pieceSweep = shiftKey ? BlackPawn : WhitePawn; // [HGM] sweep: prepare selecting piece by mouse sweep
- toX = xSqr; toY = ySqr; lastX = x, lastY = y;
- if(flipView) toX = BOARD_WIDTH - 1 - toX; else toY = BOARD_HEIGHT - 1 - toY;
+ createX = toX = xSqr; createY = toY = ySqr; lastX = x, lastY = y;
NextPiece(0);
return 2; // grab
case IcsObserving:
return 1;
}
} else moveCount = 6;
+
+ if(gameInfo.variant == VariantMakruk && // Makruk counting rules
+ (nrW == 1 || nrB == 1 || nr[WhitePawn] + nr[BlackPawn] == 0)) { // which only kick in when pawnless or bare King
+ int maxcnt, his, mine, c, wom = WhiteOnMove(forwardMostMove);
+ count = forwardMostMove;
+ while(count >= backwardMostMove) {
+ int np = nr[WhitePawn] + nr[BlackPawn];
+ if(wom) mine = nrW, his = nrB, c = BlackPawn;
+ else mine = nrB, his = nrW, c = WhitePawn;
+ if(mine > 1 && np) { count++; break; }
+ if(mine > 1) maxcnt = 64; else
+ maxcnt = (nr[WhiteRook+c] > 1 ? 8 : nr[WhiteRook+c] ? 16 : nr[WhiteMan+c] > 1 ? 22 :
+ nr[WhiteKnight+c] > 1 ? 32 : nr[WhiteMan+c] ? 44 : 64) - his - 1;
+ while(boards[count][EP_STATUS] != EP_CAPTURE && count > backwardMostMove) count--; // seek previous character
+ if(count == backwardMostMove) break;
+ if(forwardMostMove - count >= 2*maxcnt + 1 - (mine == 1)) break;
+ Count(boards[--count], nr, &nrW, &nrB, &staleW, &staleB, &bishopColor);
+ }
+ if(forwardMostMove - count >= 2*maxcnt + 1 - (mine == 1)) {
+ boards[forwardMostMove][EP_STATUS] = EP_RULE_DRAW;
+ if(canAdjudicate && appData.ruleMoves >= 0) {
+ GameEnds( GameIsDrawn, "Xboard adjudication: counting rule", GE_XBOARD );
+ return 1;
+ }
+ }
+ }
}
// Repetition draws and 50-move rule can be applied independently of legality testing
i++;
}
}
- if( count >= 100)
+ if( count >= 100 && gameInfo.variant != VariantMakruk) // do not accept 50-move claims in Makruk
boards[forwardMostMove][EP_STATUS] = EP_RULE_DRAW;
/* this is used to judge if draw claims are legal */
if(canAdjudicate && appData.ruleMoves > 0 && count >= 2*appData.ruleMoves) {
cps->useSigterm = FALSE;
}
if (strncmp(message, "feature ", 8) == 0) { // [HGM] moved forward to pre-empt non-compliant commands
- ParseFeatures(message+8, cps);
+ ParseFeatures(message+8, cps); if(tryNr < 3) tryNr = 3;
return; // [HGM] This return was missing, causing option features to be recognized as non-compliant commands!
}
first.highlight = f;
return;
}
+ if(strncmp(message, "uciok", 5) == 0) { // response to "uci" probe
+ appData.isUCI[0] = isUCI = 1;
+ ReplaceEngine(&first, 0); // retry install as UCI
+ return;
+ }
/*
* If the move is illegal, cancel it and redraw the board.
* Also deal with other error cases. Matching is rather loose
{
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
+ if(*engineListFile) ParseSettingsFile(engineListFile, &engineListFile); // update engine list
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);
s = malloc(len);
snprintf(s, len, "%s%s%s", firstChessProgramNames, currentEngine[n], q);
FREE(firstChessProgramNames); firstChessProgramNames = s; // new list
+ if(*engineListFile) SaveEngineList();
}
// following implemented as macro to avoid type limitations
if(mnemonic[n]) { // if somehow the engine with the selected nickname is no longer found in the list, we skip
ReplaceEngine(&first, 0);
FloatToFront(&appData.recentEngineList, command[n]);
+ ASSIGN(currentEngine[0], command[n]);
}
}