static int setboardSpoiledMachineBlack = 0 /*, errorExitFlag = 0*/;
int startedFromPositionFile = FALSE; Board filePosition; /* [HGM] loadPos */
Board partnerBoard; /* [HGM] bughouse: for peeking at partner game */
+int partnerHighlight[2];
+Boolean partnerBoardValid = 0;
+char partnerStatus[MSG_SIZ];
Boolean partnerUp;
+Boolean originalFlip;
+Boolean twoBoards = 0;
char endingGame = 0; /* [HGM] crash: flag to prevent recursion of GameEnds() */
int whiteNPS, blackNPS; /* [HGM] nps: for easily making clocks aware of NPS */
VariantClass currentlyInitializedVariant; /* [HGM] variantswitch */
InitChessProgram(&first, startedFromSetupPosition);
+ if(!appData.noChessProgram) { /* [HGM] tidy: redo program version to use name from myname feature */
+ free(programVersion);
+ programVersion = (char*) malloc(8 + strlen(PACKAGE_STRING) + strlen(first.tidy));
+ sprintf(programVersion, "%s + %s", PACKAGE_STRING, first.tidy);
+ }
if (appData.icsActive) {
#ifdef WIN32
}
}
+void EscapeExpand(char *p, char *q)
+{ // [HGM] initstring: routine to shape up string arguments
+ while(*p++ = *q++) if(p[-1] == '\\')
+ switch(*q++) {
+ case 'n': p[-1] = '\n'; break;
+ case 'r': p[-1] = '\r'; break;
+ case 't': p[-1] = '\t'; break;
+ case '\\': p[-1] = '\\'; break;
+ case 0: *p = 0; return;
+ default: p[-1] = q[-1]; break;
+ }
+}
+
void
show_bytes(fp, buf, count)
FILE *fp;
VariantSwitch(Board board, VariantClass newVariant)
{
int newHoldingsWidth, newWidth = 8, newHeight = 8, i, j;
- Board oldBoard;
+ static Board oldBoard;
startedFromPositionFile = FALSE;
if(gameInfo.variant == newVariant) return;
if (appData.autoKibitz && started == STARTED_NONE &&
!appData.icsEngineAnalyze && // [HGM] [DM] ICS analyze
(gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack || gameMode == IcsObserving)) {
- if(looking_at(buf, &i, "* kibitzes: ") &&
+ if((looking_at(buf, &i, "* kibitzes: ") || looking_at(buf, &i, "* whispers: ")) &&
(StrStr(star_match[0], gameInfo.white) == star_match[0] ||
StrStr(star_match[0], gameInfo.black) == star_match[0] )) { // kibitz of self or opponent
suppressKibitz = TRUE;
channel = -1;
if(started == STARTED_NONE && (looking_at(buf, &i, "* tells you:") || looking_at(buf, &i, "* says:") ||
looking_at(buf, &i, "* whispers:") ||
+ looking_at(buf, &i, "* kibitzes:") ||
+ looking_at(buf, &i, "* shouts:") ||
+ looking_at(buf, &i, "* c-shouts:") ||
+ looking_at(buf, &i, "--> * ") ||
looking_at(buf, &i, "*(*):") && (sscanf(star_match[1], "%d", &channel),1) ||
looking_at(buf, &i, "*(*)(*):") && (sscanf(star_match[2], "%d", &channel),1) ||
looking_at(buf, &i, "*(*)(*)(*):") && (sscanf(star_match[3], "%d", &channel),1) ||
for(p=0; p<MAX_CHAT; p++) {
if(channel == atoi(chatPartner[p])) {
talker[0] = '['; strcat(talker, "] ");
+ Colorize(channel == 1 ? ColorChannel1 : ColorChannel, FALSE);
chattingPartner = p; break;
}
} else
+ if(buf[i-3] == 'e') // kibitz; look if there is a KIBITZ chatbox
+ for(p=0; p<MAX_CHAT; p++) {
+ if(!strcmp("kibitzes", chatPartner[p])) {
+ talker[0] = '['; strcat(talker, "] ");
+ chattingPartner = p; break;
+ }
+ } else
if(buf[i-3] == 'r') // whisper; look if there is a WHISPER chatbox
for(p=0; p<MAX_CHAT; p++) {
- if(!strcmp("WHISPER", chatPartner[p])) {
+ if(!strcmp("whispers", chatPartner[p])) {
talker[0] = '['; strcat(talker, "] ");
chattingPartner = p; break;
}
+ } else
+ if(buf[i-3] == 't' || buf[oldi+2] == '>') {// shout, c-shout or it; look if there is a 'shouts' chatbox
+ if(buf[i-8] == '-' && buf[i-3] == 't')
+ for(p=0; p<MAX_CHAT; p++) { // c-shout; check if dedicatesd c-shout box exists
+ if(!strcmp("c-shouts", chatPartner[p])) {
+ talker[0] = '('; strcat(talker, ") "); Colorize(ColorSShout, FALSE);
+ chattingPartner = p; break;
+ }
+ }
+ if(chattingPartner < 0)
+ for(p=0; p<MAX_CHAT; p++) {
+ if(!strcmp("shouts", chatPartner[p])) {
+ if(buf[oldi+2] == '>') { talker[0] = '<'; strcat(talker, "> "); Colorize(ColorShout, FALSE); }
+ else if(buf[i-8] == '-') { talker[0] = '('; strcat(talker, ") "); Colorize(ColorSShout, FALSE); }
+ else { talker[0] = '['; strcat(talker, "] "); Colorize(ColorShout, FALSE); }
+ chattingPartner = p; break;
+ }
+ }
}
if(chattingPartner<0) // if not, look if there is a chatbox for this indivdual
for(p=0; p<MAX_CHAT; p++) if(!StrCaseCmp(talker+1, chatPartner[p])) {
- talker[0] = 0;
+ talker[0] = 0; Colorize(ColorTell, FALSE);
chattingPartner = p; break;
}
if(chattingPartner<0) i = oldi; else {
+ Colorize(curColor, TRUE); // undo the bogus colorations we just made to trigger the souds
if(oldi > 0 && buf[oldi-1] == '\n') oldi--;
if (oldi > next_out) SendToPlayer(&buf[next_out], oldi - next_out);
started = STARTED_COMMENT;
ZippyGameStart(whitename, blackname);
}
#endif /*ZIPPY*/
+ partnerBoardValid = FALSE; // [HGM] bughouse
continue;
}
Reset(TRUE, TRUE);
}
#endif /*ZIPPY*/
+ if(appData.bgObserve && partnerBoardValid) DrawPosition(TRUE, partnerBoard);
continue;
}
gameInfo.white, white_holding,
gameInfo.black, black_holding);
}
-
+ if(!partnerUp) // [HGM] bughouse: when peeking at partner game we already know what he captured...
DrawPosition(FALSE, boards[currentMove]);
DisplayTitle(str);
} else if(appData.bgObserve) { // [HGM] bughouse: holdings of other game => background
/* [HGM] copy holdings to partner-board holdings area */
CopyHoldings(partnerBoard, white_holding, WhitePawn);
CopyHoldings(partnerBoard, black_holding, BlackPawn);
+ if(twoBoards) { partnerUp = 1; flipView = !flipView; } // [HGM] dual: always draw
if(partnerUp) DrawPosition(FALSE, partnerBoard);
+ if(twoBoards) { partnerUp = 0; flipView = !flipView; }
}
}
/* Suppress following prompt */
}
if((gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack)
- && newGameMode == IcsObserving && appData.bgObserve) {
+ && newGameMode == IcsObserving && gamenum != ics_gamenum && appData.bgObserve) {
// [HGM] bughouse: don't act on alien boards while we play. Just parse the board and save it */
- char buf[MSG_SIZ];
+ char *toSqr;
for (k = 0; k < ranks; k++) {
for (j = 0; j < files; j++)
board[k][j+gameInfo.holdingsWidth] = CharToPiece(board_chars[(ranks-1-k)*(files+1) + j]);
}
}
CopyBoard(partnerBoard, board);
+ if(toSqr = strchr(str, '/')) { // extract highlights from long move
+ partnerBoard[EP_STATUS-3] = toSqr[1] - AAA; // kludge: hide highlighting info in board
+ partnerBoard[EP_STATUS-4] = toSqr[2] - ONE;
+ } else partnerBoard[EP_STATUS-4] = partnerBoard[EP_STATUS-3] = -1;
+ if(toSqr = strchr(str, '-')) {
+ partnerBoard[EP_STATUS-1] = toSqr[1] - AAA;
+ partnerBoard[EP_STATUS-2] = toSqr[2] - ONE;
+ } else partnerBoard[EP_STATUS-1] = partnerBoard[EP_STATUS-2] = -1;
+ if(appData.dualBoard && !twoBoards) { twoBoards = 1; InitDrawingSizes(-2,0); }
+ if(twoBoards) { partnerUp = 1; flipView = !flipView; } // [HGM] dual
if(partnerUp) DrawPosition(FALSE, partnerBoard);
- sprintf(buf, "W: %d:%d B: %d:%d (%d-%d) %c", white_time/60000, (white_time%60000)/1000,
+ if(twoBoards) { partnerUp = 0; flipView = !flipView; } // [HGM] dual
+ sprintf(partnerStatus, "W: %d:%02d B: %d:%02d (%d-%d) %c", white_time/60000, (white_time%60000)/1000,
(black_time/60000), (black_time%60000)/1000, white_stren, black_stren, to_play);
- DisplayMessage(buf, "");
+ DisplayMessage(partnerStatus, "");
+ partnerBoardValid = TRUE;
return;
}
ClearPremoveHighlights();
j = seekGraphUp; seekGraphUp = FALSE; // [HGM] seekgraph: when we draw a board, it overwrites the seek graph
+ if(partnerUp) { flipView = originalFlip; partnerUp = FALSE; j = TRUE; } // [HGM] bughouse: restore view
DrawPosition(j, boards[currentMove]);
DisplayMove(moveNum - 1);
}
void
+UploadGameEvent()
+{ // [HGM] upload: send entire stored game to ICS as long-algebraic moves.
+ int i, last = forwardMostMove; // make sure ICS reply cannot pre-empt us by clearing fmm
+ static char *castlingStrings[4] = { "none", "kside", "qside", "both" };
+ if(gameMode == IcsObserving || gameMode == IcsPlayingBlack || gameMode == IcsPlayingWhite) {
+ DisplayError("You cannot do this while you are playing or observing", 0);
+ return;
+ }
+ if(gameMode != IcsExamining) { // is this ever not the case?
+ char buf[MSG_SIZ], *p, *fen, command[MSG_SIZ], bsetup = 0;
+
+ if(ics_type == ICS_ICC) { // on ICC match ourselves in applicable variant
+ sprintf(command, "match %s", ics_handle);
+ } else { // on FICS we must first go to general examine mode
+ strcpy(command, "examine\nbsetup"); // and specify variant within it with bsetups
+ }
+ if(gameInfo.variant != VariantNormal) {
+ // try figure out wild number, as xboard names are not always valid on ICS
+ for(i=1; i<=36; i++) {
+ sprintf(buf, "wild/%d", i);
+ if(StringToVariant(buf) == gameInfo.variant) break;
+ }
+ if(i<=36 && ics_type == ICS_ICC) sprintf(buf, "%s w%d\n", command, i);
+ else if(i == 22) sprintf(buf, "%s fr\n", command);
+ else sprintf(buf, "%s %s\n", command, VariantName(gameInfo.variant));
+ } else sprintf(buf, "%s\n", ics_type == ICS_ICC ? command : "examine\n"); // match yourself or examine
+ SendToICS(ics_prefix);
+ SendToICS(buf);
+ if(startedFromSetupPosition || backwardMostMove != 0) {
+ fen = PositionToFEN(backwardMostMove, NULL);
+ if(ics_type == ICS_ICC) { // on ICC we can simply send a complete FEN to set everything
+ sprintf(buf, "loadfen %s\n", fen);
+ SendToICS(buf);
+ } else { // FICS: everything has to set by separate bsetup commands
+ p = strchr(fen, ' '); p[0] = NULLCHAR; // cut after board
+ sprintf(buf, "bsetup fen %s\n", fen);
+ SendToICS(buf);
+ if(!WhiteOnMove(backwardMostMove)) {
+ SendToICS("bsetup tomove black\n");
+ }
+ i = (strchr(p+3, 'K') != NULL) + 2*(strchr(p+3, 'Q') != NULL);
+ sprintf(buf, "bsetup wcastle %s\n", castlingStrings[i]);
+ SendToICS(buf);
+ i = (strchr(p+3, 'k') != NULL) + 2*(strchr(p+3, 'q') != NULL);
+ sprintf(buf, "bsetup bcastle %s\n", castlingStrings[i]);
+ SendToICS(buf);
+ i = boards[backwardMostMove][EP_STATUS];
+ if(i >= 0) { // set e.p.
+ sprintf(buf, "bsetup eppos %c\n", i+AAA);
+ SendToICS(buf);
+ }
+ bsetup++;
+ }
+ }
+ if(bsetup || ics_type != ICS_ICC && gameInfo.variant != VariantNormal)
+ SendToICS("bsetup done\n"); // switch to normal examining.
+ }
+ for(i = backwardMostMove; i<last; i++) {
+ char buf[20];
+ sprintf(buf, "%s\n", parseList[i]);
+ SendToICS(buf);
+ }
+ SendToICS(ics_prefix);
+ SendToICS(ics_type == ICS_ICC ? "tag result Game in progress\n" : "commit\n");
+}
+
+void
CoordsToComputerAlgebraic(rf, ff, rt, ft, promoChar, move)
int rf, ff, rt, ft;
char promoChar;
}
}
+char yy_textstr[8000];
+
/* Parser for moves from gnuchess, ICS, or user typein box */
Boolean
ParseOneMove(move, moveNum, moveType, fromX, fromY, toX, toY, promoChar)
if (appData.debugMode) {
fprintf(debugFP, "move to parse: %s\n", move);
}
- *moveType = yylexstr(moveNum, move);
+ *moveType = yylexstr(moveNum, move, yy_textstr, sizeof yy_textstr);
switch (*moveType) {
case WhitePromotionChancellor:
void
-ParsePV(char *pv)
+ParsePV(char *pv, Boolean storeComments)
{ // Parse a string of PV moves, and append to current game, behind forwardMostMove
int fromX, fromY, toX, toY; char promoChar;
ChessMove moveType;
endPV = forwardMostMove;
do {
- while(*pv == ' ') pv++;
- if(*pv == '(') pv++; // first (ponder) move can be in parentheses
+ while(*pv == ' ' || *pv == '\n' || *pv == '\t') pv++; // must still read away whitespace
+ if(nr == 0 && !storeComments && *pv == '(') pv++; // first (ponder) move can be in parentheses
valid = ParseOneMove(pv, endPV, &moveType, &fromX, &fromY, &toX, &toY, &promoChar);
if(appData.debugMode){
-fprintf(debugFP,"parsePV: %d %c%c%c%c '%s'\n", valid, fromX+AAA, fromY+ONE, toX+AAA, toY+ONE, pv);
+fprintf(debugFP,"parsePV: %d %c%c%c%c yy='%s'\nPV = '%s'\n", valid, fromX+AAA, fromY+ONE, toX+AAA, toY+ONE, yy_textstr, pv);
}
if(!valid && nr == 0 &&
ParseOneMove(pv, endPV-1, &moveType, &fromX, &fromY, &toX, &toY, &promoChar)){
strcpy(moveList[endPV-2], "_0_0"); // suppress premove highlight on takeback move
}
}
+ pv = strstr(pv, yy_textstr) + strlen(yy_textstr); // skip what we parsed
+ if(nr == 0 && !storeComments && *pv == ')') pv++; // closing parenthesis of ponder move;
+ if(moveType == Comment && storeComments) AppendComment(endPV, yy_textstr, FALSE);
+ if(moveType == Comment || moveType == NAG || moveType == ElapsedTime) {
+ valid++; // allow comments in PV
+ continue;
}
- while(*pv && *pv++ != ' '); // skip what we parsed; assume space separators
- if(moveType == Comment) { valid++; continue; } // allow comments in PV
nr++;
if(endPV+1 > framePtr) break; // no space, truncate
if(!valid) break;
moveList[endPV-1][1] = fromY + ONE;
moveList[endPV-1][2] = toX + AAA;
moveList[endPV-1][3] = toY + ONE;
- parseList[endPV-1][0] = NULLCHAR;
+ if(storeComments)
+ CoordsToAlgebraic(boards[endPV - 1],
+ PosFlags(endPV - 1),
+ fromY, fromX, toY, toX, promoChar,
+ parseList[endPV - 1]);
+ else
+ parseList[endPV-1][0] = NULLCHAR;
} while(valid);
currentMove = endPV;
if(currentMove == forwardMostMove) ClearPremoveHighlights(); else
LoadMultiPV(int x, int y, char *buf, int index, int *start, int *end)
{
int startPV;
+ char *p;
if(index < 0 || index >= strlen(buf)) return FALSE; // sanity
lastX = x; lastY = y;
while(index > 0 && buf[index-1] != '\n') index--; // beginning of line
startPV = index;
- while(buf[index] != '\n') if(buf[index++] == '\t') startPV = index;
- index = startPV;
- while(buf[index] && buf[index] != '\n') index++;
+ while(buf[index] != '\n') if(buf[index++] == '\t') startPV = index;
+ if(index == startPV && (p = StrCaseStr(buf+index, "PV="))) startPV = p - buf + 3;
+ index = startPV;
+ do{ while(buf[index] && buf[index] != '\n') index++;
+ } while(buf[index] == '\n' && buf[index+1] == '\\' && buf[index+2] == ' ' && index++); // join kibitzed PV continuation line
buf[index] = 0;
- ParsePV(buf+startPV);
+ ParsePV(buf+startPV, FALSE);
*start = startPV; *end = index-1;
return TRUE;
}
{ // called on right mouse click to load PV
int which = gameMode == TwoMachinesPlay && (WhiteOnMove(forwardMostMove) == (second.twoMachinesColor[0] == 'w'));
lastX = x; lastY = y;
- ParsePV(lastPV[which]); // load the PV of the thinking engine in the boards array.
+ ParsePV(lastPV[which], FALSE); // load the PV of the thinking engine in the boards array.
return TRUE;
}
if((gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack)
&& !appData.zippyPlay && appData.bgObserve) { // [HGM] bughouse: show background game
- if(action == Press) { flipView = !flipView; DrawPosition(TRUE, partnerBoard); partnerUp = TRUE; } else
- if(action == Release) { flipView = !flipView; DrawPosition(TRUE, boards[currentMove]); partnerUp = FALSE; }
+ if(!partnerBoardValid) return -2; // suppress display of uninitialized boards
+ if( appData.dualBoard) return -2; // [HGM] dual: is already displayed
+ if(action == Press) {
+ originalFlip = flipView;
+ flipView = !flipView; // temporarily flip board to see game from partners perspective
+ DrawPosition(TRUE, partnerBoard);
+ DisplayMessage(partnerStatus, "");
+ partnerUp = TRUE;
+ } else if(action == Release) {
+ flipView = originalFlip;
+ DrawPosition(TRUE, boards[currentMove]);
+ partnerUp = FALSE;
+ }
return -2;
}
|| NrWN==2 || NrBN==2 /* KNNK */
|| NrWN+NrWB == 1 && NrBN+NrBB == 1 /* KBKN, KBKB, KNKN */
) ) {
- if(canAdjudicate && --moveCount < 0 && appData.trivialDraws)
+ if(--moveCount < 0 && appData.trivialDraws && canAdjudicate)
{ /* if the first 3 moves do not show a tactical win, declare draw */
if(engineOpponent) {
SendToProgram("force\n", engineOpponent); // suppress reply
boards[forwardMostMove][CASTLING][4] != boards[k][CASTLING][4] )
rights++;
}
- if( canAdjudicate && rights == 0 && ++count > appData.drawRepeats-2
+ if( rights == 0 && ++count > appData.drawRepeats-2 && canAdjudicate
&& appData.drawRepeats > 1) {
/* adjudicate after user-specified nr of repeats */
- if(engineOpponent) {
- SendToProgram("force\n", engineOpponent); // suppress reply
- SendMoveToProgram(forwardMostMove-1, engineOpponent); /* make sure opponent gets to see move */
- }
- ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/
+ int result = GameIsDrawn;
+ char *details = "XBoard adjudication: repetition draw";
if(gameInfo.variant == VariantXiangqi && appData.testLegality) {
// [HGM] xiangqi: check for forbidden perpetuals
int m, ourPerpetual = 1, hisPerpetual = 1;
if(appData.debugMode) fprintf(debugFP, "XQ perpetual test, our=%d, his=%d\n",
ourPerpetual, hisPerpetual);
if(ourPerpetual && !hisPerpetual) { // we are actively checking him: forfeit
- GameEnds( WhiteOnMove(forwardMostMove) ? WhiteWins : BlackWins,
- "Xboard adjudication: perpetual checking", GE_XBOARD );
- return 1;
- }
- if(hisPerpetual && !ourPerpetual) // he is checking us, but did not repeat yet
+ result = WhiteOnMove(forwardMostMove) ? WhiteWins : BlackWins;
+ details = "Xboard adjudication: perpetual checking";
+ } else
+ if(hisPerpetual && !ourPerpetual) { // he is checking us, but did not repeat yet
break; // (or we would have caught him before). Abort repetition-checking loop.
+ } else
// Now check for perpetual chases
if(!ourPerpetual && !hisPerpetual) { // no perpetual check, test for chase
hisPerpetual = PerpetualChase(k, forwardMostMove);
ourPerpetual = PerpetualChase(k+1, forwardMostMove);
if(ourPerpetual && !hisPerpetual) { // we are actively chasing him: forfeit
- GameEnds( WhiteOnMove(forwardMostMove) ? WhiteWins : BlackWins,
- "Xboard adjudication: perpetual chasing", GE_XBOARD );
- return 1;
- }
+ result = WhiteOnMove(forwardMostMove) ? WhiteWins : BlackWins;
+ details = "Xboard adjudication: perpetual chasing";
+ } else
if(hisPerpetual && !ourPerpetual) // he is chasing us, but did not repeat yet
break; // Abort repetition-checking loop.
}
// if neither of us is checking or chasing all the time, or both are, it is draw
}
- GameEnds( GameIsDrawn, "Xboard adjudication: repetition draw", GE_XBOARD );
+ if(engineOpponent) {
+ SendToProgram("force\n", engineOpponent); // suppress reply
+ SendMoveToProgram(forwardMostMove-1, engineOpponent); /* make sure opponent gets to see move */
+ }
+ ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/
+ GameEnds( result, details, GE_XBOARD );
return 1;
}
if( rights == 0 && count > 1 ) /* occurred 2 or more times before */
// after a book hit we never send 'go', and the code after the call to this routine
// has '&& !bookHit' added to suppress potential sending there (based on 'firstMove').
char buf[MSG_SIZ];
- if (cps->useUsermove) sprintf(buf, "usermove "); // sorry, no SAN yet :(
- sprintf(buf, "%s\n", bookHit); // force book move into program supposed to play it
+ sprintf(buf, "%s%s\n", (cps->useUsermove ? "usermove " : ""), bookHit); // force book move into program supposed to play it
SendToProgram(buf, cps);
if(!initial) firstMove = FALSE; // normally we would clear the firstMove condition after return & sending 'go'
} else if(initial) { // 'go' was needed irrespective of firstMove, and it has to be done in this routine
* Look for communication commands
*/
if (!strncmp(message, "telluser ", 9)) {
+ EscapeExpand(message+9, message+9); // [HGM] esc: allow escape sequences in popup box
DisplayNote(message + 9);
return;
}
if (!strncmp(message, "tellusererror ", 14)) {
cps->userError = 1;
+ EscapeExpand(message+14, message+14); // [HGM] esc: allow escape sequences in popup box
DisplayError(message + 14, 0);
return;
}
}
if (!ignore) {
+ ChessProgramStats tempStats = programStats; // [HGM] info: filter out info lines
buf1[0] = NULLCHAR;
if (sscanf(message, "%d%c %d %d " u64Display " %[^\n]\n",
&plylev, &plyext, &curscore, &time, &nodes, buf1) >= 5) {
}
- programStats.depth = plylev;
- programStats.nodes = nodes;
- programStats.time = time;
- programStats.score = curscore;
- programStats.got_only_move = 0;
+ tempStats.depth = plylev;
+ tempStats.nodes = nodes;
+ tempStats.time = time;
+ tempStats.score = curscore;
+ tempStats.got_only_move = 0;
if(cps->nps >= 0) { /* [HGM] nps: use engine nodes or time to decrement clock */
int ticklen;
/* Buffer overflow protection */
if (buf1[0] != NULLCHAR) {
- if (strlen(buf1) >= sizeof(programStats.movelist)
+ if (strlen(buf1) >= sizeof(tempStats.movelist)
&& appData.debugMode) {
fprintf(debugFP,
"PV is too long; using the first %u bytes.\n",
- (unsigned) sizeof(programStats.movelist) - 1);
+ (unsigned) sizeof(tempStats.movelist) - 1);
}
- safeStrCpy( programStats.movelist, buf1, sizeof(programStats.movelist) );
+ safeStrCpy( tempStats.movelist, buf1, sizeof(tempStats.movelist) );
} else {
- sprintf(programStats.movelist, " no PV\n");
+ sprintf(tempStats.movelist, " no PV\n");
}
- if (programStats.seen_stat) {
- programStats.ok_to_send = 1;
+ if (tempStats.seen_stat) {
+ tempStats.ok_to_send = 1;
}
- if (strchr(programStats.movelist, '(') != NULL) {
- programStats.line_is_book = 1;
- programStats.nr_moves = 0;
- programStats.moves_left = 0;
+ if (strchr(tempStats.movelist, '(') != NULL) {
+ tempStats.line_is_book = 1;
+ tempStats.nr_moves = 0;
+ tempStats.moves_left = 0;
} else {
- programStats.line_is_book = 0;
+ tempStats.line_is_book = 0;
}
- SendProgramStatsToFrontend( cps, &programStats );
+ if(tempStats.score != 0 || tempStats.nodes != 0 || tempStats.time != 0)
+ programStats = tempStats; // [HGM] info: only set stats if genuine PV and not an info line
+
+ SendProgramStatsToFrontend( cps, &tempStats );
/*
[AS] Protect the thinkOutput buffer from overflow... this
if(endingGame) return; /* [HGM] crash: forbid recursion */
endingGame = 1;
-
+ if(twoBoards) { // [HGM] dual: switch back to one board
+ twoBoards = partnerUp = 0; InitDrawingSizes(-2, 0);
+ DrawPosition(TRUE, partnerBoard); // observed game becomes foreground
+ }
if (appData.debugMode) {
fprintf(debugFP, "GameEnds(%d, %s, %d)\n",
result, resultDetails ? resultDetails : "(null)", whosays);
if (numPGNTags > 0){
char *tags;
if (gameInfo.variant == VariantNormal) {
- gameInfo.variant = StringToVariant(gameInfo.event);
+ VariantClass v = StringToVariant(gameInfo.event);
+ // [HGM] do not recognize variants from event tag that were introduced after supporting variant tag
+ if(v < VariantShogi) gameInfo.variant = v;
}
if (!matchMode) {
if( appData.autoDisplayTags ) {
}
void
-RevertEvent()
+RevertEvent(Boolean annotate)
{
- if(PopTail(TRUE)) { // [HGM] vari: restore old game tail
+ if(PopTail(annotate)) { // [HGM] vari: restore old game tail
return;
}
if (gameMode != IcsExamining) {
sscanf(message, "error %c", &c)!=1 && sscanf(message, "illegal %c", &c)!=1 &&
sscanf(message, "tell%c", &c)!=1 && sscanf(message, "0-1 %c", &c)!=1 &&
sscanf(message, "1-0 %c", &c)!=1 && sscanf(message, "1/2-1/2 %c", &c)!=1 &&
- sscanf(message, "pong %c", &c)!=1 && start != '#')
- { quote = "# "; print = (appData.engineComments == 2); }
+ sscanf(message, "pong %c", &c)!=1 && start != '#') {
+ quote = appData.engineComments == 2 ? "# " : "### NON-COMPLIANT! ### ";
+ print = (appData.engineComments >= 2);
+ }
message[0] = start; // restore original message
}
if(print) {
}
storedGames++;
- forwardMostMove = currentMove; // truncte game so we can start variation
+ forwardMostMove = firstMove; // truncate game so we can start variation
if(storedGames == 1) GreyRevert(FALSE);
}
if(appData.icsActive) return FALSE; // only in local mode
if(!storedGames) return FALSE; // sanity
+ CommentPopDown(); // make sure no stale variation comments to the destroyed line can remain open
storedGames--;
ToNrEvent(savedFirst[storedGames]); // sets currentMove
sprintf(moveBuf, " %d. %s", i+2>>1, SavePart(parseList[i]));
else sprintf(moveBuf, " %s", SavePart(parseList[i]));
strcat(buf, moveBuf);
+ if(commentList[i]) { strcat(buf, " "); strcat(buf, commentList[i]); }
if(!--cnt) { strcat(buf, "\n"); cnt = 10; }
}
strcat(buf, ")");
}
- for(i=1; i<nrMoves; i++) { // copy last variation back
+ for(i=1; i<=nrMoves; i++) { // copy last variation back
CopyBoard(boards[currentMove+i], boards[framePtr+i]);
for(j=0; j<MOVE_LEN; j++)
moveList[currentMove+i-1][j] = moveList[framePtr+i][j];
framePtr = MAX_MOVES-1;
storedGames = 0;
}
+
+void
+LoadVariation(int index, char *text)
+{ // [HGM] vari: shelve previous line and load new variation, parsed from text around text[index]
+ char *p = text, *start = NULL, *end = NULL, wait = NULLCHAR;
+ int level = 0, move;
+
+ if(gameMode != EditGame && gameMode != AnalyzeMode) return;
+ // first find outermost bracketing variation
+ while(*p) { // hope I got this right... Non-nesting {} and [] can screen each other and nesting ()
+ if(!wait) { // while inside [] pr {}, ignore everyting except matching closing ]}
+ if(*p == '{') wait = '}'; else
+ if(*p == '[') wait = ']'; else
+ if(*p == '(' && level++ == 0 && p-text < index) start = p+1;
+ if(*p == ')' && level > 0 && --level == 0 && p-text > index && end == NULL) end = p-1;
+ }
+ if(*p == wait) wait = NULLCHAR; // closing ]} found
+ p++;
+ }
+ if(!start || !end) return; // no variation found, or syntax error in PGN: ignore click
+ if(appData.debugMode) fprintf(debugFP, "at move %d load variation '%s'\n", currentMove, start);
+ end[1] = NULLCHAR; // clip off comment beyond variation
+ ToNrEvent(currentMove-1);
+ PushTail(currentMove, forwardMostMove); // shelve main variation. This truncates game
+ // kludge: use ParsePV() to append variation to game
+ move = currentMove;
+ ParsePV(start, TRUE);
+ forwardMostMove = endPV; endPV = -1; currentMove = move; // cleanup what ParsePV did
+ ClearPremoveHighlights();
+ CommentPopDown();
+ ToNrEvent(currentMove+1);
+}
+