\r
#else\r
\r
-#define DoSleep( n )\r
+#define DoSleep( n ) if( (n) >= 0) sleep(n)\r
\r
#endif\r
\r
int ms; /* Assuming this is >= 16 bits */\r
} TimeMark;\r
\r
-/* Search stats from chessprogram */\r
-typedef struct {\r
- char movelist[2*MSG_SIZ]; /* Last PV we were sent */\r
- int depth; /* Current search depth */\r
- int nr_moves; /* Total nr of root moves */\r
- int moves_left; /* Moves remaining to be searched */\r
- char move_name[MOVE_LEN]; /* Current move being searched, if provided */\r
- unsigned long nodes; /* # of nodes searched */\r
- int time; /* Search time (centiseconds) */\r
- int score; /* Score (centipawns) */\r
- int got_only_move; /* If last msg was "(only move)" */\r
- int got_fail; /* 0 - nothing, 1 - got "--", 2 - got "++" */\r
- int ok_to_send; /* handshaking between send & recv */\r
- int line_is_book; /* 1 if movelist is book moves */\r
- int seen_stat; /* 1 if we've seen the stat01: line */\r
-} ChessProgramStats;\r
-\r
int establish P((void));\r
void read_from_player P((InputSourceRef isr, VOIDSTAR closure,\r
char *buf, int count, int error));\r
void FeatureDone P((ChessProgramState* cps, int val));\r
void InitChessProgram P((ChessProgramState *cps, int setup));\r
ChessProgramState *WhitePlayer();\r
+void InsertIntoMemo P((int which, char *text)); // [HGM] kibitz: in engineo.c\r
+int VerifyDisplayMode P(());\r
\r
char *GetInfoFromComment( int, char * ); // [HGM] PV time: returns stripped comment\r
void InitEngineUCI( const char * iniDir, ChessProgramState * cps ); // [HGM] moved here from winboard.c\r
extern char installDir[MSG_SIZ];\r
\r
extern int tinyLayout, smallLayout;\r
-static ChessProgramStats programStats;\r
+ChessProgramStats programStats;\r
static int exiting = 0; /* [HGM] moved to top */\r
static int setboardSpoiledMachineBlack = 0, errorExitFlag = 0;\r
extern int startedFromPositionFile;\r
int whiteNPS, blackNPS; /* [HGM] nps: for easily making clocks aware of NPS */\r
VariantClass currentlyInitializedVariant; /* [HGM] variantswitch */\r
int lastIndex = 0; /* [HGM] autoinc: last game/position used in match mode */\r
+int opponentKibitzes;\r
\r
/* States for ics_getting_history */\r
#define H_FALSE 0\r
if ((index % 2) == 0) flags |= F_WHITE_ON_MOVE;\r
switch (gameInfo.variant) {\r
case VariantSuicide:\r
- case VariantGiveaway:\r
- flags |= F_IGNORE_CHECK;\r
flags &= ~F_ALL_CASTLE_OK;\r
+ case VariantGiveaway: // [HGM] moved this case label one down: seems Giveaway does have castling on ICC!\r
+ flags |= F_IGNORE_CHECK;\r
break;\r
case VariantAtomic:\r
flags |= F_IGNORE_CHECK | F_ATOMIC_CAPTURE;\r
BlackKing, BlackBishop, BlackMarshall, BlackKnight, BlackRook }\r
};\r
\r
+ChessSquare GreatArray[2][BOARD_SIZE] = {\r
+ { WhiteDragon, WhiteKnight, WhiteAlfil, WhiteGrasshopper, WhiteKing, \r
+ WhiteSilver, WhiteCardinal, WhiteAlfil, WhiteKnight, WhiteDragon },\r
+ { BlackDragon, BlackKnight, BlackAlfil, BlackGrasshopper, BlackKing, \r
+ BlackSilver, BlackCardinal, BlackAlfil, BlackKnight, BlackDragon },\r
+};\r
+\r
ChessSquare JanusArray[2][BOARD_SIZE] = {\r
{ WhiteRook, WhiteAngel, WhiteKnight, WhiteBishop, WhiteKing, \r
WhiteQueen, WhiteBishop, WhiteKnight, WhiteAngel, WhiteRook },\r
#define XiangqiPosition FIDEArray\r
#define CapablancaArray FIDEArray\r
#define GothicArray FIDEArray\r
+#define GreatArray FIDEArray\r
#endif // !(BOARD_SIZE>=10)\r
\r
#if (BOARD_SIZE>=12)\r
case VariantCapaRandom: /* should work */\r
case VariantJanus: /* should work */\r
case VariantSuper: /* experimental */\r
+ case VariantGreat: /* experimental, requires legality testing to be off */\r
break;\r
}\r
}\r
\r
if (!found) {\r
if ((StrCaseStr(e, "fischer") && StrCaseStr(e, "random"))\r
- || StrCaseStr(e, "wild/fr")) {\r
+ || StrCaseStr(e, "wild/fr") \r
+ || StrCaseStr(e, "frc") || StrCaseStr(e, "960")) {\r
v = VariantFischeRandom;\r
} else if ((i = 4, p = StrCaseStr(e, "wild")) ||\r
(i = 1, p = StrCaseStr(e, "w"))) {\r
case 49:\r
v = VariantSuper;\r
break;\r
+ case 50:\r
+ v = VariantGreat;\r
+ break;\r
case -1:\r
/* Found "wild" or "w" in the string but no number;\r
must assume it's normal chess. */\r
j = PieceToNumber(piece);\r
if(j >= gameInfo.holdingsSize) continue; /* ignore pieces that do not fit */\r
if(j < 0) continue; /* should not happen */\r
- piece = (ChessSquare) ( j + (int)lowestPiece );\r
+ piece = (ChessSquare) ( (int)piece + (int)lowestPiece );\r
board[holdingsStartRow+j*direction][holdingsColumn] = piece;\r
board[holdingsStartRow+j*direction][countsColumn]++;\r
}\r
/*----------------------------*/\r
\r
ColorClass curColor = ColorNormal;\r
+int suppressKibitz = 0;\r
\r
void\r
read_from_ics(isr, closure, data, count, error)\r
parse[parse_pos++] = buf[i];\r
if (buf[i] == '\n') {\r
parse[parse_pos] = NULLCHAR;\r
- AppendComment(forwardMostMove, StripHighlight(parse));\r
+ if(!suppressKibitz) // [HGM] kibitz\r
+ AppendComment(forwardMostMove, StripHighlight(parse));\r
+ else { // [HGM kibitz: divert memorized engine kibitz to engine-output window\r
+ int nrDigit = 0, nrAlph = 0, i;\r
+ if(parse_pos > MSG_SIZ - 30) // defuse unreasonably long input\r
+ { parse_pos = MSG_SIZ-30; parse[parse_pos - 1] = '\n'; }\r
+ parse[parse_pos] = NULLCHAR;\r
+ // try to be smart: if it does not look like search info, it should go to\r
+ // ICS interaction window after all, not to engine-output window.\r
+ for(i=0; i<parse_pos; i++) { // count letters and digits\r
+ nrDigit += (parse[i] >= '0' && parse[i] <= '9');\r
+ nrAlph += (parse[i] >= 'a' && parse[i] <= 'z');\r
+ nrAlph += (parse[i] >= 'A' && parse[i] <= 'Z');\r
+ }\r
+ if(nrAlph < 9*nrDigit) { // if more than 10% digit we assume search info\r
+ OutputKibitz(suppressKibitz, parse);\r
+ } else {\r
+ char tmp[MSG_SIZ];\r
+ sprintf(tmp, "your opponent kibitzes: %s", parse);\r
+ SendToPlayer(tmp, strlen(tmp));\r
+ }\r
+ }\r
started = STARTED_NONE;\r
} else {\r
/* Don't match patterns against characters in chatter */\r
}\r
\r
oldi = i;\r
+ // [HGM] kibitz: try to recognize opponent engine-score kibitzes, to divert them to engine-output window\r
+ if (appData.autoKibitz && started == STARTED_NONE && \r
+ (gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack || gameMode == IcsObserving)) {\r
+ if(looking_at(buf, &i, "* kibitzes: ") &&\r
+ (StrStr(star_match[0], gameInfo.white) == star_match[0] || \r
+ StrStr(star_match[0], gameInfo.black) == star_match[0] )) { // kibitz of self or opponent\r
+ suppressKibitz = TRUE;\r
+ if((StrStr(star_match[0], gameInfo.white) == star_match[0])\r
+ && (gameMode == IcsPlayingWhite) ||\r
+ (StrStr(star_match[0], gameInfo.black) == star_match[0])\r
+ && (gameMode == IcsPlayingBlack) ) // opponent kibitz\r
+ started = STARTED_CHATTER; // own kibitz we simply discard\r
+ else {\r
+ started = STARTED_COMMENT; // make sure it will be collected in parse[]\r
+ parse_pos = 0; parse[0] = NULLCHAR;\r
+ savingComment = TRUE;\r
+ suppressKibitz = gameMode != IcsObserving ? 2 :\r
+ (StrStr(star_match[0], gameInfo.white) == NULL) + 1;\r
+ } \r
+ continue;\r
+ } else\r
+ if(looking_at(buf, &i, "kibitzed to")) { // suppress the acknowledgements of our own autoKibitz\r
+ started = STARTED_CHATTER;\r
+ suppressKibitz = TRUE;\r
+ }\r
+ } // [HGM] kibitz: end of patch\r
+\r
if (appData.zippyTalk || appData.zippyPlay) {\r
#if ZIPPY\r
if (ZippyControl(buf, &i) ||\r
\r
if (looking_at(buf, &i, "% ") ||\r
((started == STARTED_MOVES || started == STARTED_MOVES_NOHIDE)\r
- && looking_at(buf, &i, "}*"))) {\r
+ && looking_at(buf, &i, "}*"))) { char *bookHit = NULL; // [HGM] book\r
savingComment = FALSE;\r
switch (started) {\r
case STARTED_MOVES:\r
}\r
SendTimeRemaining(&first, TRUE);\r
}\r
+#if 0\r
if (first.useColors) {\r
SendToProgram("white\ngo\n", &first);\r
} else {\r
SendToProgram("go\n", &first);\r
}\r
+#else\r
+ if (first.useColors) {\r
+ SendToProgram("white\n", &first); // [HGM] book: made sending of "go\n" book dependent\r
+ }\r
+ bookHit = SendMoveToBookUser(forwardMostMove-1, &first, TRUE); // [HGM] book: probe book for initial pos\r
+#endif\r
first.maybeThinking = TRUE;\r
} else {\r
if (first.usePlayother) {\r
}\r
SendTimeRemaining(&first, FALSE);\r
}\r
+#if 0\r
if (first.useColors) {\r
SendToProgram("black\ngo\n", &first);\r
} else {\r
SendToProgram("go\n", &first);\r
}\r
+#else\r
+ if (first.useColors) {\r
+ SendToProgram("black\n", &first);\r
+ }\r
+ bookHit = SendMoveToBookUser(forwardMostMove-1, &first, TRUE);\r
+#endif\r
first.maybeThinking = TRUE;\r
} else {\r
if (first.usePlayother) {\r
default:\r
break;\r
}\r
+ if(bookHit) { // [HGM] book: simulate book reply\r
+ static char bookMove[MSG_SIZ]; // a bit generous?\r
+\r
+ programStats.depth = programStats.nodes = programStats.time = \r
+ programStats.score = programStats.got_only_move = 0;\r
+ sprintf(programStats.movelist, "%s (xbook)", bookHit);\r
+\r
+ strcpy(bookMove, "move ");\r
+ strcat(bookMove, bookHit);\r
+ HandleMachineMove(bookMove, &first);\r
+ }\r
continue;\r
}\r
\r
i++; /* skip unparsed character and loop back */\r
}\r
\r
- if (started != STARTED_MOVES && started != STARTED_BOARD &&\r
+ if (started != STARTED_MOVES && started != STARTED_BOARD && !suppressKibitz && // [HGM] kibitz suppress printing in ICS interaction window\r
started != STARTED_HOLDINGS && i > next_out) {\r
SendToPlayer(&buf[next_out], i - next_out);\r
next_out = i;\r
}\r
+ suppressKibitz = FALSE; // [HGM] kibitz: has done its duty in if-statement above\r
\r
leftover_len = buf_len - leftover_start;\r
/* if buffer ends with something we couldn't parse,\r
strcat(parseList[moveNum - 1], " ");\r
strcat(parseList[moveNum - 1], elapsed_time);\r
moveList[moveNum - 1][0] = NULLCHAR;\r
- } else if (ParseOneMove(move_str, moveNum - 1, &moveType,\r
- &fromX, &fromY, &toX, &toY, &promoChar)) {\r
+ } else if (strcmp(move_str, "none") == 0) {\r
+ // [HGM] long SAN: swapped order; test for 'none' before parsing move\r
+ /* Again, we don't know what the board looked like;\r
+ this is really the start of the game. */\r
+ parseList[moveNum - 1][0] = NULLCHAR;\r
+ moveList[moveNum - 1][0] = NULLCHAR;\r
+ backwardMostMove = moveNum;\r
+ startedFromSetupPosition = TRUE;\r
+ fromX = fromY = toX = toY = -1;\r
+ } else {\r
+ // [HGM] long SAN: if legality-testing is off, disambiguation might not work or give wrong move. \r
+ // So we parse the long-algebraic move string in stead of the SAN move\r
+ int valid; char buf[MSG_SIZ], *prom;\r
+\r
+ // str looks something like "Q/a1-a2"; kill the slash\r
+ if(str[1] == '/') \r
+ sprintf(buf, "%c%s", str[0], str+2);\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.debugMode) \r
+ fprintf(debugFP, "replaced ICS move '%s' by '%s'\n", move_str, buf);\r
+ strcpy(move_str, buf);\r
+ }\r
+ valid = ParseOneMove(move_str, moveNum - 1, &moveType,\r
+ &fromX, &fromY, &toX, &toY, &promoChar)\r
+ || ParseOneMove(buf, moveNum - 1, &moveType,\r
+ &fromX, &fromY, &toX, &toY, &promoChar);\r
+ // end of long SAN patch\r
+ if (valid) {\r
(void) CoordsToAlgebraic(boards[moveNum - 1],\r
PosFlags(moveNum - 1), EP_UNKNOWN,\r
fromY, fromX, toY, toX, promoChar,\r
/* currentMoveString is set as a side-effect of ParseOneMove */\r
strcpy(moveList[moveNum - 1], currentMoveString);\r
strcat(moveList[moveNum - 1], "\n");\r
- } else if (strcmp(move_str, "none") == 0) {\r
- /* Again, we don't know what the board looked like;\r
- this is really the start of the game. */\r
- parseList[moveNum - 1][0] = NULLCHAR;\r
- moveList[moveNum - 1][0] = NULLCHAR;\r
- backwardMostMove = moveNum;\r
- startedFromSetupPosition = TRUE;\r
- fromX = fromY = toX = toY = -1;\r
- } else {\r
+ } else {\r
/* Move from ICS was illegal!? Punt. */\r
if (appData.debugMode) {\r
fprintf(debugFP, "Illegal move from ICS '%s'\n", move_str);\r
strcat(parseList[moveNum - 1], elapsed_time);\r
moveList[moveNum - 1][0] = NULLCHAR;\r
fromX = fromY = toX = toY = -1;\r
+ }\r
}\r
if (appData.debugMode) {\r
fprintf(debugFP, "Move parsed to '%s'\n", parseList[moveNum - 1]);\r
\r
programStats.depth = programStats.nodes = programStats.time = \r
programStats.score = programStats.got_only_move = 0;\r
- sprintf(programStats.movelist, "%s (xbook)", bookMove);\r
+ sprintf(programStats.movelist, "%s (xbook)", bookHit);\r
\r
strcpy(bookMove, "move ");\r
strcat(bookMove, bookHit);\r
case BlackPromotionChancellor:\r
case WhitePromotionArchbishop:\r
case BlackPromotionArchbishop:\r
- if(gameInfo.variant == VariantShatranj)\r
+ if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier)\r
sprintf(user_move, "%c%c%c%c=%c\n",\r
AAA + fromX, ONE + fromY, AAA + toX, ONE + toY,\r
PieceToChar(WhiteFerz));\r
+ else if(gameInfo.variant == VariantGreat)\r
+ sprintf(user_move, "%c%c%c%c=%c\n",\r
+ AAA + fromX, ONE + fromY, AAA + toX, ONE + toY,\r
+ PieceToChar(WhiteMan));\r
else\r
sprintf(user_move, "%c%c%c%c=%c\n",\r
AAA + fromX, ONE + fromY, AAA + toX, ONE + toY,\r
pieces = fairyArray;\r
SetCharTable(pieceToChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk"); \r
break;\r
+ case VariantGreat:\r
+ pieces = GreatArray;\r
+ gameInfo.boardWidth = 10;\r
+ SetCharTable(pieceToChar, "PN....E...S..HWGMKpn....e...s..hwgmk");\r
+ gameInfo.holdingsSize = 8;\r
+ break;\r
case VariantSuper:\r
pieces = FIDEArray;\r
SetCharTable(pieceToChar, "PNBRQ..SE.......V.AKpnbrq..se.......v.ak");\r
}\r
\r
if(gameInfo.variant == VariantSuper) Prelude(initialPosition);\r
+ if(gameInfo.variant == VariantGreat) { // promotion commoners\r
+ initialPosition[PieceToNumber(WhiteMan)][BOARD_RGHT-1] = WhiteMan;\r
+ initialPosition[PieceToNumber(WhiteMan)][BOARD_RGHT-2] = 9;\r
+ initialPosition[BOARD_HEIGHT-1-PieceToNumber(WhiteMan)][0] = BlackMan;\r
+ initialPosition[BOARD_HEIGHT-1-PieceToNumber(WhiteMan)][1] = 9;\r
+ }\r
#if 0\r
if(gameInfo.variant == VariantFischeRandom) {\r
if( appData.defaultFrcPosition < 0 ) {\r
return ImpossibleMove;\r
}\r
}\r
-\r
+if(appData.debugMode) fprintf(debugFP, "moveType 3 = %d, promochar = %x\n", moveType, promoChar);\r
return moveType;\r
/* [HGM] <popupFix> in stead of calling FinishMove directly, this\r
function is made into one that returns an OK move type if FinishMove\r
/*char*/int promoChar;\r
{\r
char *bookHit = 0;\r
-\r
- if(gameInfo.variant == VariantSuper && promoChar != NULLCHAR) { \r
+if(appData.debugMode) fprintf(debugFP, "moveType 5 = %d, promochar = %x\n", moveType, promoChar);\r
+ if((gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat) && promoChar != NULLCHAR) { \r
// [HGM] superchess: suppress promotions to non-available piece\r
int k = PieceToNumber(CharToPiece(ToUpper(promoChar)));\r
if(WhiteOnMove(currentMove)) {\r
move type in caller when we know the move is a legal promotion */\r
if(moveType == NormalMove && promoChar)\r
moveType = PromoCharToMoveType(WhiteOnMove(currentMove), promoChar);\r
-\r
+if(appData.debugMode) fprintf(debugFP, "moveType 1 = %d, promochar = %x\n", moveType, promoChar);\r
/* [HGM] convert drag-and-drop piece drops to standard form */\r
if( fromX == BOARD_LEFT-2 || fromX == BOARD_RGHT+1) {\r
moveType = WhiteOnMove(currentMove) ? WhiteDrop : BlackDrop;\r
\r
MakeMove(fromX, fromY, toX, toY, promoChar); /*updates forwardMostMove*/\r
\r
- if(gameInfo.variant == VariantSuper && promoChar != NULLCHAR && gameInfo.holdingsSize) { \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
}\r
ModeHighlight();\r
}\r
-\r
+if(appData.debugMode) fprintf(debugFP, "moveType 2 = %d, promochar = %x\n", moveType, promoChar);\r
/* Relay move to ICS or chess engine */\r
if (appData.icsActive) {\r
if (gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
\r
programStats.depth = programStats.nodes = programStats.time = \r
programStats.score = programStats.got_only_move = 0;\r
- sprintf(programStats.movelist, "%s (xbook)", bookMove);\r
+ sprintf(programStats.movelist, "%s (xbook)", bookHit);\r
\r
strcpy(bookMove, "move ");\r
strcat(bookMove, bookHit);\r
to do anything in between, can call this routine the old way. \r
*/\r
ChessMove moveType = UserMoveTest(fromX, fromY, toX, toY, promoChar);\r
-\r
+if(appData.debugMode) fprintf(debugFP, "moveType 4 = %d, promochar = %x\n", moveType, promoChar);\r
if(moveType != ImpossibleMove)\r
FinishMove(moveType, fromX, fromY, toX, toY, promoChar);\r
}\r
//first determine if the incoming move brings opponent into his book\r
if(appData.usePolyglotBook && (cps == &first ? !appData.firstHasOwnBookUCI : !appData.secondHasOwnBookUCI))\r
bookHit = ProbeBook(moveNr+1, appData.polyglotBook); // returns move\r
- if(appData.debugMode && bookHit) fprintf(debugFP, "book hit = %s\n", bookHit);\r
+ if(appData.debugMode) fprintf(debugFP, "book hit = %s\n", bookHit ? bookHit : "(NULL)");\r
if(bookHit != NULL && !cps->bookSuspend) {\r
// make sure opponent is not going to reply after receiving move to book position\r
SendToProgram("force\n", cps);\r
if( gameMode == TwoMachinesPlay ) {\r
// [HGM] some adjudications useful with buggy engines\r
int k, count = 0, epFile = epStatus[forwardMostMove]; static int bare = 1;\r
- if(gameInfo.holdingsSize == 0 || gameInfo.variant == VariantSuper) {\r
+ if(gameInfo.holdingsSize == 0 || gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat) {\r
\r
if(appData.testLegality)\r
// don't wait for engine to announce game end if we can judge ourselves\r
case MT_STALEMATE:\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
GameEnds( GameIsDrawn, "Xboard adjudication: Stalemate",\r
GE_XBOARD );\r
case MT_CHECKMATE:\r
epStatus[forwardMostMove] = EP_CHECKMATE;\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
GameEnds( WhiteOnMove(forwardMostMove) ? BlackWins : WhiteWins, \r
"Xboard adjudication: Checkmate", \r
case WhiteKnight:\r
NrWN++; break;\r
case WhiteBishop:\r
+ case WhiteFerz: // [HGM] shatranj: kludge to mke it work in shatranj\r
bishopsColor |= 1 << ((i^j)&1);\r
NrWB++; break;\r
case BlackKnight:\r
NrBN++; break;\r
case BlackBishop:\r
+ case BlackFerz: // [HGM] shatranj: kludge to mke it work in shatranj\r
bishopsColor |= 1 << ((i^j)&1);\r
NrBB++; break;\r
case WhiteRook:\r
}\r
NrPieces += (p != EmptySquare);\r
NrW += ((int)p < (int)BlackPawn);\r
+ if(gameInfo.variant == VariantXiangqi && \r
+ (p == WhiteFerz || p == WhiteAlfil || p == BlackFerz || p == BlackAlfil)) {\r
+ NrPieces--; // [HGM] XQ: do not count purely defensive pieces\r
+ NrW -= ((int)p < (int)BlackPawn);\r
+ }\r
}\r
\r
- if( NrPieces == 3 && NrWN+NrBN+NrWB+NrBB == 1 || NrPieces == 2\r
- || NrPieces == 4 && NrBB+NrWB == NrPieces-2 && bishopsColor != 3)\r
+ if( NrPieces == 2 || gameInfo.variant != VariantXiangqi &&\r
+ (NrPieces == 3 && NrWN+NrBN+NrWB+NrBB == 1 ||\r
+ NrPieces == NrBB+NrWB+2 && bishopsColor != 3)) // [HGM] all Bishops (Ferz!) same color\r
{ /* KBK, KNK, KK of KBKB with like Bishops */\r
\r
/* always flag draws, for judging claims */\r
\r
if(appData.materialDraws) {\r
/* but only adjudicate them if adjudication enabled */\r
+ SendToProgram("force\n", cps->other); // suppress reply\r
+ SendMoveToProgram(forwardMostMove-1, cps->other); /* make sure opponent gets to see last move */\r
ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/\r
GameEnds( GameIsDrawn, "Xboard adjudication: Insufficient mating material", GE_XBOARD );\r
return;\r
\r
if(--bare < 0 && appData.checkMates) {\r
/* but only adjudicate them if adjudication enabled */\r
+ SendMoveToProgram(forwardMostMove-1, cps->other); /* make sure opponent gets to see move */\r
ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/\r
GameEnds( NrW > 1 ? WhiteWins : NrPieces - NrW > 1 ? BlackWins : GameIsDrawn, \r
"Xboard adjudication: Bare king", GE_XBOARD );\r
) ) {\r
if(--moveCount < 0 && appData.trivialDraws)\r
{ /* if the first 3 moves do not show a tactical win, declare draw */\r
+ SendToProgram("force\n", cps->other); // suppress reply\r
+ SendMoveToProgram(forwardMostMove-1, cps->other); /* make sure opponent gets to see move */\r
ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/\r
GameEnds( GameIsDrawn, "Xboard adjudication: Trivial draw", GE_XBOARD );\r
return;\r
if( rights == 0 && ++count > appData.drawRepeats-2\r
&& appData.drawRepeats > 1) {\r
/* adjudicate after user-specified nr of repeats */\r
+ SendToProgram("force\n", cps->other); // suppress reply\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 == VariantXiangqi && appData.testLegality) { \r
+ // [HGM] xiangqi: check for forbidden perpetuals\r
+ int m, ourPerpetual = 1, hisPerpetual = 1;\r
+ for(m=forwardMostMove; m>k; m-=2) {\r
+ if(MateTest(boards[m], PosFlags(m), \r
+ EP_NONE, castlingRights[m]) != MT_CHECK)\r
+ ourPerpetual = 0; // the current mover did not always check\r
+ if(MateTest(boards[m-1], PosFlags(m-1), \r
+ EP_NONE, castlingRights[m-1]) != MT_CHECK)\r
+ hisPerpetual = 0; // the opponent did not always check\r
+ }\r
+ if(ourPerpetual && !hisPerpetual) { // we are actively checking him: forfeit\r
+ GameEnds( WhiteOnMove(forwardMostMove) ? WhiteWins : BlackWins, \r
+ "Xboard adjudication: perpetual checking", GE_XBOARD );\r
+ return;\r
+ }\r
+ if(hisPerpetual && !ourPerpetual) // he is checking us, but did not repeat yet\r
+ break; // (or we would have caught him before). Abort repetition-checking loop.\r
+ // if neither of us is checking all the time, or both are, it is draw\r
+ // (illegal-chase forfeits not implemented yet!)\r
+ }\r
GameEnds( GameIsDrawn, "Xboard adjudication: repetition draw", GE_XBOARD );\r
return;\r
}\r
epStatus[forwardMostMove] = EP_RULE_DRAW;\r
/* this is used to judge if draw claims are legal */\r
if(appData.ruleMoves > 0 && count >= 2*appData.ruleMoves) {\r
+ SendToProgram("force\n", cps->other); // suppress reply\r
+ SendMoveToProgram(forwardMostMove-1, cps->other); /* make sure opponent gets to see move */\r
ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/\r
GameEnds( GameIsDrawn, "Xboard adjudication: 50-move rule", GE_XBOARD );\r
return;\r
if(epStatus[forwardMostMove] == EP_INSUF_DRAW)\r
p = "Draw claim: insufficient mating material";\r
if( p != NULL ) {\r
+ SendToProgram("force\n", cps->other); // suppress reply\r
+ SendMoveToProgram(forwardMostMove-1, cps->other); /* make sure opponent gets to see move */\r
GameEnds( GameIsDrawn, p, GE_XBOARD );\r
ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/\r
return;\r
}\r
\r
\r
- }\r
-\r
- if( appData.adjudicateDrawMoves > 0 && forwardMostMove > (2*appData.adjudicateDrawMoves) ) {\r
- ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/\r
+ if( appData.adjudicateDrawMoves > 0 && forwardMostMove > (2*appData.adjudicateDrawMoves) ) {\r
+ SendToProgram("force\n", cps->other); // suppress reply\r
+ SendMoveToProgram(forwardMostMove-1, cps->other); /* make sure opponent gets to see move */\r
+ ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/\r
\r
- GameEnds( GameIsDrawn, "Xboard adjudication: long game", GE_XBOARD );\r
+ GameEnds( GameIsDrawn, "Xboard adjudication: long game", GE_XBOARD );\r
\r
- return;\r
+ return;\r
+ }\r
}\r
\r
bookHit = NULL;\r
cps = cps->other;\r
programStats.depth = programStats.nodes = programStats.time = \r
programStats.score = programStats.got_only_move = 0;\r
- sprintf(programStats.movelist, "%s (xbook)", bookMove);\r
+ sprintf(programStats.movelist, "%s (xbook)", bookHit);\r
\r
if(cps->lastPing != cps->lastPong) {\r
savedMessage = message; // args for deferred call\r
epStatus[p] = EP_CAPTURE; \r
\r
if( board[fromY][fromX] == WhitePawn ) {\r
- epStatus[p] = EP_PAWN_MOVE; \r
- if( toY-fromY==2)\r
+ if(fromY != toY) // [HGM] Xiangqi sideway Pawn moves should not count as 50-move breakers\r
+ epStatus[p] = 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
if(toX<BOARD_RGHT-1 && board[toY][toX+1] == BlackPawn &&\r
gameInfo.variant != VariantBerolina || toX > fromX) \r
epStatus[p] = toX;\r
+ }\r
} else \r
if( board[fromY][fromX] == BlackPawn ) {\r
- epStatus[p] = EP_PAWN_MOVE; \r
- if( toY-fromY== -2)\r
+ if(fromY != toY) // [HGM] Xiangqi sideway Pawn moves should not count as 50-move breakers\r
+ epStatus[p] = 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
if(toX<BOARD_RGHT-1 && board[toY][toX+1] == WhitePawn &&\r
gameInfo.variant != VariantBerolina || toX > fromX) \r
epStatus[p] = toX;\r
+ }\r
}\r
\r
for(i=0; i<nrCastlingRights; i++) {\r
if (captured != EmptySquare && gameInfo.holdingsSize > 0\r
&& gameInfo.variant != VariantBughouse ) {\r
/* [HGM] holdings: Add to holdings, if holdings exist */\r
- if(gameInfo.variant == VariantSuper) { \r
+ if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat) { \r
// [HGM] superchess: suppress flipping color of captured pieces by reverse pre-flip\r
captured = (int) captured >= (int) BlackPawn ? BLACK_TO_WHITE captured : WHITE_TO_BLACK captured;\r
}\r
overruled = gameInfo.boardWidth != 12 || gameInfo.boardHeight != 8 || gameInfo.holdingsSize != 0;\r
if( gameInfo.variant == VariantSuper )\r
overruled = gameInfo.boardWidth != 8 || gameInfo.boardHeight != 8 || gameInfo.holdingsSize != 8;\r
+ if( gameInfo.variant == VariantGreat )\r
+ overruled = gameInfo.boardWidth != 10 || gameInfo.boardHeight != 8 || gameInfo.holdingsSize != 8;\r
\r
if(overruled) {\r
sprintf(b, "%dx%d+%d_%s", gameInfo.boardWidth, gameInfo.boardHeight, \r
cps->isr = AddInputSource(cps->pr, TRUE, ReceiveFromProgram, cps);\r
if (cps->protocolVersion > 1) {\r
sprintf(buf, "xboard\nprotover %d\n", cps->protocolVersion);\r
+ cps->nrOptions = 0; // [HGM] options: clear all engine-specific options\r
+ cps->comboCnt = 0; // and values of combo boxes\r
SendToProgram(buf, cps);\r
} else {\r
SendToProgram("xboard\n", cps);\r
claimer = whosays == GE_ENGINE1 ? /* color of claimer */\r
first.twoMachinesColor[0] :\r
second.twoMachinesColor[0] ;\r
- if( (gameInfo.holdingsWidth == 0 || gameInfo.variant == VariantSuper) &&\r
+ if( (gameInfo.holdingsWidth == 0 || gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat) &&\r
(result == WhiteWins && claimer == 'w' ||\r
result == BlackWins && claimer == 'b' ) ) {\r
if (appData.debugMode) {\r
/* (Claiming a loss is accepted no questions asked!) */\r
}\r
/* [HGM] bare: don't allow bare King to win */\r
- if((gameInfo.holdingsWidth == 0 || gameInfo.variant == VariantSuper) && result != GameIsDrawn)\r
+ if((gameInfo.holdingsWidth == 0 || gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)\r
+ && result != GameIsDrawn)\r
{ int i, j, k=0, color = (result==WhiteWins ? (int)WhitePawn : (int)BlackPawn);\r
for(j=BOARD_LEFT; j<BOARD_RGHT; j++) for(i=0; i<BOARD_HEIGHT; i++) {\r
int p = (int)boards[forwardMostMove][i][j] - color;\r
ics_gamenum = -1;\r
white_holding[0] = black_holding[0] = NULLCHAR;\r
ClearProgramStats();\r
+ opponentKibitzes = FALSE; // [HGM] kibitz: do not reserve space in engine-output window in zippy mode\r
\r
ResetFrontEnd();\r
ClearHighlights();\r
if (appData.autoFlipView && !flipView) {\r
flipView = !flipView;\r
DrawPosition(FALSE, NULL);\r
+ DisplayBothClocks(); // [HGM] logo: clocks might have to be exchanged;\r
}\r
\r
if(bookHit) { // [HGM] book: simulate book reply\r
\r
programStats.depth = programStats.nodes = programStats.time = \r
programStats.score = programStats.got_only_move = 0;\r
- sprintf(programStats.movelist, "%s (xbook)", bookMove);\r
+ sprintf(programStats.movelist, "%s (xbook)", bookHit);\r
\r
strcpy(bookMove, "move ");\r
strcat(bookMove, bookHit);\r
if (appData.autoFlipView && flipView) {\r
flipView = !flipView;\r
DrawPosition(FALSE, NULL);\r
+ DisplayBothClocks(); // [HGM] logo: clocks might have to be exchanged;\r
}\r
if(bookHit) { // [HGM] book: simulate book reply\r
static char bookMove[MSG_SIZ]; // a bit generous?\r
\r
programStats.depth = programStats.nodes = programStats.time = \r
programStats.score = programStats.got_only_move = 0;\r
- sprintf(programStats.movelist, "%s (xbook)", bookMove);\r
+ sprintf(programStats.movelist, "%s (xbook)", bookHit);\r
\r
strcpy(bookMove, "move ");\r
strcat(bookMove, bookHit);\r
\r
programStats.depth = programStats.nodes = programStats.time = \r
programStats.score = programStats.got_only_move = 0;\r
- sprintf(programStats.movelist, "%s (xbook)", bookMove);\r
+ sprintf(programStats.movelist, "%s (xbook)", bookHit);\r
\r
strcpy(bookMove, "move ");\r
strcat(bookMove, bookHit);\r
return FALSE;\r
}\r
\r
+int \r
+ParseOption(Option *opt, ChessProgramState *cps)\r
+// [HGM] options: process the string that defines an engine option, and determine\r
+// name, type, default value, and allowed value range\r
+{\r
+ char *p, *q, buf[MSG_SIZ];\r
+ int n, min = (-1)<<31, max = 1<<31, def;\r
+\r
+ if(p = strstr(opt->name, " -spin ")) {\r
+ if((n = sscanf(p, " -spin %d %d %d", &def, &min, &max)) < 3 ) return FALSE;\r
+ if(max < min) max = min; // enforce consistency\r
+ if(def < min) def = min;\r
+ if(def > max) def = max;\r
+ opt->value = def;\r
+ opt->min = min;\r
+ opt->max = max;\r
+ opt->type = Spin;\r
+ } else if(p = strstr(opt->name, " -string ")) {\r
+ opt->textValue = p+9;\r
+ opt->type = TextBox;\r
+ } else if(p = strstr(opt->name, " -check ")) {\r
+ if(sscanf(p, " -check %d", &def) < 1) return FALSE;\r
+ opt->value = (def != 0);\r
+ opt->type = CheckBox;\r
+ } else if(p = strstr(opt->name, " -combo ")) {\r
+ opt->textValue = (char*) (&cps->comboList[cps->comboCnt]); // cheat with pointer type\r
+ cps->comboList[cps->comboCnt++] = q = p+8; // holds possible choices\r
+ opt->value = n = 0;\r
+ while(q = StrStr(q, " /// ")) {\r
+ n++; *q = 0; // count choices, and null-terminate each of them\r
+ q += 5;\r
+ if(*q == '*') { // remember default, which is marked with * prefix\r
+ q++;\r
+ opt->value = n;\r
+ }\r
+ cps->comboList[cps->comboCnt++] = q;\r
+ }\r
+ cps->comboList[cps->comboCnt++] = NULL;\r
+ opt->max = n + 1;\r
+ opt->type = ComboBox;\r
+ } else if(p = strstr(opt->name, " -button")) {\r
+ opt->type = Button;\r
+ } else if(p = strstr(opt->name, " -save")) {\r
+ opt->type = SaveButton;\r
+ } else return FALSE;\r
+ *p = 0; // terminate option name\r
+ return TRUE;\r
+}\r
+\r
void\r
FeatureDone(cps, val)\r
ChessProgramState* cps;\r
if (BoolFeature(&p, "memory", &cps->memSize, cps)) continue;\r
if (BoolFeature(&p, "smp", &cps->maxCores, cps)) continue;\r
if (StringFeature(&p, "egt", &cps->egtFormats, cps)) continue;\r
+ if (StringFeature(&p, "option", &(cps->option[cps->nrOptions].name), cps)) {\r
+ ParseOption(&(cps->option[cps->nrOptions++]), cps); // [HGM] options: add option feature\r
+ if(cps->nrOptions >= MAX_OPTIONS) {\r
+ cps->nrOptions--;\r
+ sprintf(buf, "%s engine has too many options\n", cps->which);\r
+ DisplayError(buf, 0);\r
+ }\r
+ continue;\r
+ }\r
+ if (BoolFeature(&p, "smp", &cps->maxCores, cps)) continue;\r
/* End of additions by HGM */\r
\r
/* unknown feature: complain and skip */\r