X-Git-Url: http://winboard.nl/cgi-bin?a=blobdiff_plain;f=backend.c;h=8a47479ff637f2c1d2aa1a9fd72f72adb0f3203e;hb=ff23924264802924788f2a15c73a8d058e90432e;hp=a75a87ad0bc582ece2be05df6879b0cf66a290f3;hpb=7b721be1faca2a8524f56ab9e414d0bb97215765;p=xboard.git diff --git a/backend.c b/backend.c index a75a87a..8a47479 100644 --- a/backend.c +++ b/backend.c @@ -5,7 +5,7 @@ * Massachusetts. * * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006, - * 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014 Free Software Foundation, Inc. + * 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Free Software Foundation, Inc. * * Enhancements Copyright 2005 Alessandro Scotti * @@ -296,6 +296,8 @@ int promoDefaultAltered; int keepInfo = 0; /* [HGM] to protect PGN tags in auto-step game analysis */ static int initPing = -1; int border; /* [HGM] width of board rim, needed to size seek graph */ +char bestMove[MSG_SIZ]; +int solvingTime, totalTime; /* States for ics_getting_history */ #define H_FALSE 0 @@ -441,6 +443,7 @@ char *currentDebugFile; // [HGM] debug split: to remember name char cmailMove[CMAIL_MAX_GAMES][MOVE_LEN], cmailMsg[MSG_SIZ]; char bookOutput[MSG_SIZ*10], thinkOutput[MSG_SIZ*10], lastHint[MSG_SIZ]; char thinkOutput1[MSG_SIZ*10]; +char promoRestrict[MSG_SIZ]; ChessProgramState first, second, pairing; @@ -1738,6 +1741,7 @@ InitBackEnd3 P((void)) if(!blackPlaysFirst) { startedFromPositionFile = TRUE; CopyBoard(filePosition, boards[0]); + CopyBoard(initialPosition, boards[0]); } } if (initialMode == AnalyzeMode) { @@ -2125,7 +2129,7 @@ StringToVariant (char *e) } else for (i=0; i= VariantShogi && (p != e || isalpha(p[strlen(variantNames[i])]))) continue; + if(p && i >= VariantShogi && (p != e && !appData.icsActive || isalpha(p[strlen(variantNames[i])]))) continue; v = (VariantClass) i; found = TRUE; break; @@ -4886,13 +4890,13 @@ ParseBoard12 (char *string) if(fromY == DROP_RANK && k==toY && j==toX) continue; // dropped pieces always stand for themselves old = (k==toY && j==toX) ? boards[moveNum-1][fromY][fromX] : boards[moveNum-1][k][j]; // trace back mover if(old == new) continue; - if(old == PROMOTED new) boards[moveNum][k][j] = old; // prevent promoted pieces to revert to primordial ones + if(old == PROMOTED(new)) boards[moveNum][k][j] = old;// prevent promoted pieces to revert to primordial ones else if(new == WhiteWazir || new == BlackWazir) { if(old < WhiteCannon || old >= BlackPawn && old < BlackCannon) - boards[moveNum][k][j] = PROMOTED old; // choose correct type of Gold in promotion + boards[moveNum][k][j] = PROMOTED(old); // choose correct type of Gold in promotion else boards[moveNum][k][j] = old; // preserve type of Gold } else if((old == WhitePawn || old == BlackPawn) && new != EmptySquare) // Pawn promotions (but not e.p.capture!) - boards[moveNum][k][j] = PROMOTED new; // use non-primordial representation of chosen piece + boards[moveNum][k][j] = PROMOTED(new); // use non-primordial representation of chosen piece } } else { /* Move from ICS was illegal!? Punt. */ @@ -5228,7 +5232,7 @@ SendMoveToICS (ChessMove moveType, int fromX, int fromY, int toX, int toY, char case WhitePromotion: case BlackPromotion: if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier || - gameInfo.variant == VariantMakruk || gameInfo.variant == VariantASEAN) + gameInfo.variant == VariantMakruk) snprintf(user_move, MSG_SIZ, "%c%c%c%c=%c\n", AAA + fromX, ONE + fromY, AAA + toX, ONE + toY, PieceToChar(WhiteFerz)); @@ -5334,11 +5338,11 @@ UploadGameEvent () SendToICS(ics_type == ICS_ICC ? "tag result Game in progress\n" : "commit\n"); } -int killX = -1, killY = -1; // [HGM] lion: used for passing e.p. capture square to MakeMove +int killX = -1, killY = -1, kill2X = -1, kill2Y = -1; // [HGM] lion: used for passing e.p. capture square to MakeMove int legNr = 1; void -CoordsToComputerAlgebraic (int rf, int ff, int rt, int ft, char promoChar, char move[7]) +CoordsToComputerAlgebraic (int rf, int ff, int rt, int ft, char promoChar, char move[9]) { if (rf == DROP_RANK) { if(ff == EmptySquare) sprintf(move, "@@@@\n"); else // [HGM] pass @@ -5348,7 +5352,10 @@ CoordsToComputerAlgebraic (int rf, int ff, int rt, int ft, char promoChar, char if (promoChar == 'x' || promoChar == NULLCHAR) { sprintf(move, "%c%c%c%c\n", AAA + ff, ONE + rf, AAA + ft, ONE + rt); - if(killX >= 0 && killY >= 0) sprintf(move+4, ";%c%c\n", AAA + killX, ONE + killY); + if(killX >= 0 && killY >= 0) { + sprintf(move+4, ";%c%c\n", AAA + killX, ONE + killY); + if(kill2X >= 0 && kill2Y >= 0) sprintf(move+7, "%c%c\n", AAA + killX, ONE + killY); + } } else { sprintf(move, "%c%c%c%c%c\n", AAA + ff, ONE + rf, AAA + ft, ONE + rt, promoChar); @@ -5376,7 +5383,7 @@ static ClickType lastClickType; int Partner (ChessSquare *p) { // change piece into promotion partner if one shogi-promotes to the other - int stride = gameInfo.variant == VariantChu ? 22 : 11; + int stride = gameInfo.variant == VariantChu ? WhiteTokin : 11; ChessSquare partner; partner = (*p/stride & 1 ? *p - stride : *p + stride); if(PieceToChar(*p) != '+' && PieceToChar(partner) != '+') return 0; @@ -5402,8 +5409,10 @@ Sweep (int step) else if(promoSweep == BlackPawn && step < 0 && !toggleFlag) promoSweep = WhitePawn; else if(promoSweep == WhiteKing && step > 0 && !toggleFlag) promoSweep = BlackKing; if(!step) step = -1; - } while(PieceToChar(promoSweep) == '.' || PieceToChar(promoSweep) == '~' || promoSweep == pawn || + } while(PieceToChar(promoSweep) == '.' || PieceToChar(promoSweep) == '~' || !toggleFlag && PieceToChar(promoSweep) == '+' || // skip promoted versions of other + promoRestrict[0] ? !strchr(promoRestrict, ToUpper(PieceToChar(promoSweep))) : // if choice set available, use it + promoSweep == pawn || appData.testLegality && (promoSweep == king || gameInfo.variant != VariantChuChess && (promoSweep == WhiteLion || promoSweep == BlackLion))); if(toX >= 0) { @@ -5655,21 +5664,26 @@ ParsePV (char *pv, Boolean storeComments, Boolean atEnd) } int -MultiPV (ChessProgramState *cps) +MultiPV (ChessProgramState *cps, int kind) { // check if engine supports MultiPV, and if so, return the number of the option that sets it int i; - for(i=0; inrOptions; i++) - if(!strcmp(cps->option[i].name, "MultiPV") && cps->option[i].type == Spin) - return i; + for(i=0; inrOptions; i++) { + char *s = cps->option[i].name; + if((kind & 1) && !StrCaseCmp(s, "MultiPV") && cps->option[i].type == Spin) return i; + if((kind & 2) && StrCaseStr(s, "multi") && StrCaseStr(s, "PV") + && StrCaseStr(s, "margin") && cps->option[i].type == Spin) return -i-2; + } return -1; } Boolean extendGame; // signals to UnLoadPV() if walked part of PV has to be appended to game +static int multi, pv_margin; +static ChessProgramState *activeCps; Boolean LoadMultiPV (int x, int y, char *buf, int index, int *start, int *end, int pane) { - int startPV, multi, lineStart, origIndex = index; + int startPV, lineStart, origIndex = index; char *p, buf2[MSG_SIZ]; ChessProgramState *cps = (pane ? &second : &first); @@ -5683,14 +5697,22 @@ LoadMultiPV (int x, int y, char *buf, int index, int *start, int *end, int pane) do{ while(buf[index] && buf[index] != '\n') index++; } while(buf[index] == '\n' && buf[index+1] == '\\' && buf[index+2] == ' ' && index++); // join kibitzed PV continuation line buf[index] = 0; - if(lineStart == 0 && gameMode == AnalyzeMode && (multi = MultiPV(cps)) >= 0) { - int n = cps->option[multi].value; - if(origIndex > 17 && origIndex < 24) { if(n>1) n--; } else if(origIndex > index - 6) n++; + if(lineStart == 0 && gameMode == AnalyzeMode) { + int n = 0; + if(origIndex > 17 && origIndex < 24) n--; else if(origIndex > index - 6) n++; + if(n == 0) { // click not on "fewer" or "more" + if((multi = -2 - MultiPV(cps, 2)) >= 0) { + pv_margin = cps->option[multi].value; + activeCps = cps; // non-null signals margin adjustment + } + } else if((multi = MultiPV(cps, 1)) >= 0) { + n += cps->option[multi].value; if(n < 1) n = 1; snprintf(buf2, MSG_SIZ, "option MultiPV=%d\n", n); if(cps->option[multi].value != n) SendToProgram(buf2, cps); cps->option[multi].value = n; *start = *end = 0; return FALSE; + } } else if(strstr(buf+lineStart, "exclude:") == buf+lineStart) { // exclude moves clicked ExcludeClick(origIndex - lineStart); return FALSE; @@ -5738,6 +5760,16 @@ void UnLoadPV () { int oldFMM = forwardMostMove; // N.B.: this was currentMove before PV was loaded! + if(activeCps) { + if(pv_margin != activeCps->option[multi].value) { + char buf[MSG_SIZ]; + snprintf(buf, MSG_SIZ, "option %s=%d\n", "Multi-PV Margin", pv_margin); + SendToProgram(buf, activeCps); + activeCps->option[multi].value = pv_margin; + } + activeCps = NULL; + return; + } if(endPV < 0) return; if(appData.autoCopyPV) CopyFENToClipboard(); endPV = -1; @@ -5766,6 +5798,17 @@ MovePV (int x, int y, int h) { // step through PV based on mouse coordinates (called on mouse move) int margin = h>>3, step = 0, threshold = (pieceSweep == EmptySquare ? 10 : 15); + if(activeCps) { // adjusting engine's multi-pv margin + if(x > lastX) pv_margin++; else + if(x < lastX) pv_margin -= (pv_margin > 0); + if(x != lastX) { + char buf[MSG_SIZ]; + snprintf(buf, MSG_SIZ, "margin = %d", pv_margin); + DisplayMessage(buf, ""); + } + lastX = x; + return; + } // we must somehow check if right button is still down (might be released off board!) if(endPV < 0 && pieceSweep == EmptySquare) return; // needed in XBoard because lastX/Y is shared :-( if(abs(x - lastX) < threshold && abs(y - lastY) < threshold) return; @@ -5947,37 +5990,38 @@ SetUpShuffle (Board board, int number) } int -ptclen (const char *s) +ptclen (const char *s, char *escapes) { int n = 0; - while(*s) n += (*s != '\'' && *s != '"' && *s != '`' && *s != '!'), s++; + if(!*escapes) return strlen(s); + while(*s) n += (*s != '/' && !strchr(escapes, *s)), s++; return n; } int -SetCharTable (char *table, const char * map) +SetCharTableEsc (unsigned char *table, const char * map, char * escapes) /* [HGM] moved here from winboard.c because of its general usefulness */ /* Basically a safe strcpy that uses the last character as King */ { - int result = FALSE; int NrPieces; + int result = FALSE; int NrPieces, offs; - if( map != NULL && (NrPieces=ptclen(map)) <= (int) EmptySquare + if( map != NULL && (NrPieces=ptclen(map, escapes)) <= (int) EmptySquare && NrPieces >= 12 && !(NrPieces&1)) { int i, j = 0; /* [HGM] Accept even length from 12 to 88 */ for( i=0; i<(int) EmptySquare; i++ ) table[i] = '.'; - for( i=0; ialphaRank) { /* [HGM] shogi: translate coords */ message[1] = BOARD_RGHT - 1 - j + '1'; @@ -6411,12 +6461,12 @@ SendBoard (ChessProgramState *cps, int moveNum) && ((int) *bp >= (int) BlackPawn)) { if(j == BOARD_LEFT-2) snprintf(message, MSG_SIZ, "%c@%d\n", ToUpper(PieceToChar(*bp)), bp[1]); - else snprintf(message,MSG_SIZ, "%c%c%c\n", ToUpper(PieceToChar(*bp)), - AAA + j, ONE + i); + else snprintf(message,MSG_SIZ, "%c%c%d\n", ToUpper(PieceToChar(*bp)), + AAA + j, ONE + i - '0'); if(message[0] == '+' || message[0] == '~') { - snprintf(message, MSG_SIZ,"%c%c%c+\n", - PieceToChar((ChessSquare)(DEMOTED *bp)), - AAA + j, ONE + i); + snprintf(message, MSG_SIZ,"%c%c%d+\n", + PieceToChar((ChessSquare)(DEMOTED(*bp))), + AAA + j, ONE + i - '0'); } if(cps->alphaRank) { /* [HGM] shogi: translate coords */ message[1] = BOARD_RGHT - 1 - j + '1'; @@ -6546,8 +6596,10 @@ DefaultPromoChoice (int white) { ChessSquare result; if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier || - gameInfo.variant == VariantMakruk || gameInfo.variant == VariantASEAN) + gameInfo.variant == VariantMakruk) result = WhiteFerz; // no choice + else if(gameInfo.variant == VariantASEAN) + result = WhiteRook; // no choice else if(gameInfo.variant == VariantSuicide || gameInfo.variant == VariantGiveaway) result= WhiteKing; // in Suicide Q is the last thing we want else if(gameInfo.variant == VariantSpartan) @@ -6580,7 +6632,7 @@ HasPromotionChoice (int fromX, int fromY, int toX, int toY, char *promoChoice, i if(gameInfo.variant == VariantChu) { int p = piece >= BlackPawn ? BLACK_TO_WHITE piece : piece; promotionZoneSize = BOARD_HEIGHT/3; - highestPromotingPiece = (p >= WhiteLion || PieceToChar(piece + 22) == '.') ? WhitePawn : WhiteLion; + highestPromotingPiece = (p >= WhiteTokin || PieceToChar(piece + WhiteTokin) != '+') ? WhitePawn : WhiteTokin-1; } else if(gameInfo.variant == VariantShogi) { promotionZoneSize = BOARD_HEIGHT/3 +(BOARD_HEIGHT == 8); highestPromotingPiece = (int)WhiteAlfil; @@ -6639,7 +6691,7 @@ HasPromotionChoice (int fromX, int fromY, int toX, int toY, char *promoChoice, i // we either have a choice what to promote to, or (in Shogi) whether to promote if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier || - gameInfo.variant == VariantMakruk || gameInfo.variant == VariantASEAN) { + gameInfo.variant == VariantMakruk) { ChessSquare p=BlackFerz; // no choice while(p < EmptySquare) { //but make sure we use piece that exists *promoChoice = PieceToChar(p++); @@ -6985,7 +7037,7 @@ UserMoveEvent(int fromX, int fromY, int toX, int toY, int promoChar) if(!appData.pieceMenu && toX == fromX && toY == fromY && boards[0][rf][ff] != EmptySquare) { ChessSquare q, p = boards[0][rf][ff]; if(p >= BlackPawn) p = BLACK_TO_WHITE p; - if(CHUPROMOTED p < BlackPawn) p = q = CHUPROMOTED boards[0][rf][ff]; + if(CHUPROMOTED(p) < BlackPawn) p = q = CHUPROMOTED(boards[0][rf][ff]); else p = CHUDEMOTED (q = boards[0][rf][ff]); if(PieceToChar(q) == '+') gatingPiece = p; } @@ -7031,6 +7083,8 @@ UserMoveEvent(int fromX, int fromY, int toX, int toY, int promoChar) if(fromY == DROP_RANK && fromX == EmptySquare && (gameMode == AnalyzeMode || gameMode == EditGame || PosFlags(0) & F_NULL_MOVE)) moveType = NormalMove; + if(moveType == IllegalMove && legal[toY][toX] > 1) moveType = NormalMove; // someone explicitly told us this move is legal + /* [HGM] but possibly ignore an IllegalMove result */ if (appData.testLegality) { if (moveType == IllegalMove || moveType == ImpossibleMove) { @@ -7049,6 +7103,7 @@ UserMoveEvent(int fromX, int fromY, int toX, int toY, int promoChar) if(addToBookFlag) { // adding moves to book char buf[MSG_SIZ], move[MSG_SIZ]; CoordsToAlgebraic(boards[currentMove], PosFlags(currentMove), fromY, fromX, toY, toX, promoChar, move); + if(killX >= 0) snprintf(move, MSG_SIZ, "%c%dx%c%d-%c%d", fromX + AAA, fromY + ONE - '0', killX + AAA, killY + ONE - '0', toX + AAA, toY + ONE - '0'); snprintf(buf, MSG_SIZ, " 0.0%% 1 %s\n", move); AddBookMove(buf); addToBookFlag = FALSE; @@ -7265,7 +7320,7 @@ MarkByFEN(char *fen) int s = 0; marker[r][f] = 0; if(*fen == 'M') legal[r][f] = 2; else // request promotion choice - if(*fen >= 'A' && *fen <= 'Z') legal[r][f] = 1; else + if(*fen >= 'A' && *fen <= 'Z') legal[r][f] = 3; else if(*fen >= 'a' && *fen <= 'z') *fen += 'A' - 'a'; if(*fen == '/' && f > BOARD_LEFT) f = BOARD_LEFT, r--; else if(*fen == 'T') marker[r][f++] = 0; else @@ -7297,8 +7352,8 @@ Mark (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VO if(rf == fromY && ff == fromX && (killX < 0 ? !(rt == rf && ft == ff) && legNr & 1 : rt == killY && ft == killX || legNr & 2)) (*m)[rt][ft] = 1 + (board[rt][ft] != EmptySquare || kind == WhiteCapturesEnPassant - || kind == BlackCapturesEnPassant) + 3*(kind == FirstLeg && killX < 0), legal[rt][ft] = 1; - else if(flags & F_MANDATORY_CAPTURE && board[rt][ft] != EmptySquare) (*m)[rt][ft] = 3, legal[rt][ft] = 1; + || kind == BlackCapturesEnPassant) + 3*(kind == FirstLeg && killX < 0), legal[rt][ft] = 3; + else if(flags & F_MANDATORY_CAPTURE && board[rt][ft] != EmptySquare) (*m)[rt][ft] = 3, legal[rt][ft] = 3; } static int hoverSavedValid; @@ -7350,7 +7405,7 @@ CanPromote (ChessSquare piece, int y) if(IS_SHOGI(gameInfo.variant) || gameInfo.variant == VariantXiangqi || gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat || gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier || - gameInfo.variant == VariantMakruk || gameInfo.variant == VariantASEAN) return FALSE; + gameInfo.variant == VariantMakruk) return FALSE; return (piece == BlackPawn && y <= zone || piece == WhitePawn && y >= BOARD_HEIGHT-1-zone || piece == BlackLance && y <= zone || @@ -7512,7 +7567,7 @@ LeftClick (ClickType clickType, int xPix, int yPix) DragPieceBegin(xPix, yPix, FALSE); dragging = 1; if(appData.sweepSelect && CanPromote(piece = boards[currentMove][fromY][fromX], fromY)) { promoSweep = defaultPromoChoice; - selectFlag = 0; lastX = xPix; lastY = yPix; + selectFlag = 0; lastX = xPix; lastY = yPix; *promoRestrict = 0; Sweep(0); // Pawn that is going to promote: preview promotion piece DisplayMessage("", _("Pull pawn backwards to under-promote")); } @@ -7525,7 +7580,7 @@ LeftClick (ClickType clickType, int xPix, int yPix) return; } } -printf("to click %d,%d\n",x,y); + /* fromX != -1 */ if (clickType == Press && gameMode != EditPosition) { ChessSquare fromP; @@ -7577,7 +7632,7 @@ printf("to click %d,%d\n",x,y); DragPieceBegin(xPix, yPix, FALSE); if(appData.sweepSelect && CanPromote(piece = boards[currentMove][y][x], y)) { promoSweep = defaultPromoChoice; - selectFlag = 0; lastX = xPix; lastY = yPix; + selectFlag = 0; lastX = xPix; lastY = yPix; *promoRestrict = 0; Sweep(0); // Pawn that is going to promote: preview promotion piece } } @@ -7588,10 +7643,10 @@ printf("to click %d,%d\n",x,y); // ignore clicks on holdings if(x < BOARD_LEFT || x >= BOARD_RGHT) return; } -printf("A type=%d\n",clickType); - if(x == fromX && y == fromY && gameMode == EditPosition && SubtractTimeMarks(&lastClickTime, &prevClickTime) < 200) { + if(x == fromX && y == fromY && clickType == Press && gameMode == EditPosition && SubtractTimeMarks(&lastClickTime, &prevClickTime) < 200) { gatingPiece = boards[currentMove][fromY][fromX]; // prepare to copy rather than move + DragPieceBegin(xPix, yPix, FALSE); dragging = 1; return; } @@ -7626,7 +7681,7 @@ printf("A type=%d\n",clickType); } clearFlag = 0; -printf("B\n"); + if(gameMode != EditPosition && !appData.testLegality && !legal[y][x] && fromX >= BOARD_LEFT && fromX < BOARD_RGHT && (x != killX || y != killY) && !sweepSelecting) { if(dragging) DragPieceEnd(xPix, yPix), dragging = 0; @@ -7634,7 +7689,7 @@ printf("B\n"); DrawPosition(TRUE, NULL); return; // ignore to-click } -printf("(%d,%d)-(%d,%d) %d %d\n",fromX,fromY,toX,toY,x,y); + /* we now have a different from- and (possibly off-board) to-square */ /* Completed move */ if(!sweepSelecting) { @@ -7663,8 +7718,9 @@ printf("(%d,%d)-(%d,%d) %d %d\n",fromX,fromY,toX,toY,x,y); if(legal[y][x] == 2 || HasPromotionChoice(fromX, fromY, toX, toY, &promoChoice, FALSE)) { if(appData.sweepSelect) { promoSweep = defaultPromoChoice; - if(gameInfo.variant != VariantChuChess && PieceToChar(CHUPROMOTED piece) == '+') promoSweep = CHUPROMOTED piece; + if(gameInfo.variant != VariantChuChess && PieceToChar(CHUPROMOTED(piece)) == '+') promoSweep = CHUPROMOTED(piece); selectFlag = 0; lastX = xPix; lastY = yPix; + ReportClick("put", x, y); // extra put to prompt engine for 'choice' command Sweep(0); // Pawn that is going to promote: preview promotion piece sweepSelecting = 1; DisplayMessage("", _("Pull pawn backwards to under-promote")); @@ -7680,6 +7736,7 @@ printf("(%d,%d)-(%d,%d) %d %d\n",fromX,fromY,toX,toY,x,y); } } else if(sweepSelecting) { // this must be the up-click corresponding to the down-click that started the sweep sweepSelecting = 0; appData.animate = FALSE; // do not animate, a selected piece already on to-square + *promoRestrict = 0; if (appData.animate || appData.highlightLastMove) { SetHighlights(fromX, fromY, toX, toY); } else { @@ -7699,7 +7756,9 @@ printf("(%d,%d)-(%d,%d) %d %d\n",fromX,fromY,toX,toY,x,y); if(marker[y][x] == 5) { // [HGM] lion: this was the release of a to-click or drag on a cyan square dragging *= 2; // flag button-less dragging if we are dragging MarkTargetSquares(1); - if(x == killX && y == killY) killX = killY = -1; else { + if(x == killX && y == killY) killX = kill2X, killY = kill2Y, kill2X = kill2Y = -1; // cancel last kill + else { + kill2X = killX; kill2Y = killY; killX = x; killY = y; //remeber this square as intermediate ReportClick("put", x, y); // and inform engine ReportClick("lift", x, y); @@ -8092,7 +8151,7 @@ Adjudicate (ChessProgramState *cps) // most tests only when we understand the game, i.e. legality-checking on if( appData.testLegality ) { /* [HGM] Some more adjudications for obstinate engines */ - int nrW, nrB, bishopColor, staleW, staleB, nr[EmptySquare+1], i; + int nrW, nrB, bishopColor, staleW, staleB, nr[EmptySquare+2], i; static int moveCount = 6; ChessMove result; char *reason = NULL; @@ -8534,7 +8593,7 @@ DeferredBookMove (void) static int savedWhitePlayer, savedBlackPlayer, pairingReceived; static ChessProgramState *stalledEngine; -static char stashedInputMove[MSG_SIZ]; +static char stashedInputMove[MSG_SIZ], abortEngineThink; void HandleMachineMove (char *message, ChessProgramState *cps) @@ -8617,24 +8676,27 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h return; } + if(cps->usePing) { + /* This method is only useful on engines that support ping */ + if(abortEngineThink) { + if (appData.debugMode) { + fprintf(debugFP, "Undoing move from aborted think of %s\n", cps->which); + } + SendToProgram("undo\n", cps); + return; + } + if (cps->lastPing != cps->lastPong) { - if (gameMode == BeginningOfGame) { /* Extra move from before last new; ignore */ if (appData.debugMode) { fprintf(debugFP, "Ignoring extra move from %s\n", cps->which); } - } else { - if (appData.debugMode) { - fprintf(debugFP, "Undoing extra move from %s, gameMode %d\n", - cps->which, gameMode); - } - - SendToProgram("undo\n", cps); - } return; } + } else { + switch (gameMode) { case BeginningOfGame: /* Extra move from before last reset; ignore */ @@ -8680,15 +8742,21 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h } return; } + } if(cps->alphaRank) AlphaRank(machineMove, 4); // [HGM] lion: (some very limited) support for Alien protocol - killX = killY = -1; + killX = killY = kill2X = kill2Y = -1; if(machineMove[strlen(machineMove)-1] == ',') { // move ends in coma: non-final leg of composite move safeStrCpy(firstLeg, machineMove, 20); // just remember it for processing when second leg arrives return; - } else if(firstLeg[0]) { // there was a previous leg; + } + if(p = strchr(machineMove, ',')) { // we got both legs in one (happens on book move) + safeStrCpy(firstLeg, machineMove, 20); // kludge: fake we received the first leg earlier, and clip it off + safeStrCpy(machineMove, firstLeg + (p - machineMove) + 1, 20); + } + if(firstLeg[0]) { // there was a previous leg; // only support case where same piece makes two step char buf[20], *p = machineMove+1, *q = buf+1, f; safeStrCpy(buf, machineMove, 20); @@ -8707,8 +8775,8 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h snprintf(buf1, MSG_SIZ*10, _("Illegal move \"%s\" from %s machine"), machineMove, _(cps->which)); DisplayMoveError(buf1); - snprintf(buf1, MSG_SIZ*10, "Xboard: Forfeit due to invalid move: %s (%c%c%c%c via %c%c) res=%d", - machineMove, fromX+AAA, fromY+ONE, toX+AAA, toY+ONE, killX+AAA, killY+ONE, moveType); + snprintf(buf1, MSG_SIZ*10, "Xboard: Forfeit due to invalid move: %s (%c%c%c%c via %c%c, %c%c) res=%d", + machineMove, fromX+AAA, fromY+ONE, toX+AAA, toY+ONE, killX+AAA, killY+ONE, kill2X+AAA, kill2Y+ONE, moveType); if (gameMode == TwoMachinesPlay) { GameEnds(machineWhite ? BlackWins : WhiteWins, buf1, GE_XBOARD); @@ -8773,6 +8841,17 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h free(fen); GameEnds(GameUnfinished, NULL, GE_XBOARD); } + if(appData.epd) { + if(solvingTime >= 0) { + snprintf(buf1, MSG_SIZ, "%d. %4.2fs\n", matchGame, solvingTime/100.); + totalTime += solvingTime; first.matchWins++; + } else { + snprintf(buf1, MSG_SIZ, "%d. wrong (%s)\n", matchGame, parseList[backwardMostMove]); + second.matchWins++; + } + OutputKibitz(2, buf1); + GameEnds(GameUnfinished, NULL, GE_XBOARD); + } /* [AS] Adjudicate game if needed (note: remember that forwardMostMove now points past the last move) */ if( gameMode == TwoMachinesPlay && appData.adjudicateLossThreshold != 0 && forwardMostMove >= adjudicateLossPlies ) { @@ -8923,7 +9002,7 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h if(appData.icsActive || forwardMostMove != 0 || cps != &first) return; *buf = NULLCHAR; if(sscanf(message, "setup (%s", buf) == 1) { - s = 8 + strlen(buf), buf[s-9] = NULLCHAR, SetCharTable(pieceToChar, buf); + s = 8 + strlen(buf), buf[s-9] = NULLCHAR, SetCharTableEsc(pieceToChar, buf, SUFFIXES); ASSIGN(appData.pieceToCharTable, buf); } dummy = sscanf(message+s, "%dx%d+%d_%s", &w, &h, &hand, varName); @@ -8934,7 +9013,7 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h appData.NrFiles = w; appData.NrRanks = h; appData.holdingsSize = hand; if(dummy == 4) gameInfo.variant = StringToVariant(varName); // parent variant InitPosition(1); // calls InitDrawingSizes to let new parameters take effect - if(*buf) SetCharTable(pieceToChar, buf); // do again, for it was spoiled by InitPosition + if(*buf) SetCharTableEsc(pieceToChar, buf, SUFFIXES); // do again, for it was spoiled by InitPosition startedFromSetupPosition = FALSE; } } @@ -8947,9 +9026,10 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h } if(sscanf(message, "piece %s %s", buf2, buf1) == 2) { ChessSquare piece = WhitePawn; - char *p=buf2; - if(*p == '+') piece = CHUPROMOTED WhitePawn, p++; - piece += CharToPiece(*p) - WhitePawn; + char *p=message+6, *q, *s = SUFFIXES, ID = *p; + if(*p == '+') piece = CHUPROMOTED(WhitePawn), ID = *++p; + if(q = strchr(s, p[1])) ID += 64*(q - s + 1), p++; + piece += CharToPiece(ID & 255) - WhitePawn; if(cps != &first || appData.testLegality && *engineVariant == NULLCHAR /* always accept definition of */ && piece != WhiteFalcon && piece != BlackFalcon /* wild-card pieces. */ && piece != WhiteCobra && piece != BlackCobra @@ -8961,10 +9041,15 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h if(piece < EmptySquare) { pieceDefs = TRUE; ASSIGN(pieceDesc[piece], buf1); - if(isupper(*p) && p[1] == '&') { ASSIGN(pieceDesc[WHITE_TO_BLACK piece], buf1); } + if((ID & 32) == 0 && p[1] == '&') { ASSIGN(pieceDesc[WHITE_TO_BLACK piece], buf1); } } return; } + if(sscanf(message, "choice %s", promoRestrict) == 1 && promoSweep != EmptySquare) { + promoSweep = PieceToChar(forwardMostMove&1 ? ToLower(*promoRestrict) : ToUpper(*promoRestrict)); + Sweep(0); + return; + } /* [HGM] Allow engine to set up a position. Don't ask me why one would * want this, I was asked to put it in, and obliged. */ @@ -9081,10 +9166,15 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h } initPing = -1; } + if(cps->lastPing == cps->lastPong && abortEngineThink) { + abortEngineThink = FALSE; + DisplayMessage("", ""); + ThawUI(); + } return; } if(!strncmp(message, "highlight ", 10)) { - if(appData.testLegality && appData.markers) return; + if(appData.testLegality && !*engineVariant && appData.markers) return; MarkByFEN(message+10); // [HGM] alien: allow engine to mark board squares return; } @@ -9499,6 +9589,7 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h buf1[0] = NULLCHAR; if (sscanf(message, "%d%c %d %d " u64Display " %[^\n]\n", &plylev, &plyext, &curscore, &time, &nodes, buf1) >= 5) { + char score_buf[MSG_SIZ]; if(nodes>>32 == u64Const(0xFFFFFFFF)) // [HGM] negative node count read nodes += u64Const(0x100000000); @@ -9521,6 +9612,14 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h if(appData.pvSAN[cps==&second]) pv = PvToSAN(buf1); + if(*bestMove) { // rememer time best EPD move was first found + int ff1, tf1, fr1, tr1, ff2, tf2, fr2, tr2; char pp1, pp2; + ChessMove mt; + int ok = ParseOneMove(bestMove, forwardMostMove, &mt, &ff1, &fr1, &tf1, &tr1, &pp1); + ok &= ParseOneMove(pv, forwardMostMove, &mt, &ff2, &fr2, &tf2, &tr2, &pp2); + solvingTime = (ok && ff1==ff2 && fr1==fr2 && tf1==tf2 && tr1==tr2 && pp1==pp2 ? time : -1); + } + if(serverMoves && (time > 100 || time == 0 && plylev > 7)) { char buf[MSG_SIZ]; FILE *f; @@ -9591,11 +9690,17 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h [AS] Protect the thinkOutput buffer from overflow... this is only useful if buf1 hasn't overflowed first! */ - snprintf(thinkOutput, sizeof(thinkOutput)/sizeof(thinkOutput[0]), "[%d]%c%+.2f %s%s", + if(curscore >= MATE_SCORE) + snprintf(score_buf, MSG_SIZ, "#%d", curscore - MATE_SCORE); + else if(curscore <= -MATE_SCORE) + snprintf(score_buf, MSG_SIZ, "#%d", curscore + MATE_SCORE); + else + snprintf(score_buf, MSG_SIZ, "%+.2f", ((double) curscore) / 100.0); + snprintf(thinkOutput, sizeof(thinkOutput)/sizeof(thinkOutput[0]), "[%d]%c%s %s%s", plylev, (gameMode == TwoMachinesPlay ? ToUpper(cps->twoMachinesColor[0]) : ' '), - ((double) curscore) / 100.0, + score_buf, prefixHint ? lastHint : "", prefixHint ? " " : "" ); @@ -9937,7 +10042,7 @@ ParseGameHistory (char *game) void ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board) { - ChessSquare captured = board[toY][toX], piece, pawn, king, killed; int p, rookX, oldEP, epRank, berolina = 0; + ChessSquare captured = board[toY][toX], piece, pawn, king, killed, killed2; int p, rookX, oldEP, epRank, 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 */ @@ -9959,11 +10064,14 @@ ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board) // ChessSquare victim; int i; - if( killX >= 0 && killY >= 0 ) // [HGM] lion: Lion trampled over something + if( killX >= 0 && killY >= 0 ) { // [HGM] lion: Lion trampled over something // victim = board[killY][killX], killed = board[killY][killX], board[killY][killX] = EmptySquare, board[EP_STATUS] = EP_CAPTURE; + if( kill2X >= 0 && kill2Y >= 0) + killed2 = board[kill2Y][kill2X], board[kill2Y][kill2X] = EmptySquare; + } if( board[toY][toX] != EmptySquare ) { board[EP_STATUS] = EP_CAPTURE; @@ -10039,6 +10147,11 @@ ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board) board[toY][toX + (killX < fromX ? 1 : -1)] = killed; board[EP_STATUS] = EP_NONE; // capture was fake! } else + if(nrCastlingRights == 0 && board[toY][toX] < EmptySquare && (piece < BlackPawn) == (board[toY][toX] < BlackPawn)) { + board[fromY][fromX] = board[toY][toX]; // capture own will lead to swapping + board[toY][toX] = piece; + board[EP_STATUS] = EP_NONE; // capture was fake! + } else /* Code added by Tord: */ /* FRC castling assumed when king captures friendly rook. [HGM] or RxK for S-Chess */ if (board[fromY][fromX] == WhiteKing && board[toY][toX] == WhiteRook || @@ -10063,6 +10176,9 @@ ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board) } /* End of code added by Tord */ + } else if (pieceDesc[piece] && piece == king && !strchr(pieceDesc[piece], 'O') && strchr(pieceDesc[piece], 'i')) { + board[fromY][fromX] = EmptySquare; // never castle if King has virgin moves defined on it other than castling + board[toY][toX] = piece; } else if (board[fromY][fromX] == king && fromX != BOARD_LEFT && fromX != BOARD_RGHT-1 // [HGM] cylinder */ && toY == fromY && toX > fromX+1) { @@ -10085,8 +10201,8 @@ ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board) ) { /* white pawn promotion */ board[toY][toX] = CharToPiece(ToUpper(promoChar)); - if(board[toY][toX] < WhiteCannon && PieceToChar(PROMOTED board[toY][toX]) == '~') /* [HGM] use shadow piece (if available) */ - board[toY][toX] = (ChessSquare) (PROMOTED board[toY][toX]); + if(board[toY][toX] < WhiteCannon && PieceToChar(PROMOTED(board[toY][toX])) == '~') /* [HGM] use shadow piece (if available) */ + 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) @@ -10150,8 +10266,8 @@ ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board) ) { /* black pawn promotion */ board[toY][toX] = CharToPiece(ToLower(promoChar)); - if(board[toY][toX] < BlackCannon && PieceToChar(PROMOTED board[toY][toX]) == '~') /* [HGM] use shadow piece (if available) */ - board[toY][toX] = (ChessSquare) (PROMOTED board[toY][toX]); + if(board[toY][toX] < BlackCannon && PieceToChar(PROMOTED(board[toY][toX])) == '~') /* [HGM] use shadow piece (if available) */ + 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) @@ -10225,10 +10341,10 @@ ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board) p = (int) captured; if (p >= (int) BlackPawn) { p -= (int)BlackPawn; - if(DEMOTED p >= 0 && PieceToChar(p) == '+') { + if(DEMOTED(p) >= 0 && PieceToChar(p) == '+') { /* Restore shogi-promoted piece to its original first */ - captured = (ChessSquare) (DEMOTED captured); - p = DEMOTED p; + captured = (ChessSquare) (DEMOTED(captured)); + p = DEMOTED(p); } p = PieceToNumber((ChessSquare)p); if(p >= gameInfo.holdingsSize) { p = 0; captured = BlackPawn; } @@ -10236,9 +10352,9 @@ ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board) board[p][BOARD_WIDTH-1] = BLACK_TO_WHITE captured; } else { p -= (int)WhitePawn; - if(DEMOTED p >= 0 && PieceToChar(p) == '+') { - captured = (ChessSquare) (DEMOTED captured); - p = DEMOTED p; + if(DEMOTED(p) >= 0 && PieceToChar(p) == '+') { + captured = (ChessSquare) (DEMOTED(captured)); + p = DEMOTED(p); } p = PieceToNumber((ChessSquare)p); if(p >= gameInfo.holdingsSize) { p = 0; captured = WhitePawn; } @@ -10266,13 +10382,13 @@ ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board) } else if(promoChar == '+') { /* [HGM] Shogi-style promotions, to piece implied by original (Might overwrite ordinary Pawn promotion) */ - board[toY][toX] = (ChessSquare) (CHUPROMOTED piece); + board[toY][toX] = (ChessSquare) (CHUPROMOTED(piece)); if(gameInfo.variant == VariantChuChess && (piece == WhiteKnight || piece == BlackKnight)) board[toY][toX] = piece + WhiteLion - WhiteKnight; // adjust Knight promotions to Lion } else if(!appData.testLegality && promoChar != NULLCHAR && promoChar != '=') { // without legality testing, unconditionally believe 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 + 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) @@ -11368,16 +11484,17 @@ GameEnds (ChessMove result, char *resultDetails, int whosays) && gameInfo.variant != VariantLosers && gameInfo.variant != VariantGiveaway && gameInfo.variant != VariantSuicide // [HGM] losers: except in losers, of course... && result != GameIsDrawn) - { int i, j, k=0, color = (result==WhiteWins ? (int)WhitePawn : (int)BlackPawn); + { int i, j, k=0, oppoKings = 0, color = (result==WhiteWins ? (int)WhitePawn : (int)BlackPawn); for(j=BOARD_LEFT; j= 0 && p <= (int)WhiteKing) k++; + oppoKings += (p + color == WhiteKing + BlackPawn - color); } if (appData.debugMode) { fprintf(debugFP, "GE(%d, %s, %d) bare king k=%d color=%d\n", result, resultDetails ? resultDetails : "(null)", whosays, k, color); } - if(k <= 1) { + if(k <= 1 && oppoKings > 0) { // the latter needed in Atomic, where bare K wins if opponent King already destroyed result = GameIsDrawn; snprintf(buf, MSG_SIZ, "%s but bare king", resultDetails); resultDetails = buf; @@ -11964,7 +12081,7 @@ LoadGameOneMove (ChessMove readAhead) case BlackASideCastleFR: /* POP Fabien */ if (appData.debugMode) - fprintf(debugFP, "Parsed %s into %s\n", yy_text, currentMoveString); + fprintf(debugFP, "Parsed %s into %s virgin=%x,%x\n", yy_text, currentMoveString, boards[forwardMostMove][TOUCHED_W], boards[forwardMostMove][TOUCHED_B]); fromX = currentMoveString[0] - AAA; fromY = currentMoveString[1] - ONE; toX = currentMoveString[2] - AAA; @@ -12924,7 +13041,7 @@ LoadGame (FILE *f, int gameNumber, char *title, int useList) gameInfo.event = StrSave(yy_text); } - startedFromSetupPosition = FALSE; + startedFromSetupPosition = startedFromPositionFile; // [HGM] while (cm == PGNTag) { if (appData.debugMode) fprintf(debugFP, "Parsed PGNTag: %s\n", yy_text); @@ -13288,10 +13405,14 @@ LoadPosition (FILE *f, int positionNumber, char *title) } if (fenMode) { + char *p; if (!ParseFEN(initial_position, &blackPlaysFirst, line, TRUE)) { DisplayError(_("Bad FEN position in file"), 0); return FALSE; } + if((p = strstr(line, ";")) && (p = strstr(p+1, "bm "))) { // EPD with best move + sscanf(p+3, "%s", bestMove); + } else *bestMove = NULLCHAR; } else { (void) fgets(line, MSG_SIZ, f); (void) fgets(line, MSG_SIZ, f); @@ -14914,6 +15035,16 @@ EditGameEvent () case MachinePlaysBlack: case BeginningOfGame: SendToProgram("force\n", &first); + if(gameMode == (forwardMostMove & 1 ? MachinePlaysBlack : MachinePlaysWhite)) { // engine is thinking + if (first.usePing) { // [HGM] always send ping when we might interrupt machine thinking + char buf[MSG_SIZ]; + abortEngineThink = TRUE; + snprintf(buf, MSG_SIZ, "ping %d\n", initPing = ++first.lastPing); + SendToProgram(buf, &first); + DisplayMessage("Aborting engine think", ""); + FreezeUI(); + } + } SetUserThinkingEnables(); break; case PlayFromGameFile: @@ -15247,7 +15378,7 @@ EditPositionMenuEvent (ChessSquare selection, int x, int y) case PromotePiece: if(piece >= (int)WhitePawn && piece < (int)WhiteMan || piece >= (int)BlackPawn && piece < (int)BlackMan ) { - selection = (ChessSquare) (PROMOTED piece); + selection = (ChessSquare) (PROMOTED(piece)); } else if(piece == EmptySquare) selection = WhiteSilver; else selection = (ChessSquare)((int)piece - 1); goto defaultlabel; @@ -15255,7 +15386,7 @@ EditPositionMenuEvent (ChessSquare selection, int x, int y) case DemotePiece: if(piece > (int)WhiteMan && piece <= (int)WhiteKing || piece > (int)BlackMan && piece <= (int)BlackKing ) { - selection = (ChessSquare) (DEMOTED piece); + selection = (ChessSquare) (DEMOTED(piece)); } else if(piece == EmptySquare) selection = BlackSilver; else selection = (ChessSquare)((int)piece + 1); goto defaultlabel; @@ -17834,7 +17965,7 @@ char * PositionToFEN (int move, char *overrideCastling, int moveCounts) { int i, j, fromX, fromY, toX, toY; - int whiteToPlay; + int whiteToPlay, haveRights = nrCastlingRights; char buf[MSG_SIZ]; char *p, *q; int emptycount; @@ -17861,12 +17992,13 @@ PositionToFEN (int move, char *overrideCastling, int moveCounts) if(PieceToChar(piece) == '+') { /* [HGM] write promoted pieces as '+' (Shogi) */ *p++ = '+'; - piece = (ChessSquare)(CHUDEMOTED piece); + piece = (ChessSquare)(CHUDEMOTED(piece)); } *p++ = (piece == DarkSquare ? '*' : PieceToChar(piece)); + if(*p = PieceSuffix(piece)) p++; if(p[-1] == '~') { /* [HGM] flag promoted pieces as '~' (Crazyhouse) */ - p[-1] = PieceToChar((ChessSquare)(CHUDEMOTED piece)); + p[-1] = PieceToChar((ChessSquare)(CHUDEMOTED(piece))); *p++ = '~'; } } @@ -17907,10 +18039,28 @@ PositionToFEN (int move, char *overrideCastling, int moveCounts) *p++ = whiteToPlay ? 'w' : 'b'; *p++ = ' '; + if(pieceDesc[WhiteKing] && strchr(pieceDesc[WhiteKing], 'i') && !strchr(pieceDesc[WhiteKing], 'O')) { // redefined without castling + haveRights = 0; q = p; + for(i=BOARD_RGHT-1; i>=BOARD_LEFT; i--) { + piece = boards[move][0][i]; + if(piece >= WhitePawn && piece <= WhiteKing && pieceDesc[piece] && strchr(pieceDesc[piece], 'i')) { // piece with initial move + if(!(boards[move][TOUCHED_W] & 1<=BOARD_LEFT; i--) { + piece = boards[move][BOARD_HEIGHT-1][i]; + if(piece >= BlackPawn && piece <= BlackKing && pieceDesc[piece] && strchr(pieceDesc[piece], 'i')) { // piece with initial move + if(!(boards[move][TOUCHED_B] & 1<=BOARD_LEFT+q; i--) @@ -17957,9 +18107,9 @@ PositionToFEN (int move, char *overrideCastling, int moveCounts) } if(q) *p++ = 'Q'; k = 0; - if(boards[move][CASTLING][3] == BOARD_RGHT-1 && + if(boards[move][CASTLING][3] != NoRights && boards[move][CASTLING][5] != NoRights ) k = 1, *p++ = 'k'; - q = (boards[move][CASTLING][4] == BOARD_LEFT && + q = (boards[move][CASTLING][4] != NoRights && boards[move][CASTLING][5] != NoRights ); if(handB) { for(i=BOARD_RGHT-1-k; i>=BOARD_LEFT+q; i--) @@ -18036,7 +18186,7 @@ ParseFEN (Board board, int *blackPlaysFirst, char *fen, Boolean autoSize) int i, j, k, w=0, subst=0, shuffle=0, wKingRank = -1, bKingRank = -1; char *p, c; int emptycount, virgin[BOARD_FILES]; - ChessSquare piece; + ChessSquare piece, king = (gameInfo.variant == VariantKnightmate ? WhiteUnicorn : WhiteKing); p = fen; @@ -18050,7 +18200,7 @@ ParseFEN (Board board, int *blackPlaysFirst, char *fen, Boolean autoSize) while (emptycount--) board[i][(j++)+gameInfo.holdingsWidth] = EmptySquare; if (*p == '/') p++; - else if(autoSize) { // we stumbled unexpectedly into end of board + else if(autoSize && i != BOARD_HEIGHT-1) { // we stumbled unexpectedly into end of board for(k=i; k= gameInfo.boardWidth) return FALSE; if(*p=='+') { - piece = CharToPiece(*++p); + char c = *++p; + if(q = strchr(s, p[1])) p++; + piece = CharToPiece(c + (q ? 64*(q - s + 1) : 0)); if(piece == EmptySquare) return FALSE; /* unknown piece */ - piece = (ChessSquare) (CHUPROMOTED piece ); p++; + piece = (ChessSquare) (CHUPROMOTED(piece)); p++; if(PieceToChar(piece) != '+') return FALSE; /* unpromotable piece */ - } else piece = CharToPiece(*p++); + } else { + char c = *p++; + if(q = strchr(s, *p)) p++; + piece = CharToPiece(c + (q ? 64*(q - s + 1) : 0)); + } if(piece==EmptySquare) return FALSE; /* unknown piece */ if(*p == '~') { /* [HGM] make it a promoted piece for Crazyhouse */ - piece = (ChessSquare) (PROMOTED piece); + piece = (ChessSquare) (PROMOTED(piece)); if(PieceToChar(piece) != '~') return FALSE; /* cannot be a promoted piece */ p++; } board[i][(j++)+gameInfo.holdingsWidth] = piece; - if(piece == WhiteKing) wKingRank = i; - if(piece == BlackKing) bKingRank = i; + if(piece == king) wKingRank = i; + if(piece == WHITE_TO_BLACK king) bKingRank = i; } else { return FALSE; } @@ -18107,7 +18264,7 @@ ParseFEN (Board board, int *blackPlaysFirst, char *fen, Boolean autoSize) } while (*p == '/' || *p == ' ') p++; - if(autoSize) appData.NrFiles = w, InitPosition(TRUE); + if(autoSize && w != 0) appData.NrFiles = w, InitPosition(TRUE); /* [HGM] by default clear Crazyhouse holdings, if present */ if(gameInfo.holdingsWidth) { @@ -18208,6 +18365,7 @@ ParseFEN (Board board, int *blackPlaysFirst, char *fen, Boolean autoSize) /* set defaults in case FEN is incomplete */ board[EP_STATUS] = EP_UNKNOWN; + board[TOUCHED_W] = board[TOUCHED_B] = 0; for(i=0; i> 1; // for these variant scanning fails - if(whiteKingFile == NoRights || board[0][whiteKingFile] != WhiteUnicorn - && board[0][whiteKingFile] != WhiteKing) whiteKingFile = NoRights; - if(blackKingFile == NoRights || board[BOARD_HEIGHT-1][blackKingFile] != BlackUnicorn - && board[BOARD_HEIGHT-1][blackKingFile] != BlackKing) blackKingFile = NoRights; + if(whiteKingFile == NoRights || board[castlingRank[2]][whiteKingFile] != WhiteUnicorn + && board[castlingRank[2]][whiteKingFile] != WhiteKing) whiteKingFile = NoRights; + if(blackKingFile == NoRights || board[castlingRank[5]][blackKingFile] != BlackUnicorn + && board[castlingRank[5]][blackKingFile] != BlackKing) blackKingFile = NoRights; switch(c) { case'K': for(i=BOARD_RGHT-1; board[castlingRank[2]][i]!=WhiteRook && i>whiteKingFile; i--);