int LoadPositionFromFile P((char *filename, int n, char *title));\r
int SavePositionToFile P((char *filename));\r
void ApplyMove P((int fromX, int fromY, int toX, int toY, int promoChar,\r
- Board board));\r
+ Board board, char *castle, char *ep));\r
void MakeMove P((int fromX, int fromY, int toX, int toY, int promoChar));\r
void ShowMove P((int fromX, int fromY, int toX, int toY));\r
int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,\r
}\r
if(nrAlph < 9*nrDigit) { // if more than 10% digit we assume search info\r
int depth=0; float score;\r
- if(sscanf(parse, "%f/%d", &score, &depth) == 2 && depth>0) {\r
+ if(sscanf(parse, "!!! %f/%d", &score, &depth) == 2 && depth>0) {\r
// [HGM] kibitz: save kibitzed opponent info for PGN and eval graph\r
pvInfoList[forwardMostMove-1].depth = depth;\r
pvInfoList[forwardMostMove-1].score = 100*score;\r
else strcpy(buf, str); // might be castling\r
if((prom = strstr(move_str, "=")) && !strstr(buf, "=")) \r
strcat(buf, prom); // long move lacks promo specification!\r
- if(!appData.testLegality) {\r
+ if(!appData.testLegality && move_str[1] != '@') { // drops never ambiguous (parser chokes on long form!)\r
if(appData.debugMode) \r
fprintf(debugFP, "replaced ICS move '%s' by '%s'\n", move_str, buf);\r
strcpy(move_str, buf);\r
strcat(parseList[moveNum - 1], "+");\r
break;\r
case MT_CHECKMATE:\r
+ case MT_STAINMATE: // [HGM] xq: for notation stalemate that wins counts as checkmate\r
strcat(parseList[moveNum - 1], "#");\r
break;\r
}\r
* If they don't match, display an error message.\r
*/\r
int saveAnimate;\r
- Board testBoard;\r
+ Board testBoard; char testRights[BOARD_SIZE]; char testStatus;\r
CopyBoard(testBoard, boards[currentMove]);\r
- ApplyMove(fromX, fromY, toX, toY, promoChar, testBoard);\r
+ ApplyMove(fromX, fromY, toX, toY, promoChar, testBoard, testRights, &testStatus);\r
\r
if (CompareBoards(testBoard, boards[currentMove+1])) {\r
ForwardInner(currentMove+1);\r
\r
MakeMove(fromX, fromY, toX, toY, promoChar); /*updates forwardMostMove*/\r
\r
- if((gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat) \r
- && promoChar != NULLCHAR && gameInfo.holdingsSize) { \r
- // [HGM] superchess: take promotion piece out of holdings\r
- int k = PieceToNumber(CharToPiece(ToUpper(promoChar)));\r
- if(WhiteOnMove(forwardMostMove-1)) {\r
- if(!--boards[forwardMostMove][k][BOARD_WIDTH-2])\r
- boards[forwardMostMove][k][BOARD_WIDTH-1] = EmptySquare;\r
- } else {\r
- if(!--boards[forwardMostMove][BOARD_HEIGHT-1-k][1])\r
- boards[forwardMostMove][BOARD_HEIGHT-1-k][0] = EmptySquare;\r
- }\r
- }\r
-\r
if (gameMode == BeginningOfGame) {\r
if (appData.noChessProgram) {\r
gameMode = EditGame;\r
case MT_CHECK:\r
break;\r
case MT_CHECKMATE:\r
+ case MT_STAINMATE:\r
if (WhiteOnMove(currentMove)) {\r
GameEnds(BlackWins, "Black mates", GE_PLAYER);\r
} else {\r
if(appData.autoKibitz && !appData.icsEngineAnalyze ) { /* [HGM] kibitz: send most-recent PV info to ICS */\r
char buf[3*MSG_SIZ];\r
\r
- sprintf(buf, "kibitz %+.2f/%d (%.2f sec, %.0f nodes, %1.0f knps) PV=%s\n",\r
+ sprintf(buf, "kibitz !!! %+.2f/%d (%.2f sec, %.0f nodes, %1.0f knps) PV=%s\n",\r
programStats.score / 100.,\r
programStats.depth,\r
programStats.time / 100.,\r
NrWQ=0, NrBQ=0, NrW=0, NrK=0, bishopsColor = 0,\r
NrPieces=0, NrPawns=0, PawnAdvance=0, i, j;\r
static int moveCount = 6;\r
+ ChessMove result;\r
+ char *reason = NULL;\r
\r
/* Count what is on board. */\r
for(i=0; i<BOARD_HEIGHT; i++) for(j=BOARD_LEFT; j<BOARD_RGHT; j++)\r
/* Bare King in Shatranj (loses) or Losers (wins) */\r
if( NrW == 1 || NrPieces - NrW == 1) {\r
if( gameInfo.variant == VariantLosers) { // [HGM] losers: bare King wins (stm must have it first)\r
- epStatus[forwardMostMove] = EP_STALEMATE; // kludge to make position claimable as win\r
+ epStatus[forwardMostMove] = EP_WINS; // mark as win, so it becomes claimable\r
if(appData.checkMates) {\r
- SendMoveToProgram(forwardMostMove-1, cps->other); /* make sure opponent gets to see move */\r
+ SendMoveToProgram(forwardMostMove-1, cps->other); // make sure opponent gets to see move\r
ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/\r
GameEnds( WhiteOnMove(forwardMostMove) ? WhiteWins : BlackWins, \r
"Xboard adjudication: Bare king", GE_XBOARD );\r
} else\r
if( gameInfo.variant == VariantShatranj && --bare < 0)\r
{ /* bare King */\r
- epStatus[forwardMostMove] = EP_CHECKMATE; // make claimable as win for stm\r
+ epStatus[forwardMostMove] = EP_WINS; // make claimable as win for stm\r
if(appData.checkMates) {\r
/* but only adjudicate if adjudication enabled */\r
SendMoveToProgram(forwardMostMove-1, cps->other); // make sure opponent gets move\r
\r
\r
// don't wait for engine to announce game end if we can judge ourselves\r
- switch (MateTest(boards[forwardMostMove],\r
- PosFlags(forwardMostMove), epFile,\r
+ switch (MateTest(boards[forwardMostMove], PosFlags(forwardMostMove), epFile,\r
castlingRights[forwardMostMove]) ) {\r
case MT_NONE:\r
case MT_CHECK:\r
default:\r
break;\r
case MT_STALEMATE:\r
- if(epStatus[forwardMostMove] != EP_CHECKMATE) // [HGM] spare win through baring or K-capt\r
- epStatus[forwardMostMove] = EP_STALEMATE;\r
- if(appData.checkMates) {\r
- SendMoveToProgram(forwardMostMove-1, cps->other); /* make sure opponent gets to see move */\r
- ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/\r
- if(gameInfo.variant == VariantLosers || gameInfo.variant == VariantSuicide\r
- || gameInfo.variant == VariantGiveaway) // [HGM] losers:\r
- GameEnds( WhiteOnMove(forwardMostMove) ? WhiteWins : BlackWins, // stalemated side wins!\r
- "Xboard adjudication: Stalemate", GE_XBOARD );\r
- else\r
- GameEnds( GameIsDrawn, "Xboard adjudication: Stalemate", GE_XBOARD );\r
- return;\r
+ case MT_STAINMATE:\r
+ reason = "Xboard adjudication: Stalemate";\r
+ if(epStatus[forwardMostMove] != EP_CHECKMATE) { // [HGM] don't touch win through baring or K-capt\r
+ epStatus[forwardMostMove] = EP_STALEMATE; // default result for stalemate is draw\r
+ if(gameInfo.variant == VariantLosers || gameInfo.variant == VariantGiveaway) // [HGM] losers:\r
+ epStatus[forwardMostMove] = EP_WINS; // in these variants stalemated is always a win\r
+ else if(gameInfo.variant == VariantSuicide) // in suicide it depends\r
+ epStatus[forwardMostMove] = NrW == NrPieces-NrW ? EP_STALEMATE :\r
+ ((NrW < NrPieces-NrW) != WhiteOnMove(forwardMostMove) ?\r
+ EP_CHECKMATE : EP_WINS);\r
+ else if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantXiangqi)\r
+ epStatus[forwardMostMove] = EP_CHECKMATE; // and in these variants being stalemated loses\r
}\r
break;\r
case MT_CHECKMATE:\r
- epStatus[forwardMostMove] = EP_CHECKMATE;\r
- if(appData.checkMates) {\r
+ reason = "Xboard adjudication: Checkmate";\r
+ epStatus[forwardMostMove] = (gameInfo.variant == VariantLosers ? EP_WINS : EP_CHECKMATE);\r
+ break;\r
+ }\r
+\r
+ switch(i = epStatus[forwardMostMove]) {\r
+ case EP_STALEMATE:\r
+ result = GameIsDrawn; break;\r
+ case EP_CHECKMATE:\r
+ result = WhiteOnMove(forwardMostMove) ? BlackWins : WhiteWins; break;\r
+ case EP_WINS:\r
+ result = WhiteOnMove(forwardMostMove) ? WhiteWins : BlackWins; break;\r
+ default:\r
+ result = (ChessMove) 0;\r
+ }\r
+ if(appData.checkMates && result) { // [HGM] mates: adjudicate finished games if requested\r
SendMoveToProgram(forwardMostMove-1, cps->other); /* make sure opponent gets to see move */\r
ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/\r
- GameEnds( WhiteOnMove(forwardMostMove) != (gameInfo.variant == VariantLosers) // [HGM] losers:\r
- ? BlackWins : WhiteWins, // reverse the result ( A!=1 is !A for a boolean)\r
- "Xboard adjudication: Checkmate", GE_XBOARD );\r
+ GameEnds( result, reason, GE_XBOARD );\r
return;\r
}\r
- break;\r
- }\r
\r
/* Next absolutely insufficient mating material. */\r
if( NrPieces == 2 || gameInfo.variant != VariantXiangqi && \r
if(!ourPerpetual && !hisPerpetual) { // no perpetual check, test for chase\r
hisPerpetual = PerpetualChase(k, forwardMostMove);\r
ourPerpetual = PerpetualChase(k+1, forwardMostMove);\r
- if(ourPerpetual && !hisPerpetual) { // we are actively checking him: forfeit\r
+ if(ourPerpetual && !hisPerpetual) { // we are actively chasing him: forfeit\r
GameEnds( WhiteOnMove(forwardMostMove) ? WhiteWins : BlackWins, \r
"Xboard adjudication: perpetual chasing", GE_XBOARD );\r
return;\r
EP_UNKNOWN, fromY, fromX, toY, toX, promoChar,\r
parseList[boardIndex]);\r
CopyBoard(boards[boardIndex + 1], boards[boardIndex]);\r
+ {int i; for(i=0; i<BOARD_SIZE; i++) castlingRights[boardIndex+1][i] = castlingRights[boardIndex][i];}\r
/* currentMoveString is set as a side-effect of yylex */\r
strcpy(moveList[boardIndex], currentMoveString);\r
strcat(moveList[boardIndex], "\n");\r
boardIndex++;\r
- ApplyMove(fromX, fromY, toX, toY, promoChar, boards[boardIndex]);\r
+ ApplyMove(fromX, fromY, toX, toY, promoChar, boards[boardIndex], \r
+ castlingRights[boardIndex], &epStatus[boardIndex]);\r
switch (MateTest(boards[boardIndex], PosFlags(boardIndex),\r
EP_UNKNOWN, castlingRights[boardIndex]) ) {\r
case MT_NONE:\r
strcat(parseList[boardIndex - 1], "+");\r
break;\r
case MT_CHECKMATE:\r
+ case MT_STAINMATE:\r
strcat(parseList[boardIndex - 1], "#");\r
break;\r
}\r
\r
/* Apply a move to the given board */\r
void\r
-ApplyMove(fromX, fromY, toX, toY, promoChar, board)\r
+ApplyMove(fromX, fromY, toX, toY, promoChar, board, castling, ep)\r
int fromX, fromY, toX, toY;\r
int promoChar;\r
Board board;\r
+ char *castling;\r
+ char *ep;\r
{\r
ChessSquare captured = board[toY][toX], piece, king; int p, oldEP = EP_NONE, berolina = 0;\r
\r
/* [HGM] compute & store e.p. status and castling rights for new position */\r
- /* if we are updating a board for which those exist (i.e. in boards[]) */\r
- if((p = ((int)board - (int)boards[0])/((int)boards[1]-(int)boards[0])) < MAX_MOVES && p > 0)\r
+ /* we can always do that 'in place', now pointers to these rights are passed to ApplyMove */\r
{ int i;\r
\r
if(gameInfo.variant == VariantBerolina) berolina = EP_BEROLIN_A;\r
- oldEP = epStatus[p-1];\r
- epStatus[p] = EP_NONE;\r
+ oldEP = *ep;\r
+ *ep = EP_NONE;\r
\r
if( board[toY][toX] != EmptySquare ) \r
- epStatus[p] = EP_CAPTURE; \r
+ *ep = EP_CAPTURE; \r
\r
if( board[fromY][fromX] == WhitePawn ) {\r
if(fromY != toY) // [HGM] Xiangqi sideway Pawn moves should not count as 50-move breakers\r
- epStatus[p] = EP_PAWN_MOVE;\r
+ *ep = EP_PAWN_MOVE;\r
if( toY-fromY==2) {\r
if(toX>BOARD_LEFT && board[toY][toX-1] == BlackPawn &&\r
gameInfo.variant != VariantBerolina || toX < fromX)\r
- epStatus[p] = toX | berolina;\r
+ *ep = toX | berolina;\r
if(toX<BOARD_RGHT-1 && board[toY][toX+1] == BlackPawn &&\r
gameInfo.variant != VariantBerolina || toX > fromX) \r
- epStatus[p] = toX;\r
+ *ep = toX;\r
}\r
} else \r
if( board[fromY][fromX] == BlackPawn ) {\r
if(fromY != toY) // [HGM] Xiangqi sideway Pawn moves should not count as 50-move breakers\r
- epStatus[p] = EP_PAWN_MOVE; \r
+ *ep = EP_PAWN_MOVE; \r
if( toY-fromY== -2) {\r
if(toX>BOARD_LEFT && board[toY][toX-1] == WhitePawn &&\r
gameInfo.variant != VariantBerolina || toX < fromX)\r
- epStatus[p] = toX | berolina;\r
+ *ep = toX | berolina;\r
if(toX<BOARD_RGHT-1 && board[toY][toX+1] == WhitePawn &&\r
gameInfo.variant != VariantBerolina || toX > fromX) \r
- epStatus[p] = toX;\r
+ *ep = toX;\r
}\r
}\r
\r
for(i=0; i<nrCastlingRights; i++) {\r
- castlingRights[p][i] = castlingRights[p-1][i];\r
- if(castlingRights[p][i] == fromX && castlingRank[i] == fromY ||\r
- castlingRights[p][i] == toX && castlingRank[i] == toY \r
- ) castlingRights[p][i] = -1; // revoke for moved or captured piece\r
+ if(castling[i] == fromX && castlingRank[i] == fromY ||\r
+ castling[i] == toX && castlingRank[i] == toY \r
+ ) castling[i] = -1; // revoke for moved or captured piece\r
}\r
\r
}\r
board[toY][toX] = (ChessSquare) (PROMOTED piece);\r
}\r
\r
+ if((gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat) \r
+ && promoChar != NULLCHAR && gameInfo.holdingsSize) { \r
+ // [HGM] superchess: take promotion piece out of holdings\r
+ int k = PieceToNumber(CharToPiece(ToUpper(promoChar)));\r
+ if((int)piece < (int)BlackPawn) { // determine stm from piece color\r
+ if(!--board[k][BOARD_WIDTH-2])\r
+ board[k][BOARD_WIDTH-1] = EmptySquare;\r
+ } else {\r
+ if(!--board[BOARD_HEIGHT-1-k][1])\r
+ board[BOARD_HEIGHT-1-k][0] = EmptySquare;\r
+ }\r
+ }\r
+\r
}\r
\r
/* Updates forwardMostMove */\r
commentList[forwardMostMove+1] = NULL;\r
}\r
CopyBoard(boards[forwardMostMove+1], boards[forwardMostMove]);\r
- ApplyMove(fromX, fromY, toX, toY, promoChar, boards[forwardMostMove+1]);\r
+ {int i; for(i=0; i<BOARD_SIZE; i++) castlingRights[forwardMostMove+1][i] = castlingRights[forwardMostMove][i];}\r
+ ApplyMove(fromX, fromY, toX, toY, promoChar, boards[forwardMostMove+1], \r
+ castlingRights[forwardMostMove+1], &epStatus[forwardMostMove+1]);\r
forwardMostMove++; // [HGM] bare: moved to after ApplyMove, to make sure clock interrupt finds complete board\r
gameInfo.result = GameUnfinished;\r
if (gameInfo.resultDetails != NULL) {\r
strcat(parseList[forwardMostMove - 1], "+");\r
break;\r
case MT_CHECKMATE:\r
+ case MT_STAINMATE:\r
strcat(parseList[forwardMostMove - 1], "#");\r
break;\r
}\r
// [HGM] losers: because the logic is becoming a bit hairy, determine true result first\r
if(epStatus[forwardMostMove] == EP_CHECKMATE) {\r
/* [HGM] verify: engine mate claims accepted if they were flagged */\r
- trueResult = WhiteOnMove(forwardMostMove) != (gameInfo.variant == VariantLosers)\r
- ? BlackWins : WhiteWins; // [HGM] losers: reverse the result in VariantLosers!\r
+ trueResult = WhiteOnMove(forwardMostMove) ? BlackWins : WhiteWins;\r
+ } else\r
+ if(epStatus[forwardMostMove] == EP_WINS) { // added code for games where being mated is a win\r
+ /* [HGM] verify: engine mate claims accepted if they were flagged */\r
+ trueResult = WhiteOnMove(forwardMostMove) ? WhiteWins : BlackWins;\r
} else\r
- if(epStatus[forwardMostMove] == EP_STALEMATE) {\r
+ if(epStatus[forwardMostMove] == EP_STALEMATE) { // only used to indicate draws now\r
trueResult = GameIsDrawn; // default; in variants where stalemate loses, Status is CHECKMATE\r
- if(gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuicide || \r
- gameInfo.variant == VariantLosers) // [HGM] losers: in giveaway variants stalemate wins\r
- trueResult = WhiteOnMove(forwardMostMove) ? WhiteWins : BlackWins;\r
}\r
\r
// now verify win claims, but not in drop games, as we don't understand those yet\r
case MT_CHECK:\r
break;\r
case MT_CHECKMATE:\r
+ case MT_STAINMATE:\r
if (WhiteOnMove(currentMove)) {\r
GameEnds(BlackWins, "Black mates", GE_FILE);\r
} else {\r
case MT_CHECK:\r
break;\r
case MT_CHECKMATE:\r
+ case MT_STAINMATE:\r
if (WhiteOnMove(currentMove)) {\r
GameEnds(BlackWins, "Black mates", GE_FILE);\r
} else {\r
break;\r
\r
case MT_CHECKMATE:\r
+ case MT_STAINMATE:\r
if (WhiteOnMove(currentMove)) {\r
GameEnds(BlackWins, "Black mates", GE_PLAYER);\r
} else {\r
WhiteOnMove(moveNumber) ? " " : ".. ",\r
parseList[moveNumber]);\r
}\r
+ // [HGM] PV info: display PV info together with (or as) comment\r
+ if(moveNumber >= 0 && (depth = pvInfoList[moveNumber].depth) > 0) {\r
+ if(text == NULL) text = ""; \r
+ score = pvInfoList[moveNumber].score;\r
+ sprintf(buf, "%s%.2f/%d %d\n%s", score>0 ? "+" : "", score/100.,\r
+ depth, (pvInfoList[moveNumber].time+50)/100, text);\r
+ text = buf;\r
+ }\r
} else title[0] = 0;\r
\r
- // [HGM] PV info: display PV info together with (or as) comment\r
- if(moveNumber >= 0 && (depth = pvInfoList[moveNumber].depth) > 0) {\r
- if(text == NULL) text = ""; \r
- score = pvInfoList[moveNumber].score;\r
- sprintf(buf, "%s%.2f/%d %d\n%s", score>0 ? "+" : "", score/100.,\r
- depth, (pvInfoList[moveNumber].time+50)/100, text);\r
- CommentPopUp(title, buf);\r
- } else\r
if (text != NULL)\r
CommentPopUp(title, text);\r
}\r