X-Git-Url: http://winboard.nl/cgi-bin?a=blobdiff_plain;f=backend.c;h=c531656bed48f42a754c3587762858d3b45cdc9f;hb=v4.9.x;hp=48695ad804485bf07ab19ee8a0c448fe2cf6da12;hpb=a70f567b63dafae38af86be07c0cc3112a013cca;p=xboard.git diff --git a/backend.c b/backend.c index 48695ad..c531656 100644 --- a/backend.c +++ b/backend.c @@ -261,7 +261,7 @@ void ics_update_width P((int new_width)); extern char installDir[MSG_SIZ]; VariantClass startVariant; /* [HGM] nicks: initial variant */ Boolean abortMatch; -int deadRanks; +int deadRanks, handSize, handOffsets; extern int tinyLayout, smallLayout; ChessProgramStats programStats; @@ -913,13 +913,79 @@ InitEngine (ChessProgramState *cps, int n) 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) +{ + if(addToList) { + 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 @@ -948,14 +1014,11 @@ ReplaceEngine (ChessProgramState *cps, int n) appData.clockMode = TRUE; InitEngine(cps, n); UpdateLogos(TRUE); - if(n) return; // only startup first engine immediately; second can wait + if(n && !tryNr) return; // only startup first engine immediately; second can wait (unless autodetect) savCps = cps; // parameter to LoadEngine passed as globals, to allow scheduled calling :-( 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 "\" " @@ -963,31 +1026,11 @@ static char resetOptions[] = "-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; // 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; @@ -1019,34 +1062,14 @@ Load (ChessProgramState *cps, int i) } if(jar) { snprintf(buf3, MSG_SIZ, "java -jar %s", p); p = buf3; } ASSIGN(appData.chessProgram[i], p); + tryNr = 3; // requests adding to list without auto-detect + 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\n", - 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)-1, MSG_SIZ-strlen(buf), " %s\n", wbOptions); - firstChessProgramNames = malloc(len = strlen(q) + strlen(buf) + 1); - if(insert != q) insert[-1] = NULLCHAR; - snprintf(firstChessProgramNames, len, "%s\n%s%s", q, buf, insert); - if(q) free(q); - FloatToFront(&appData.recentEngineList, buf); - } + safeStrCpy(newEngineCommand, p, MSG_SIZ); ReplaceEngine(cps, i); } @@ -1591,8 +1614,6 @@ MatchEvent (int mode) NextMatchGame(); } -char *comboLine = NULL; // [HGM] recent: WinBoard's first-engine combobox line - void InitBackEnd3 P((void)) { @@ -1600,6 +1621,7 @@ 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 @@ -1623,7 +1645,7 @@ InitBackEnd3 P((void)) 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) { @@ -2484,7 +2506,7 @@ CopyHoldings (Board board, char *holdings, ChessSquare lowestPiece) if( (int)lowestPiece >= BlackPawn ) { holdingsColumn = 0; countsColumn = 1; - holdingsStartRow = BOARD_HEIGHT-1; + holdingsStartRow = handSize-1; direction = -1; } else { holdingsColumn = BOARD_WIDTH-1; @@ -2493,7 +2515,7 @@ CopyHoldings (Board board, char *holdings, ChessSquare lowestPiece) direction = 1; } - for(i=0; i= BlackPawn && old < BlackCannon) 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!) + } else if(old == WhitePawn || old == BlackPawn) // Pawn promotions (but not e.p.capture!) boards[moveNum][k][j] = PROMOTED(new); // use non-primordial representation of chosen piece } } else { @@ -6125,19 +6156,19 @@ Prelude (Board board) j = seed%4; seed /= 4; p = board[0][BOARD_LEFT+j]; board[0][BOARD_LEFT+j] = EmptySquare; k = PieceToNumber(p); board[k][BOARD_WIDTH-1] = p; board[k][BOARD_WIDTH-2]++; - board[BOARD_HEIGHT-1-k][0] = WHITE_TO_BLACK p; board[BOARD_HEIGHT-1-k][1]++; + board[handSize-1-k][0] = WHITE_TO_BLACK p; board[handSize-1-k][1]++; j = seed%3 + (seed%3 >= j); seed /= 3; p = board[0][BOARD_LEFT+j]; board[0][BOARD_LEFT+j] = EmptySquare; k = PieceToNumber(p); board[k][BOARD_WIDTH-1] = p; board[k][BOARD_WIDTH-2]++; - board[BOARD_HEIGHT-1-k][0] = WHITE_TO_BLACK p; board[BOARD_HEIGHT-1-k][1]++; + board[handSize-1-k][0] = WHITE_TO_BLACK p; board[handSize-1-k][1]++; j = seed%3; seed /= 3; p = board[0][BOARD_LEFT+j+5]; board[0][BOARD_LEFT+j+5] = EmptySquare; k = PieceToNumber(p); board[k][BOARD_WIDTH-1] = p; board[k][BOARD_WIDTH-2]++; - board[BOARD_HEIGHT-1-k][0] = WHITE_TO_BLACK p; board[BOARD_HEIGHT-1-k][1]++; + board[handSize-1-k][0] = WHITE_TO_BLACK p; board[handSize-1-k][1]++; j = seed%2 + (seed%2 >= j); seed /= 2; p = board[0][BOARD_LEFT+j+5]; board[0][BOARD_LEFT+j+5] = EmptySquare; k = PieceToNumber(p); board[k][BOARD_WIDTH-1] = p; board[k][BOARD_WIDTH-2]++; - board[BOARD_HEIGHT-1-k][0] = WHITE_TO_BLACK p; board[BOARD_HEIGHT-1-k][1]++; + board[handSize-1-k][0] = WHITE_TO_BLACK p; board[handSize-1-k][1]++; j = seed%4; seed /= 4; put(board, exoPieces[3], 0, j, ANY); j = seed%3; seed /= 3; put(board, exoPieces[2], 0, j, ANY); j = seed%2; seed /= 2; put(board, exoPieces[1], 0, j, ANY); @@ -6338,10 +6369,11 @@ InitPosition (int redraw) shuffleOpenings = 1; break; case VariantNoCastle: - pieces = FIDEArray; - nrCastlingRights = 0; /* !!?unconstrained back-rank shuffle */ shuffleOpenings = 1; + case VariantSuicide: + pieces = FIDEArray; + nrCastlingRights = 0; break; } @@ -6355,13 +6387,14 @@ InitPosition (int redraw) } if(appData.holdingsSize >= 0) { i = appData.holdingsSize; - if(i > gameInfo.boardHeight) i = gameInfo.boardHeight; +// if(i > gameInfo.boardHeight) i = gameInfo.boardHeight; gameInfo.holdingsSize = i; } if(gameInfo.holdingsSize) gameInfo.holdingsWidth = 2; if(BOARD_HEIGHT > BOARD_RANKS || BOARD_WIDTH > BOARD_FILES) DisplayFatalError(_("Recompile to support this BOARD_RANKS or BOARD_FILES!"), 0, 2); + if(!handSize) handSize = BOARD_HEIGHT; pawnRow = gameInfo.boardHeight - 7; /* seems to work in all common variants */ if(pawnRow < 1) pawnRow = 1; if(gameInfo.variant == VariantMakruk || gameInfo.variant == VariantASEAN || @@ -6439,8 +6472,8 @@ InitPosition (int redraw) if(gameInfo.variant == VariantGreat) { // promotion commoners initialPosition[PieceToNumber(WhiteMan)][BOARD_WIDTH-1] = WhiteMan; initialPosition[PieceToNumber(WhiteMan)][BOARD_WIDTH-2] = 9; - initialPosition[BOARD_HEIGHT-1-PieceToNumber(WhiteMan)][0] = BlackMan; - initialPosition[BOARD_HEIGHT-1-PieceToNumber(WhiteMan)][1] = 9; + initialPosition[handSize-1-PieceToNumber(WhiteMan)][0] = BlackMan; + initialPosition[handSize-1-PieceToNumber(WhiteMan)][1] = 9; } if( gameInfo.variant == VariantSChess ) { initialPosition[1][0] = BlackMarshall; @@ -6450,6 +6483,7 @@ InitPosition (int redraw) initialPosition[1][1] = initialPosition[2][1] = initialPosition[6][BOARD_WIDTH-2] = initialPosition[5][BOARD_WIDTH-2] = 1; } + initialPosition[CHECK_COUNT] = (gameInfo.variant == Variant3Check ? 0x303 : 0); if (appData.debugMode) { fprintf(debugFP, "shuffleOpenings = %d\n", shuffleOpenings); } @@ -6498,7 +6532,7 @@ SendBoard (ChessProgramState *cps, int moveNum) * 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 @@ -6706,12 +6740,13 @@ HasPromotionChoice (int fromX, int fromY, int toX, int toY, char *promoChoice, i piece = boards[currentMove][fromY][fromX]; if(gameInfo.variant == VariantChu) { - promotionZoneSize = BOARD_HEIGHT/3; + promotionZoneSize = (BOARD_HEIGHT - deadRanks)/3; if(legal[toY][toX] == 6) return FALSE; // no promotion if highlights deny it highestPromotingPiece = (PieceToChar(piece) == '+' || PieceToChar(CHUPROMOTED(piece)) != '+') ? WhitePawn : WhiteKing; } else if(gameInfo.variant == VariantShogi) { - promotionZoneSize = BOARD_HEIGHT/3 +(BOARD_HEIGHT == 8); + promotionZoneSize = (BOARD_HEIGHT- deadRanks)/3 +(BOARD_HEIGHT == 8); highestPromotingPiece = (int)WhiteAlfil; + if(PieceToChar(piece) != '+' || PieceToChar(CHUPROMOTED(piece)) == '+') highestPromotingPiece = piece; } else if(gameInfo.variant == VariantMakruk || gameInfo.variant == VariantGrand || gameInfo.variant == VariantChuChess) { promotionZoneSize = 3; } @@ -6729,9 +6764,9 @@ HasPromotionChoice (int fromX, int fromY, int toX, int toY, char *promoChoice, i if(fromY < promotionZoneSize && gameInfo.variant == VariantChuChess) return FALSE; highestPromotingPiece = WHITE_TO_BLACK highestPromotingPiece; } else { - if( toY < BOARD_HEIGHT - promotionZoneSize && - fromY < BOARD_HEIGHT - promotionZoneSize) return FALSE; - if(fromY >= BOARD_HEIGHT - promotionZoneSize && gameInfo.variant == VariantChuChess) + if( toY < BOARD_HEIGHT - deadRanks - promotionZoneSize && + fromY < BOARD_HEIGHT - deadRanks - promotionZoneSize) return FALSE; + if(fromY >= BOARD_HEIGHT - deadRanks - promotionZoneSize && gameInfo.variant == VariantChuChess) return FALSE; } @@ -6747,9 +6782,9 @@ HasPromotionChoice (int fromX, int fromY, int toX, int toY, char *promoChoice, i return FALSE; } } else { - if(toY == BOARD_HEIGHT-1 && piece == WhitePawn || - toY == BOARD_HEIGHT-1 && piece == WhiteQueen || - toY >= BOARD_HEIGHT-2 && piece == WhiteKnight) { + if(toY == BOARD_HEIGHT-deadRanks-1 && piece == WhitePawn || + toY == BOARD_HEIGHT-deadRanks-1 && piece == WhiteQueen || + toY >= BOARD_HEIGHT-deadRanks-2 && piece == WhiteKnight) { *promoChoice = '+'; return FALSE; } @@ -6816,12 +6851,45 @@ InPalace (int row, int column) int PieceForSquare (int x, int y) { - if (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) - return -1; - else + if (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) return -1; + if(x == BOARD_RGHT+1 && handOffsets & 1) y += handSize - BOARD_HEIGHT; + if(x == BOARD_LEFT-2 && !(handOffsets & 2)) y += handSize - BOARD_HEIGHT; return boards[currentMove][y][x]; } +ChessSquare +More (Board board, int col, int start, int end) +{ + int k; + for(k=start; k BOARD_HEIGHT && board) { + int k; + CopyBoard(compactedBoard, board); + if(handOffsets & 1) { + for(k=0; k1) capt++; if(capt) - for(x=0; x= BOARD_HEIGHT-1-zone || + piece == WhitePawn && y >= BOARD_HEIGHT-1-deadRanks-zone || piece == BlackLance && y <= zone || - piece == WhiteLance && y >= BOARD_HEIGHT-1-zone ); + piece == WhiteLance && y >= BOARD_HEIGHT-1-deadRanks-zone ); } void @@ -7541,6 +7609,7 @@ void ReportClick(char *action, int x, int y) 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) @@ -7564,13 +7633,30 @@ LeftClick (ClickType clickType, int xPix, int yPix) x = BOARD_WIDTH - 1 - x; } - if(appData.monoMouse && gameMode == EditPosition && fromX < 0 && clickType == Press && boards[currentMove][y][x] == EmptySquare) { + // map clicks in offsetted holdings back to true coords (or switch the offset) + if(x == BOARD_RGHT+1) { + if(handOffsets & 1) { + if(y == 0) { handOffsets &= ~1; DrawPosition(TRUE, boards[currentMove]); return; } + y += handSize - BOARD_HEIGHT; + } else if(y == BOARD_HEIGHT-1) { handOffsets |= 1; DrawPosition(TRUE, boards[currentMove]); return; } + } + if(x == BOARD_LEFT-2) { + if(!(handOffsets & 2)) { + if(y == 0) { handOffsets |= 2; DrawPosition(TRUE, boards[currentMove]); return; } + y += handSize - BOARD_HEIGHT; + } 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 || 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); @@ -7879,8 +7965,8 @@ LeftClick (ClickType clickType, int xPix, int yPix) if(x == BOARD_LEFT-2 && piece >= BlackPawn) { n = PieceToNumber(piece - (int)BlackPawn); if(n >= gameInfo.holdingsSize) { n = 0; piece = BlackPawn; } - boards[currentMove][BOARD_HEIGHT-1 - n][0] = piece; - boards[currentMove][BOARD_HEIGHT-1 - n][1]++; + boards[currentMove][handSize-1 - n][0] = piece; + boards[currentMove][handSize-1 - n][1]++; } else if(x == BOARD_RGHT+1 && piece < BlackPawn) { n = PieceToNumber(piece); @@ -8001,9 +8087,15 @@ RightClick (ClickType action, int x, int y, int *fromX, int *fromY) 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: @@ -8436,6 +8528,32 @@ Adjudicate (ChessProgramState *cps) 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 @@ -8544,7 +8662,7 @@ Adjudicate (ChessProgramState *cps) 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) { @@ -8939,7 +9057,7 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h GameEnds(cps->twoMachinesColor[0] == 'w' ? BlackWins : WhiteWins, buf1, GE_XBOARD); return; - } else if(!appData.fischerCastling) + } else if(!appData.fischerCastling && toX != BOARD_WIDTH>>1) /* [HGM] Kludge to handle engines that send FRC-style castling when they shouldn't (like TSCP-Gothic) */ switch(moveType) { @@ -9130,7 +9248,7 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h 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 && tryNr < 3) tryNr = 3; return; // [HGM] This return was missing, causing option features to be recognized as non-compliant commands! } @@ -9150,7 +9268,8 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h while(message[s] && message[s++] != ' '); if(BOARD_HEIGHT != h || BOARD_WIDTH != w + 4*(hand != 0) || gameInfo.holdingsSize != hand || dummy == 4 && gameInfo.variant != StringToVariant(varName) ) { // engine wants to change board format or variant - if(hand <= h) deadRanks = 0; else deadRanks = hand - h, h = hand; // adapt board to over-sized holdings +// if(hand <= h) deadRanks = 0; else deadRanks = hand - h, h = hand; // adapt board to over-sized holdings + if(hand > h) handSize = hand; else handSize = 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 @@ -9340,6 +9459,12 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h first.highlight = f; return; } + if(strncmp(message, "uciok", 5) == 0) { // response to "uci" probe + int nr = (cps == &second); + appData.isUCI[nr] = isUCI = 1; + ReplaceEngine(cps, nr); // 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 @@ -10029,7 +10154,7 @@ void ParseGameHistory (char *game) { ChessMove moveType; - int fromX, fromY, toX, toY, boardIndex; + int fromX, fromY, toX, toY, boardIndex, mask; char promoChar; char *p, *q; char buf[MSG_SIZ]; @@ -10190,15 +10315,18 @@ ParseGameHistory (char *game) strcat(moveList[boardIndex], "\n"); boardIndex++; ApplyMove(fromX, fromY, toX, toY, promoChar, boards[boardIndex]); + mask = (WhiteOnMove(boardIndex) ? 0xFF : 0xFF00); switch (MateTest(boards[boardIndex], PosFlags(boardIndex)) ) { case MT_NONE: case MT_STALEMATE: default: break; case MT_CHECK: - if(!IS_SHOGI(gameInfo.variant)) - strcat(parseList[boardIndex - 1], "+"); - break; + if(boards[boardIndex][CHECK_COUNT]) boards[boardIndex][CHECK_COUNT] -= mask & 0x101; + if(!boards[boardIndex][CHECK_COUNT] || boards[boardIndex][CHECK_COUNT] & mask) { + if(!IS_SHOGI(gameInfo.variant)) strcat(parseList[boardIndex - 1], "+"); + break; + } case MT_CHECKMATE: case MT_STAINMATE: strcat(parseList[boardIndex - 1], "#"); @@ -10212,16 +10340,16 @@ 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, 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_TO] & 255,lastRank = board[LAST_TO] >> 8; board[EP_STATUS] = EP_NONE; - board[EP_FILE] = board[EP_RANK] = 100; + board[EP_FILE] = board[EP_RANK] = 100, board[LAST_TO] = 0x4040; if (fromY == DROP_RANK) { /* must be first */ @@ -10253,6 +10381,13 @@ ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board) } 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 @@ -10270,6 +10405,7 @@ ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board) if(toX fromX) board[EP_STATUS] = toX; + board[LAST_TO] = toX + 256*toY; } } else if( pawn == BlackPawn ) { @@ -10283,6 +10419,7 @@ ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board) if(toX fromX) board[EP_STATUS] = toX; + board[LAST_TO] = toX + 256*toY; } } @@ -10377,18 +10514,16 @@ ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board) 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 @@ -10444,18 +10579,16 @@ ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board) 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 @@ -10499,10 +10632,10 @@ ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board) p -= (int)BlackPawn; p = PieceToNumber((ChessSquare)p); if(p >= gameInfo.holdingsSize) p = 0; - if(--board[BOARD_HEIGHT-1-p][1] <= 0) - board[BOARD_HEIGHT-1-p][0] = EmptySquare; - if((int)board[BOARD_HEIGHT-1-p][1] < 0) - board[BOARD_HEIGHT-1-p][1] = 0; + if(--board[handSize-1-p][1] <= 0) + board[handSize-1-p][0] = EmptySquare; + if((int)board[handSize-1-p][1] < 0) + board[handSize-1-p][1] = 0; } } if (captured != EmptySquare && gameInfo.holdingsSize > 0 @@ -10532,8 +10665,8 @@ ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board) } p = PieceToNumber((ChessSquare)p); if(p >= gameInfo.holdingsSize) { p = 0; captured = WhitePawn; } - board[BOARD_HEIGHT-1-p][1]++; - board[BOARD_HEIGHT-1-p][0] = WHITE_TO_BLACK captured; + board[handSize-1-p][1]++; + board[handSize-1-p][0] = WHITE_TO_BLACK captured; } } } else if (gameInfo.variant == VariantAtomic) { @@ -10573,8 +10706,8 @@ ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board) if(!--board[k][BOARD_WIDTH-2]) board[k][BOARD_WIDTH-1] = EmptySquare; } else { - if(!--board[BOARD_HEIGHT-1-k][1]) - board[BOARD_HEIGHT-1-k][0] = EmptySquare; + if(!--board[handSize-1-k][1]) + board[handSize-1-k][0] = EmptySquare; } } } @@ -10583,7 +10716,7 @@ ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board) void MakeMove (int fromX, int fromY, int toX, int toY, int promoChar) { - int x = toX, y = toY; + int x = toX, y = toY, mask; char *s = parseList[forwardMostMove]; ChessSquare p = boards[forwardMostMove][toY][toX]; // forwardMostMove++; // [HGM] bare: moved downstream @@ -10679,15 +10812,18 @@ MakeMove (int fromX, int fromY, int toX, int toY, int promoChar) } CoordsToComputerAlgebraic(fromY, fromX, toY, toX, promoChar, moveList[forwardMostMove - 1]); + mask = (WhiteOnMove(forwardMostMove) ? 0xFF : 0xFF00); switch (MateTest(boards[forwardMostMove], PosFlags(forwardMostMove)) ) { case MT_NONE: case MT_STALEMATE: default: break; case MT_CHECK: - if(!IS_SHOGI(gameInfo.variant)) - strcat(parseList[forwardMostMove - 1], "+"); - break; + if(boards[forwardMostMove][CHECK_COUNT]) boards[forwardMostMove][CHECK_COUNT] -= mask & 0x101; + if(!boards[forwardMostMove][CHECK_COUNT] || boards[forwardMostMove][CHECK_COUNT] & mask) { + if(!IS_SHOGI(gameInfo.variant)) strcat(parseList[forwardMostMove - 1], "+"); + break; + } case MT_CHECKMATE: case MT_STAINMATE: strcat(parseList[forwardMostMove - 1], "#"); @@ -10922,31 +11058,47 @@ InitChessProgram (ChessProgramState *cps, int setup) } -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; inrOptions; 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 @@ -10994,7 +11146,7 @@ StartChessProgram (ChessProgramState *cps) 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); } @@ -11248,6 +11400,34 @@ NamesToList (char *names, char **engineList, char **engineMnemonic, char *group) 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 + 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); + 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 + if(*engineListFile) SaveEngineList(); +} + // 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; @@ -11295,6 +11475,7 @@ GetEngineLine (char *s, int n) 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); @@ -11340,6 +11521,7 @@ RecentEngineEvent (int nr) 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]); } } @@ -12041,6 +12223,7 @@ Reset (int redraw, int init) } pieceDefs = FALSE; // [HGM] gen: reset engine-defined piece moves deadRanks = 0; // assume entire board is used + handSize = 0; for(i=0; i= BOARD_RGHT) { if(x == BOARD_LEFT-2) { - if(y < BOARD_HEIGHT-1-gameInfo.holdingsSize) break; + if(y < handSize-1-gameInfo.holdingsSize) break; boards[0][y][1] = 0; } else if(x == BOARD_RGHT+1) { @@ -15650,8 +15844,8 @@ EditPositionMenuEvent (ChessSquare selection, int x, int y) if(x == BOARD_LEFT-2 && selection >= BlackPawn) { n = PieceToNumber(selection - BlackPawn); if(n >= gameInfo.holdingsSize) { n = 0; selection = BlackPawn; } - boards[0][BOARD_HEIGHT-1-n][0] = selection; - boards[0][BOARD_HEIGHT-1-n][1]++; + boards[0][handSize-1-n][0] = selection; + boards[0][handSize-1-n][1]++; } else if(x == BOARD_RGHT+1 && selection < BlackPawn) { n = PieceToNumber(selection); @@ -17166,8 +17360,10 @@ StringFeature (char **p, char *name, char **loc, ChessProgramState *cps) 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); @@ -17246,6 +17442,8 @@ ParseOption (Option *opt, ChessProgramState *cps) opt->type = SaveButton; } else return FALSE; *p = 0; // terminate option name + *(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); // now look if the command-line options define a setting for this engine option. if(cps->optionSettings && cps->optionSettings[0]) p = strstr(cps->optionSettings, opt->name); else p = NULL; @@ -17258,6 +17456,8 @@ ParseOption (Option *opt, ChessProgramState *cps) if(!strcmp(((char**)opt->textValue)[n], q+1)) opt->value = n; break; case TextBox: + case FileName: + case PathName: safeStrCpy(opt->textValue, q+1, MSG_SIZ - (opt->textValue - opt->name)); break; case Spin: @@ -17278,7 +17478,7 @@ FeatureDone (ChessProgramState *cps, int val) DelayedEventCallback cb = GetDelayedEvent(); if ((cb == InitBackEnd3 && cps == &first) || (cb == SettingsMenuIfReady && cps == &second) || - (cb == LoadEngine) || + (cb == LoadEngine) || (cb == StartSecond) || (cb == TwoMachinesEventIfReady)) { CancelDelayedEvent(); ScheduleDelayedEvent(cb, val ? 1 : 3600000); @@ -17349,6 +17549,7 @@ ParseFeatures (char *args, ChessProgramState *cps) 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 @@ -18267,9 +18468,9 @@ PositionToFEN (int move, char *overrideCastling, int moveCounts) *p++ = PieceToChar(piece); } for(i=0; i fromY + 1) && + boards[move][toY][toX] == (whiteToPlay ? BlackPawn : WhitePawn) ) { /* 2-square pawn move just happened */ - *p++ = toX + AAA; - *p++ = whiteToPlay ? '6'+BOARD_HEIGHT-8 : '3'; + *p++ = (3*toX + 5*fromX + 4)/8 + AAA; + *p++ = (3*toY + 5*fromY + 4)/8 + ONE; + if(gameInfo.variant == VariantBerolina) { + *p++ = toX + AAA; + *p++ = toY + ONE; + } } else { *p++ = '-'; } @@ -18402,6 +18605,12 @@ PositionToFEN (int move, char *overrideCastling, int moveCounts) } } + i = boards[move][CHECK_COUNT]; + if(i) { + sprintf(p, "%d+%d ", i&255, i>>8); + while(*p) p++; + } + if(moveCounts) { int i = 0, j=move; @@ -18515,7 +18724,7 @@ ParseFEN (Board board, int *blackPlaysFirst, char *fen, Boolean autoSize) /* [HGM] by default clear Crazyhouse holdings, if present */ if(gameInfo.holdingsWidth) { - for(i=0; i= gameInfo.holdingsSize ) return FALSE; - board[BOARD_HEIGHT-1-i][0] = piece; /* black holdings */ - board[BOARD_HEIGHT-1-i][1]++; /* black counts */ + board[handSize-1-i][0] = piece; /* black holdings */ + board[handSize-1-i][1]++; /* black counts */ bcnt++; } else { i = (int)piece - (int)WhitePawn; @@ -18566,9 +18775,9 @@ ParseFEN (Board board, int *blackPlaysFirst, char *fen, Boolean autoSize) if(board[BOARD_HEIGHT-1][j] == ClearBoard) { if(!bcnt) return FALSE; if(n >= bcnt) n = rand() % bcnt; // use same randomization for black and white if possible - for(k=0, m=n; k= BOARD_RGHT) return TRUE; - if(*p >= '0' && *p <='9') p++; - board[EP_STATUS] = c; + int d, r, c = *p - AAA; + + if(c >= BOARD_LEFT && c < BOARD_RGHT) { + p++; + board[EP_STATUS] = board[EP_FILE] = c; r = 0; + if(*p >= '0' && *p <='9') r = board[EP_RANK] = *p++ - ONE; + d = (r < BOARD_HEIGHT << 1 ? 1 : -1); // assume double-push (P next to e.p. square nearer center) + if(board[r+d][c] == EmptySquare) d *= 2; // but if no Pawn there, triple push + board[LAST_TO] = 256*(r + d) + c; + c = *p++ - AAA; + if(c >= BOARD_LEFT && c < BOARD_RGHT) { // mover explicitly mentioned + if(*p >= '0' && *p <='9') r = board[EP_RANK] = *p++ - ONE; + board[LAST_TO] = 256*r + c; + if(!(board[EP_RANK]-r & 1)) board[EP_RANK] |= 128; + } + } } } + while(*p == ' ') p++; + + board[CHECK_COUNT] = 0; // [HGM] 3check: check-count field + if(sscanf(p, "%d+%d", &i, &j) == 2) { + board[CHECK_COUNT] = i + 256*j; + while(*p && *p != ' ') p++; + } - if(sscanf(p, "%d", &i) == 1) { + c = sscanf(p, "%d%*d +%d+%d", &i, &j, &k); + if(c > 0) { FENrulePlies = i; /* 50-move ply counter */ /* (The move number is still ignored) */ + if(c == 3 && !board[CHECK_COUNT]) board[CHECK_COUNT] = (3 - j) + 256*(3 - k); // SCIDB-style check count } return TRUE;