X-Git-Url: http://winboard.nl/cgi-bin?a=blobdiff_plain;f=backend.c;h=3c3ff374d9488a0cdefdbf4edae2ce1d107287fc;hb=bbcce91fb6aced3fea5c5c1415d0edfecc33927c;hp=891d3e08cd57fff537fda329c6915451299bed88;hpb=f795bf86b3011a5e0aa88493ce106ca93e664089;p=xboard.git diff --git a/backend.c b/backend.c index 891d3e0..3c3ff37 100644 --- a/backend.c +++ b/backend.c @@ -157,7 +157,7 @@ int LoadGameFromFile P((char *filename, int n, char *title, int useList)); int LoadPositionFromFile P((char *filename, int n, char *title)); int SavePositionToFile P((char *filename)); void ApplyMove P((int fromX, int fromY, int toX, int toY, int promoChar, - Board board)); + Board board, char *castle, char *ep)); void MakeMove P((int fromX, int fromY, int toX, int toY, int promoChar)); void ShowMove P((int fromX, int fromY, int toX, int toY)); int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY, @@ -749,6 +749,8 @@ InitBackEnd1() first.useFEN960 = FALSE; second.useFEN960 = FALSE; first.useOOCastle = TRUE; second.useOOCastle = TRUE; /* End of new features added by Tord. */ + first.fenOverride = appData.fenOverride1; + second.fenOverride = appData.fenOverride2; /* [HGM] time odds: set factor for each machine */ first.timeOdds = appData.firstTimeOdds; @@ -2274,6 +2276,12 @@ read_from_ics(isr, closure, data, count, error) nrAlph += (parse[i] >= 'A' && parse[i] <= 'Z'); } if(nrAlph < 9*nrDigit) { // if more than 10% digit we assume search info + int depth=0; float score; + if(sscanf(parse, "!!! %f/%d", &score, &depth) == 2 && depth>0) { + // [HGM] kibitz: save kibitzed opponent info for PGN and eval graph + pvInfoList[forwardMostMove-1].depth = depth; + pvInfoList[forwardMostMove-1].score = 100*score; + } OutputKibitz(suppressKibitz, parse); } else { char tmp[MSG_SIZ]; @@ -3716,7 +3724,7 @@ ParseBoard12(string) else strcpy(buf, str); // might be castling if((prom = strstr(move_str, "=")) && !strstr(buf, "=")) strcat(buf, prom); // long move lacks promo specification! - if(!appData.testLegality) { + if(!appData.testLegality && move_str[1] != '@') { // drops never ambiguous (parser chokes on long form!) if(appData.debugMode) fprintf(debugFP, "replaced ICS move '%s' by '%s'\n", move_str, buf); strcpy(move_str, buf); @@ -3742,6 +3750,7 @@ ParseBoard12(string) strcat(parseList[moveNum - 1], "+"); break; case MT_CHECKMATE: + case MT_STAINMATE: // [HGM] xq: for notation stalemate that wins counts as checkmate strcat(parseList[moveNum - 1], "#"); break; } @@ -4753,7 +4762,7 @@ SendBoard(cps, moveNum) char message[MSG_SIZ]; if (cps->useSetboard) { - char* fen = PositionToFEN(moveNum, cps->useFEN960); + char* fen = PositionToFEN(moveNum, cps->fenOverride); sprintf(message, "setboard %s\n", fen); SendToProgram(message, cps); free(fen); @@ -5208,9 +5217,9 @@ if(appData.debugMode) fprintf(debugFP, "moveType 1 = %d, promochar = %x\n", move * If they don't match, display an error message. */ int saveAnimate; - Board testBoard; + Board testBoard; char testRights[BOARD_SIZE]; char testStatus; CopyBoard(testBoard, boards[currentMove]); - ApplyMove(fromX, fromY, toX, toY, promoChar, testBoard); + ApplyMove(fromX, fromY, toX, toY, promoChar, testBoard, testRights, &testStatus); if (CompareBoards(testBoard, boards[currentMove+1])) { ForwardInner(currentMove+1); @@ -5254,19 +5263,6 @@ if(appData.debugMode) fprintf(debugFP, "moveType 1 = %d, promochar = %x\n", move MakeMove(fromX, fromY, toX, toY, promoChar); /*updates forwardMostMove*/ - if((gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat) - && promoChar != NULLCHAR && gameInfo.holdingsSize) { - // [HGM] superchess: take promotion piece out of holdings - int k = PieceToNumber(CharToPiece(ToUpper(promoChar))); - if(WhiteOnMove(forwardMostMove-1)) { - if(!--boards[forwardMostMove][k][BOARD_WIDTH-2]) - boards[forwardMostMove][k][BOARD_WIDTH-1] = EmptySquare; - } else { - if(!--boards[forwardMostMove][BOARD_HEIGHT-1-k][1]) - boards[forwardMostMove][BOARD_HEIGHT-1-k][0] = EmptySquare; - } - } - if (gameMode == BeginningOfGame) { if (appData.noChessProgram) { gameMode = EditGame; @@ -5320,6 +5316,7 @@ if(appData.debugMode) fprintf(debugFP, "moveType 2 = %d, promochar = %x\n", move case MT_CHECK: break; case MT_CHECKMATE: + case MT_STAINMATE: if (WhiteOnMove(currentMove)) { GameEnds(BlackWins, "Black mates", GE_PLAYER); } else { @@ -5604,6 +5601,7 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h machineMove, fromX+AAA, fromY+ONE, toX+AAA, toY+ONE, 0); GameEnds(machineWhite ? BlackWins : WhiteWins, buf1, GE_XBOARD); + return; } else if(gameInfo.variant != VariantFischeRandom && gameInfo.variant != VariantCapaRandom) /* [HGM] Kludge to handle engines that send FRC-style castling when they shouldn't (like TSCP-Gothic) */ @@ -5637,9 +5635,9 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h if(appData.autoKibitz && !appData.icsEngineAnalyze ) { /* [HGM] kibitz: send most-recent PV info to ICS */ char buf[3*MSG_SIZ]; - sprintf(buf, "kibitz %d/%+.2f (%.2f sec, %.0f nodes, %1.0f knps) PV = %s\n", - programStats.depth, + sprintf(buf, "kibitz !!! %+.2f/%d (%.2f sec, %.0f nodes, %1.0f knps) PV=%s\n", programStats.score / 100., + programStats.depth, programStats.time / 100., u64ToDouble(programStats.nodes), u64ToDouble(programStats.nodes) / (10*abs(programStats.time) + 1.), @@ -5697,41 +5695,6 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h int k, count = 0, epFile = epStatus[forwardMostMove]; static int bare = 1; if(gameInfo.holdingsSize == 0 || gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat) { - if(appData.testLegality) - // don't wait for engine to announce game end if we can judge ourselves - switch (MateTest(boards[forwardMostMove], - PosFlags(forwardMostMove), epFile, - castlingRights[forwardMostMove]) ) { - case MT_NONE: - case MT_CHECK: - default: - break; - case MT_STALEMATE: - epStatus[forwardMostMove] = EP_STALEMATE; - if(appData.checkMates) { - SendMoveToProgram(forwardMostMove-1, cps->other); /* make sure opponent gets to see move */ - ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/ - if(gameInfo.variant == VariantLosers || gameInfo.variant == VariantSuicide - || gameInfo.variant == VariantGiveaway) // [HGM] losers: - GameEnds( WhiteOnMove(forwardMostMove) ? WhiteWins : BlackWins, // stalemated side wins! - "Xboard adjudication: Stalemate", GE_XBOARD ); - else - GameEnds( GameIsDrawn, "Xboard adjudication: Stalemate", GE_XBOARD ); - return; - } - break; - case MT_CHECKMATE: - epStatus[forwardMostMove] = EP_CHECKMATE; - if(appData.checkMates) { - SendMoveToProgram(forwardMostMove-1, cps->other); /* make sure opponent gets to see move */ - ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/ - GameEnds( WhiteOnMove(forwardMostMove) != (gameInfo.variant == VariantLosers) // [HGM] losers: - ? BlackWins : WhiteWins, // reverse the result ( A!=1 is !A for a boolean) - "Xboard adjudication: Checkmate", GE_XBOARD ); - return; - } - break; - } if( appData.testLegality ) { /* [HGM] Some more adjudications for obstinate engines */ @@ -5739,8 +5702,10 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h NrWQ=0, NrBQ=0, NrW=0, NrK=0, bishopsColor = 0, NrPieces=0, NrPawns=0, PawnAdvance=0, i, j; static int moveCount = 6; + ChessMove result; + char *reason = NULL; - /* First absolutely insufficient mating material. Count what is on board. */ + /* Count what is on board. */ for(i=0; iother); /* make sure opponent gets to see move */ + SendMoveToProgram(forwardMostMove-1, cps->other); // make sure opponent gets to see move ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/ GameEnds( WhiteOnMove(forwardMostMove) ? WhiteWins : BlackWins, "Xboard adjudication: Bare king", GE_XBOARD ); @@ -5812,7 +5778,7 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h } else if( gameInfo.variant == VariantShatranj && --bare < 0) { /* bare King */ - epStatus[forwardMostMove] = EP_CHECKMATE; // make claimable as win for stm + epStatus[forwardMostMove] = EP_WINS; // make claimable as win for stm if(appData.checkMates) { /* but only adjudicate if adjudication enabled */ SendMoveToProgram(forwardMostMove-1, cps->other); // make sure opponent gets move @@ -5825,6 +5791,52 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h } else bare = 1; + // don't wait for engine to announce game end if we can judge ourselves + switch (MateTest(boards[forwardMostMove], PosFlags(forwardMostMove), epFile, + castlingRights[forwardMostMove]) ) { + case MT_NONE: + case MT_CHECK: + default: + break; + case MT_STALEMATE: + case MT_STAINMATE: + reason = "Xboard adjudication: Stalemate"; + if(epStatus[forwardMostMove] != EP_CHECKMATE) { // [HGM] don't touch win through baring or K-capt + epStatus[forwardMostMove] = EP_STALEMATE; // default result for stalemate is draw + if(gameInfo.variant == VariantLosers || gameInfo.variant == VariantGiveaway) // [HGM] losers: + epStatus[forwardMostMove] = EP_WINS; // in these variants stalemated is always a win + else if(gameInfo.variant == VariantSuicide) // in suicide it depends + epStatus[forwardMostMove] = NrW == NrPieces-NrW ? EP_STALEMATE : + ((NrW < NrPieces-NrW) != WhiteOnMove(forwardMostMove) ? + EP_CHECKMATE : EP_WINS); + else if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantXiangqi) + epStatus[forwardMostMove] = EP_CHECKMATE; // and in these variants being stalemated loses + } + break; + case MT_CHECKMATE: + reason = "Xboard adjudication: Checkmate"; + epStatus[forwardMostMove] = (gameInfo.variant == VariantLosers ? EP_WINS : EP_CHECKMATE); + break; + } + + switch(i = epStatus[forwardMostMove]) { + case EP_STALEMATE: + result = GameIsDrawn; break; + case EP_CHECKMATE: + result = WhiteOnMove(forwardMostMove) ? BlackWins : WhiteWins; break; + case EP_WINS: + result = WhiteOnMove(forwardMostMove) ? WhiteWins : BlackWins; break; + default: + result = (ChessMove) 0; + } + if(appData.checkMates && result) { // [HGM] mates: adjudicate finished games if requested + SendMoveToProgram(forwardMostMove-1, cps->other); /* make sure opponent gets to see move */ + ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/ + GameEnds( result, reason, GE_XBOARD ); + return; + } + + /* Next absolutely insufficient mating material. */ if( NrPieces == 2 || gameInfo.variant != VariantXiangqi && gameInfo.variant != VariantShatranj && // [HGM] baring will remain possible (NrPieces == 3 && NrWN+NrBN+NrWB+NrBB == 1 || @@ -5948,7 +5960,7 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h if(!ourPerpetual && !hisPerpetual) { // no perpetual check, test for chase hisPerpetual = PerpetualChase(k, forwardMostMove); ourPerpetual = PerpetualChase(k+1, forwardMostMove); - if(ourPerpetual && !hisPerpetual) { // we are actively checking him: forfeit + if(ourPerpetual && !hisPerpetual) { // we are actively chasing him: forfeit GameEnds( WhiteOnMove(forwardMostMove) ? WhiteWins : BlackWins, "Xboard adjudication: perpetual chasing", GE_XBOARD ); return; @@ -6969,11 +6981,13 @@ ParseGameHistory(game) EP_UNKNOWN, fromY, fromX, toY, toX, promoChar, parseList[boardIndex]); CopyBoard(boards[boardIndex + 1], boards[boardIndex]); + {int i; for(i=0; i 0) + /* we can always do that 'in place', now pointers to these rights are passed to ApplyMove */ { int i; if(gameInfo.variant == VariantBerolina) berolina = EP_BEROLIN_A; - oldEP = epStatus[p-1]; - epStatus[p] = EP_NONE; + oldEP = *ep; + *ep = EP_NONE; if( board[toY][toX] != EmptySquare ) - epStatus[p] = EP_CAPTURE; + *ep = EP_CAPTURE; if( board[fromY][fromX] == WhitePawn ) { if(fromY != toY) // [HGM] Xiangqi sideway Pawn moves should not count as 50-move breakers - epStatus[p] = EP_PAWN_MOVE; + *ep = EP_PAWN_MOVE; if( toY-fromY==2) { if(toX>BOARD_LEFT && board[toY][toX-1] == BlackPawn && gameInfo.variant != VariantBerolina || toX < fromX) - epStatus[p] = toX | berolina; + *ep = toX | berolina; if(toX fromX) - epStatus[p] = toX; + *ep = toX; } } else if( board[fromY][fromX] == BlackPawn ) { if(fromY != toY) // [HGM] Xiangqi sideway Pawn moves should not count as 50-move breakers - epStatus[p] = EP_PAWN_MOVE; + *ep = EP_PAWN_MOVE; if( toY-fromY== -2) { if(toX>BOARD_LEFT && board[toY][toX-1] == WhitePawn && gameInfo.variant != VariantBerolina || toX < fromX) - epStatus[p] = toX | berolina; + *ep = toX | berolina; if(toX fromX) - epStatus[p] = toX; + *ep = toX; } } for(i=0; i 0 || startedFromSetupPosition) { - char *fen = PositionToFEN(backwardMostMove, 1); + char *fen = PositionToFEN(backwardMostMove, NULL); fprintf(f, "[FEN \"%s\"]\n[SetUp \"1\"]\n", fen); fprintf(f, "\n{--------------\n"); PrintPosition(f, backwardMostMove); @@ -9547,7 +9581,16 @@ SaveGamePGN(f) linelen += numlen; /* Get move */ - movelen = strlen(parseList[i]); /* [HGM] pgn: line-break point before move */ + strcpy(move_buffer, parseList[i]); // [HGM] pgn: print move via buffer, so it can be edited + movelen = strlen(move_buffer); /* [HGM] pgn: line-break point before move */ + if( i >= 0 && appData.saveExtendedInfoInPGN && pvInfoList[i].depth > 0 ) { + int p = movelen - 1; + if(move_buffer[p] == ' ') p--; + if(move_buffer[p] == ')') { // [HGM] pgn: strip off ICS time if we have extended info + while(p && move_buffer[--p] != '('); + if(p && move_buffer[p-1] == ' ') move_buffer[movelen=p-1] = 0; + } + } /* Print move */ blank = linelen > 0 && movelen > 0; @@ -9560,7 +9603,7 @@ SaveGamePGN(f) fprintf(f, " "); linelen++; } - fprintf(f, parseList[i]); + fprintf(f, move_buffer); linelen += movelen; /* [AS] Add PV info if present */ @@ -9568,14 +9611,14 @@ SaveGamePGN(f) /* [HGM] add time */ char buf[MSG_SIZ]; int seconds = 0; -#if 0 +#if 1 if(i >= backwardMostMove) { if(WhiteOnMove(i)) seconds = timeRemaining[0][i] - timeRemaining[0][i+1] - + GetTimeQuota(i/2) / WhitePlayer()->timeOdds; + + GetTimeQuota(i/2) / (1000*WhitePlayer()->timeOdds); else seconds = timeRemaining[1][i] - timeRemaining[1][i+1] - + GetTimeQuota(i/2) / WhitePlayer()->other->timeOdds; + + GetTimeQuota(i/2) / (1000*WhitePlayer()->other->timeOdds); } seconds = (seconds+50)/100; // deci-seconds, rounded to nearest #else @@ -9757,7 +9800,7 @@ SavePosition(f, dummy, dummy2) PrintPosition(f, currentMove); fprintf(f, "--------------]\n"); } else { - fen = PositionToFEN(currentMove, 1); + fen = PositionToFEN(currentMove, NULL); fprintf(f, "%s\n", fen); free(fen); } @@ -12716,16 +12759,16 @@ DisplayComment(moveNumber, text) WhiteOnMove(moveNumber) ? " " : ".. ", parseList[moveNumber]); } + // [HGM] PV info: display PV info together with (or as) comment + if(moveNumber >= 0 && (depth = pvInfoList[moveNumber].depth) > 0) { + if(text == NULL) text = ""; + score = pvInfoList[moveNumber].score; + sprintf(buf, "%s%.2f/%d %d\n%s", score>0 ? "+" : "", score/100., + depth, (pvInfoList[moveNumber].time+50)/100, text); + text = buf; + } } else title[0] = 0; - // [HGM] PV info: display PV info together with (or as) comment - if(moveNumber >= 0 && (depth = pvInfoList[moveNumber].depth) > 0) { - if(text == NULL) text = ""; - score = pvInfoList[moveNumber].score; - sprintf(buf, "%s%.2f/%d %d\n%s", score>0 ? "+" : "", score/100., - depth, (pvInfoList[moveNumber].time+50)/100, text); - CommentPopUp(title, buf); - } else if (text != NULL) CommentPopUp(title, text); } @@ -13293,9 +13336,9 @@ PGNDate() char * -PositionToFEN(move, useFEN960) +PositionToFEN(move, overrideCastling) int move; - int useFEN960; + char *overrideCastling; { int i, j, fromX, fromY, toX, toY; int whiteToPlay; @@ -13370,6 +13413,9 @@ PositionToFEN(move, useFEN960) *p++ = whiteToPlay ? 'w' : 'b'; *p++ = ' '; + if(q = overrideCastling) { // [HGM] FRC: override castling & e.p fields for non-compliant engines + while(*p++ = *q++); if(q != overrideCastling+1) p[-1] = ' '; + } else { if(nrCastlingRights) { q = p; if(gameInfo.variant == VariantFischeRandom || gameInfo.variant == VariantCapaRandom) { @@ -13427,6 +13473,7 @@ PositionToFEN(move, useFEN960) } *p++ = ' '; } + } /* [HGM] find reversible plies */ { int i = 0, j=move;