* Massachusetts.
*
* Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
- * 2007, 2008, 2009 Free Software Foundation, Inc.
+ * 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
*
* Enhancements Copyright 2005 Alessandro Scotti
*
void ParseBoard12 P((char *string));
void KeepAlive P((void));
void StartClocks P((void));
-void SwitchClocks P((void));
+void SwitchClocks P((int nr));
void StopClocks P((void));
void ResetClocks P((void));
char *PGNDate P((void));
static int exiting = 0; /* [HGM] moved to top */
static int setboardSpoiledMachineBlack = 0 /*, errorExitFlag = 0*/;
int startedFromPositionFile = FALSE; Board filePosition; /* [HGM] loadPos */
+Board partnerBoard; /* [HGM] bughouse: for peeking at partner game */
+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 */
ColorClass curColor = ColorNormal;
int suppressKibitz = 0;
+// [HGM] seekgraph
+Boolean soughtPending = FALSE;
+Boolean seekGraphUp;
+#define MAX_SEEK_ADS 200
+#define SQUARE 0x80
+char *seekAdList[MAX_SEEK_ADS];
+int ratingList[MAX_SEEK_ADS], xList[MAX_SEEK_ADS], yList[MAX_SEEK_ADS], seekNrList[MAX_SEEK_ADS], zList[MAX_SEEK_ADS];
+float tcList[MAX_SEEK_ADS];
+char colorList[MAX_SEEK_ADS];
+int nrOfSeekAds = 0;
+int minRating = 1010, maxRating = 2800;
+int hMargin = 10, vMargin = 20, h, w;
+extern int squareSize, lineGap;
+
+void
+PlotSeekAd(int i)
+{
+ int x, y, color = 0, r = ratingList[i]; float tc = tcList[i];
+ xList[i] = yList[i] = -100; // outside graph, so cannot be clicked
+ if(r < minRating+100 && r >=0 ) r = minRating+100;
+ if(r > maxRating) r = maxRating;
+ if(tc < 1.) tc = 1.;
+ if(tc > 95.) tc = 95.;
+ x = (w-hMargin-squareSize/8-7)* log(tc)/log(95.) + hMargin;
+ y = ((double)r - minRating)/(maxRating - minRating)
+ * (h-vMargin-squareSize/8-1) + vMargin;
+ if(ratingList[i] < 0) y = vMargin + squareSize/4;
+ if(strstr(seekAdList[i], " u ")) color = 1;
+ if(!strstr(seekAdList[i], "lightning") && // for now all wilds same color
+ !strstr(seekAdList[i], "bullet") &&
+ !strstr(seekAdList[i], "blitz") &&
+ !strstr(seekAdList[i], "standard") ) color = 2;
+ if(strstr(seekAdList[i], "(C) ")) color |= SQUARE; // plot computer seeks as squares
+ DrawSeekDot(xList[i]=x+3*(color&~SQUARE), yList[i]=h-1-y, colorList[i]=color);
+}
+
+void
+AddAd(char *handle, char *rating, int base, int inc, char rated, char *type, int nr, Boolean plot)
+{
+ char buf[MSG_SIZ], *ext = "";
+ VariantClass v = StringToVariant(type);
+ if(strstr(type, "wild")) {
+ ext = type + 4; // append wild number
+ if(v == VariantFischeRandom) type = "chess960"; else
+ if(v == VariantLoadable) type = "setup"; else
+ type = VariantName(v);
+ }
+ sprintf(buf, "%s (%s) %d %d %c %s%s", handle, rating, base, inc, rated, type, ext);
+ if(nrOfSeekAds < MAX_SEEK_ADS-1) {
+ if(seekAdList[nrOfSeekAds]) free(seekAdList[nrOfSeekAds]);
+ ratingList[nrOfSeekAds] = -1; // for if seeker has no rating
+ sscanf(rating, "%d", &ratingList[nrOfSeekAds]);
+ tcList[nrOfSeekAds] = base + (2./3.)*inc;
+ seekNrList[nrOfSeekAds] = nr;
+ zList[nrOfSeekAds] = 0;
+ seekAdList[nrOfSeekAds++] = StrSave(buf);
+ if(plot) PlotSeekAd(nrOfSeekAds-1);
+ }
+}
+
+void
+EraseSeekDot(int i)
+{
+ int x = xList[i], y = yList[i], d=squareSize/4, k;
+ DrawSeekBackground(x-squareSize/8, y-squareSize/8, x+squareSize/8+1, y+squareSize/8+1);
+ if(x < hMargin+d) DrawSeekAxis(hMargin, y-squareSize/8, hMargin, y+squareSize/8+1);
+ // now replot every dot that overlapped
+ for(k=0; k<nrOfSeekAds; k++) if(k != i) {
+ int xx = xList[k], yy = yList[k];
+ if(xx <= x+d && xx > x-d && yy <= y+d && yy > y-d)
+ DrawSeekDot(xx, yy, colorList[k]);
+ }
+}
+
+void
+RemoveSeekAd(int nr)
+{
+ int i;
+ for(i=0; i<nrOfSeekAds; i++) if(seekNrList[i] == nr) {
+ EraseSeekDot(i);
+ if(seekAdList[i]) free(seekAdList[i]);
+ seekAdList[i] = seekAdList[--nrOfSeekAds];
+ seekNrList[i] = seekNrList[nrOfSeekAds];
+ ratingList[i] = ratingList[nrOfSeekAds];
+ colorList[i] = colorList[nrOfSeekAds];
+ tcList[i] = tcList[nrOfSeekAds];
+ xList[i] = xList[nrOfSeekAds];
+ yList[i] = yList[nrOfSeekAds];
+ zList[i] = zList[nrOfSeekAds];
+ seekAdList[nrOfSeekAds] = NULL;
+ break;
+ }
+}
+
+Boolean
+MatchSoughtLine(char *line)
+{
+ char handle[MSG_SIZ], rating[MSG_SIZ], type[MSG_SIZ];
+ int nr, base, inc, u=0; char dummy;
+
+ if(sscanf(line, "%d %s %s %d %d rated %s", &nr, rating, handle, &base, &inc, type) == 6 ||
+ sscanf(line, "%d %s %s %s %d %d rated %c", &nr, rating, handle, type, &base, &inc, &dummy) == 7 ||
+ (u=1) &&
+ (sscanf(line, "%d %s %s %d %d unrated %s", &nr, rating, handle, &base, &inc, type) == 6 ||
+ sscanf(line, "%d %s %s %s %d %d unrated %c", &nr, rating, handle, type, &base, &inc, &dummy) == 7) ) {
+ // match: compact and save the line
+ AddAd(handle, rating, base, inc, u ? 'u' : 'r', type, nr, FALSE);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+int
+DrawSeekGraph()
+{
+ if(!seekGraphUp) return FALSE;
+ int i;
+ h = BOARD_HEIGHT * (squareSize + lineGap) + lineGap;
+ w = BOARD_WIDTH * (squareSize + lineGap) + lineGap;
+
+ DrawSeekBackground(0, 0, w, h);
+ DrawSeekAxis(hMargin, h-1-vMargin, w-5, h-1-vMargin);
+ DrawSeekAxis(hMargin, h-1-vMargin, hMargin, 5);
+ for(i=0; i<4000; i+= 100) if(i>=minRating && i<maxRating) {
+ int yy =((double)i - minRating)/(maxRating - minRating)*(h-vMargin-squareSize/8-1) + vMargin;
+ yy = h-1-yy;
+ DrawSeekAxis(hMargin+5*(i%500==0), yy, hMargin-5, yy); // rating ticks
+ if(i%500 == 0) {
+ char buf[MSG_SIZ];
+ sprintf(buf, "%d", i);
+ DrawSeekText(buf, hMargin+squareSize/8+7, yy);
+ }
+ }
+ DrawSeekText("unrated", hMargin+squareSize/8+7, h-1-vMargin-squareSize/4);
+ for(i=1; i<100; i+=(i<10?1:5)) {
+ int xx = (w-hMargin-squareSize/8-7)* log((double)i)/log(95.) + hMargin;
+ DrawSeekAxis(xx, h-1-vMargin, xx, h-6-vMargin-3*(i%10==0)); // TC ticks
+ if(i<=5 || (i>40 ? i%20 : i%10) == 0) {
+ char buf[MSG_SIZ];
+ sprintf(buf, "%d", i);
+ DrawSeekText(buf, xx-2-3*(i>9), h-1-vMargin/2);
+ }
+ }
+ for(i=0; i<nrOfSeekAds; i++) PlotSeekAd(i);
+ return TRUE;
+}
+
+int SeekGraphClick(ClickType click, int x, int y, int moving)
+{
+ static int lastDown = 0, displayed = 0, lastSecond;
+ if(!seekGraphUp) { // initiate cration of seek graph by requesting seek-ad list
+ if(click == Release || moving) return FALSE;
+ nrOfSeekAds = 0;
+ soughtPending = TRUE;
+ SendToICS(ics_prefix);
+ SendToICS("sought\n"); // should this be "sought all"?
+ } else { // issue challenge based on clicked ad
+ int dist = 10000; int i, closest = 0, second = 0;
+ for(i=0; i<nrOfSeekAds; i++) {
+ int d = (x-xList[i])*(x-xList[i]) + (y-yList[i])*(y-yList[i]) + zList[i];
+ if(d < dist) { dist = d; closest = i; }
+ second += (d - zList[i] < 120); // count in-range ads
+ if(click == Press && moving != 1 && zList[i]>0) zList[i] *= 0.8; // age priority
+ }
+ if(dist < 120) {
+ char buf[MSG_SIZ];
+ second = (second > 1);
+ if(displayed != closest || second != lastSecond) {
+ DisplayMessage(second ? "!" : "", seekAdList[closest]);
+ lastSecond = second; displayed = closest;
+ }
+ if(click == Press) {
+ if(moving == 2) zList[closest] = 100; // right-click; push to back on press
+ lastDown = closest;
+ return TRUE;
+ } // on press 'hit', only show info
+ if(moving == 2) return TRUE; // ignore right up-clicks on dot
+ sprintf(buf, "play %d\n", seekNrList[closest]);
+ SendToICS(ics_prefix);
+ SendToICS(buf);
+ return TRUE; // let incoming board of started game pop down the graph
+ } else if(click == Release) { // release 'miss' is ignored
+ zList[lastDown] = 100; // make future selection of the rejected ad more difficult
+ if(moving == 2) { // right up-click
+ nrOfSeekAds = 0; // refresh graph
+ soughtPending = TRUE;
+ SendToICS(ics_prefix);
+ SendToICS("sought\n"); // should this be "sought all"?
+ }
+ return TRUE;
+ } else if(moving) { if(displayed >= 0) DisplayMessage("", ""); displayed = -1; return TRUE; }
+ // press miss or release hit 'pop down' seek graph
+ seekGraphUp = FALSE;
+ DrawPosition(TRUE, NULL);
+ }
+ return TRUE;
+}
+
void
read_from_ics(isr, closure, data, count, error)
InputSourceRef isr;
sprintf(str,
"/set-quietly interface %s\n/set-quietly style 12\n",
programVersion);
+ if(appData.seekGraph && appData.autoRefresh) // [HGM] seekgraph
+ strcat(str, "/set-2 51 1\n/set seek 1\n");
} else if (ics_type == ICS_CHESSNET) {
sprintf(str, "/style 12\n");
} else {
strcpy(str, "alias $ @\n$set interface ");
strcat(str, programVersion);
strcat(str, "\n$iset startpos 1\n$iset ms 1\n");
+ if(appData.seekGraph && appData.autoRefresh) // [HGM] seekgraph
+ strcat(str, "$iset seekremove 1\n$set seek 1\n");
#ifdef WIN32
strcat(str, "$iset nohighlight 1\n");
#endif
sprintf(mess, "%s%s", talker, parse);
OutputChatMessage(chattingPartner, mess);
chattingPartner = -1;
+ next_out = i+1; // [HGM] suppress printing in ICS window
} else
if(!suppressKibitz) // [HGM] kibitz
AppendComment(forwardMostMove, StripHighlight(parse), TRUE);
pvInfoList[forwardMostMove-1].score = 100*score;
}
OutputKibitz(suppressKibitz, parse);
- next_out = i+1; // [HGM] suppress printing in ICS window
} else {
char tmp[MSG_SIZ];
sprintf(tmp, _("your opponent kibitzes: %s"), parse);
SendToPlayer(tmp, strlen(tmp));
}
+ next_out = i+1; // [HGM] suppress printing in ICS window
}
started = STARTED_NONE;
} else {
continue;
}
started = STARTED_NONE;
+ if(suppressKibitz) next_out = i+1;
}
/* Kludge to deal with rcmd protocol */
continue;
}
+ oldi = i;
+ // [HGM] seekgraph: recognize sought lines and end-of-sought message
+ if(appData.seekGraph) {
+ if(soughtPending && MatchSoughtLine(buf+i)) {
+ i = strstr(buf+i, "rated") - buf;
+ if (oldi > next_out) SendToPlayer(&buf[next_out], oldi - next_out);
+ next_out = leftover_start = i;
+ started = STARTED_CHATTER;
+ suppressKibitz = TRUE;
+ continue;
+ }
+ if((gameMode == IcsIdle || gameMode == BeginningOfGame)
+ && looking_at(buf, &i, "* ads displayed")) {
+ soughtPending = FALSE;
+ seekGraphUp = TRUE;
+ DrawSeekGraph();
+ continue;
+ }
+ if(appData.autoRefresh) {
+ if(looking_at(buf, &i, "* (*) seeking * * * * *\"play *\" to respond)\n")) {
+ int s = (ics_type == ICS_ICC); // ICC format differs
+ if(seekGraphUp)
+ AddAd(star_match[0], star_match[1], atoi(star_match[2+s]), atoi(star_match[3+s]),
+ star_match[4+s][0], star_match[5-3*s], atoi(star_match[7]), TRUE);
+ looking_at(buf, &i, "*% "); // eat prompt
+ if(oldi > 0 && buf[oldi-1] == '\n') oldi--; // suppress preceding LF, if any
+ if (oldi > next_out) SendToPlayer(&buf[next_out], oldi - next_out);
+ next_out = i; // suppress
+ continue;
+ }
+ if(looking_at(buf, &i, "\nAds removed: *\n") || looking_at(buf, &i, "\031(51 * *\031)")) {
+ char *p = star_match[0];
+ while(*p) {
+ if(seekGraphUp) RemoveSeekAd(atoi(p));
+ while(*p && *p++ != ' '); // next
+ }
+ looking_at(buf, &i, "*% "); // eat prompt
+ if (oldi > next_out) SendToPlayer(&buf[next_out], oldi - next_out);
+ next_out = i;
+ continue;
+ }
+ }
+ }
+
/* skip formula vars */
if (started == STARTED_NONE &&
buf[i] == 'f' && isdigit(buf[i+1]) && buf[i+2] == ':') {
continue;
}
- oldi = i;
// [HGM] kibitz: try to recognize opponent engine-score kibitzes, to divert them to engine-output window
if (appData.autoKibitz && started == STARTED_NONE &&
!appData.icsEngineAnalyze && // [HGM] [DM] ICS analyze
(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;
+ if (oldi > next_out) SendToPlayer(&buf[next_out], oldi - next_out);
+ next_out = i;
if((StrStr(star_match[0], gameInfo.white) == star_match[0]
&& (gameMode == IcsPlayingWhite)) ||
(StrStr(star_match[0], gameInfo.black) == star_match[0]
}
continue;
} else
- if(looking_at(buf, &i, "kibitzed to *\n") && atoi(star_match[0])) {
+ if((looking_at(buf, &i, "\nkibitzed to *\n") || looking_at(buf, &i, "kibitzed to *\n") ||
+ looking_at(buf, &i, "\n(kibitzed to *\n") || looking_at(buf, &i, "(kibitzed to *\n"))
+ && atoi(star_match[0])) {
// suppress the acknowledgements of our own autoKibitz
+ char *p;
+ if (oldi > next_out) SendToPlayer(&buf[next_out], oldi - next_out);
+ if(p = strchr(star_match[0], ' ')) p[1] = NULLCHAR; // clip off "players)" on FICS
SendToPlayer(star_match[0], strlen(star_match[0]));
- looking_at(buf, &i, "*% "); // eat prompt
+ if(looking_at(buf, &i, "*% ")) // eat prompt
+ suppressKibitz = FALSE;
next_out = i;
+ continue;
}
} // [HGM] kibitz: end of patch
-//if(appData.debugMode) fprintf(debugFP, "hunt for tell, buf = %s\n", buf+i);
-
// [HGM] chat: intercept tells by users for which we have an open chat window
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, "* 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[2], "%d", &channel),1) ||
+ looking_at(buf, &i, "*(*)(*)(*):") && (sscanf(star_match[3], "%d", &channel),1) ||
+ looking_at(buf, &i, "*(*)(*)(*)(*):") && sscanf(star_match[4], "%d", &channel) == 1 )) {
int p;
sscanf(star_match[0], "%[^(]", talker+1); // strip (C) or (U) off ICS handle
chattingPartner = -1;
if(channel >= 0) // channel broadcast; look if there is a chatbox for this channel
for(p=0; p<MAX_CHAT; p++) {
if(channel == atoi(chatPartner[p])) {
- talker[0] = '['; strcat(talker, "]");
+ talker[0] = '['; strcat(talker, "] ");
+ Colorize(channel == 1 ? ColorChannel1 : ColorChannel, FALSE);
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])) {
- talker[0] = '['; strcat(talker, "]");
+ 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
+ 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;
parse_pos = 0; parse[0] = NULLCHAR;
- savingComment = TRUE;
+ savingComment = 3 + chattingPartner; // counts as TRUE
suppressKibitz = TRUE;
+ continue;
}
} // [HGM] chat: end of patch
memcpy(parse, &buf[oldi], parse_pos);
parse[parse_pos] = NULLCHAR;
started = STARTED_COMMENT;
+ if(savingComment >= 3) // [HGM] chat: continuation of line for chat box
+ chattingPartner = savingComment - 3; // kludge to remember the box
} else {
started = STARTED_CHATTER;
}
if (looking_at(buf, &i, "% ") ||
((started == STARTED_MOVES || started == STARTED_MOVES_NOHIDE)
&& looking_at(buf, &i, "}*"))) { char *bookHit = NULL; // [HGM] book
+ if(ics_type == ICS_ICC && soughtPending) { // [HGM] seekgraph: on ICC sought-list has no termination line
+ soughtPending = FALSE;
+ seekGraphUp = TRUE;
+ DrawSeekGraph();
+ }
if(suppressKibitz) next_out = i;
savingComment = FALSE;
suppressKibitz = 0;
looking_at(buf, &i, "It is not your move")) {
/* Illegal move */
if (ics_user_moved && forwardMostMove > backwardMostMove) { // only backup if we already moved
- currentMove = --forwardMostMove;
+ currentMove = forwardMostMove-1;
DisplayMove(currentMove - 1); /* before DMError */
DrawPosition(FALSE, boards[currentMove]);
- SwitchClocks();
+ SwitchClocks(forwardMostMove-1); // [HGM] race
DisplayBothClocks();
}
DisplayMoveError(_("Illegal move (rejected by ICS)")); // [HGM] but always relay error msg
ZippyGameStart(whitename, blackname);
}
#endif /*ZIPPY*/
+ partnerBoardValid = FALSE; // [HGM] bughouse
continue;
}
Reset(TRUE, TRUE);
}
#endif /*ZIPPY*/
+ if(appData.bgObserve && partnerBoardValid) DrawPosition(TRUE, partnerBoard);
continue;
}
if (appData.debugMode)
fprintf(debugFP, "Parsing holdings: %s, currentMove = %d\n",
parse, currentMove);
- if (sscanf(parse, " game %d", &gamenum) == 1 &&
- gamenum == ics_gamenum) {
+ if (sscanf(parse, " game %d", &gamenum) == 1) {
+ if(gamenum == ics_gamenum) { // [HGM] bughouse: old code if part of foreground game
if (gameInfo.variant == VariantNormal) {
/* [HGM] We seem to switch variant during a game!
* Presumably no holdings were displayed, so we have
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
+ sscanf(parse, "game %d white [%s black [%s <- %s",
+ &gamenum, white_holding, black_holding,
+ new_piece);
+ white_holding[strlen(white_holding)-1] = NULLCHAR;
+ black_holding[strlen(black_holding)-1] = NULLCHAR;
+ /* [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 (looking_at(buf, &i, "*% ")) {
char promoChar;
int ranks=1, files=0; /* [HGM] ICS80: allow variable board size */
char *bookHit = NULL; // [HGM] book
- Boolean weird = FALSE, reqFlag = FALSE;
+ Boolean weird = FALSE, reqFlag = FALSE, repaint = FALSE;
fromX = fromY = toX = toY = -1;
break;
}
+ if((gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack)
+ && newGameMode == IcsObserving && appData.bgObserve) {
+ // [HGM] bughouse: don't act on alien boards while we play. Just parse the board and save it */
+ 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]);
+ if(gameInfo.holdingsWidth > 1) {
+ board[k][0] = board[k][BOARD_WIDTH-1] = EmptySquare;
+ board[k][1] = board[k][BOARD_WIDTH-2] = (ChessSquare) 0;;
+ }
+ }
+ CopyBoard(partnerBoard, board);
+ if(appData.dualBoard && !twoBoards) { twoBoards = repaint = 1; InitDrawingSizes(-2,0); }
+ if(twoBoards) { partnerUp = 1; flipView = !flipView; } // [HGM] dual
+ if(partnerUp) DrawPosition(repaint, partnerBoard);
+ 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(partnerStatus, "");
+ partnerBoardValid = TRUE;
+ return;
+ }
+
/* Modify behavior for initial board display on move listing
of wild games.
*/
}
}
-
+
/* Display the board */
if (!pausing && !appData.noGUI) {
((gameMode == IcsPlayingBlack) && (!WhiteOnMove(currentMove))))
ClearPremoveHighlights();
- DrawPosition(FALSE, boards[currentMove]);
+ 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);
if (appData.ringBellAfterMoves && /*!ics_user_moved*/ // [HGM] use absolute method to recognize own move
!((gameMode == IcsPlayingWhite) && (!WhiteOnMove(moveNum)) ||
}
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)){
nr++; moveType = Comment; // First move has been played; kludge to make sure we continue
+ // Hande case where played move is different from leading PV move
+ CopyBoard(boards[endPV+1], boards[endPV-1]); // tentatively unplay last game move
+ CopyBoard(boards[endPV+2], boards[endPV-1]); // and play first move of PV
+ ApplyMove(fromX, fromY, toX, toY, promoChar, boards[endPV+2]);
+ if(!CompareBoards(boards[endPV], boards[endPV+2])) {
+ endPV += 2; // if position different, keep this
+ moveList[endPV-1][0] = fromX + AAA;
+ moveList[endPV-1][1] = fromY + ONE;
+ moveList[endPV-1][2] = toX + AAA;
+ moveList[endPV-1][3] = toY + ONE;
+ parseList[endPV-1][0] = NULLCHAR;
+ 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;
}
setboardSpoiledMachineBlack = 0; /* [HGM] assume WB 4.2.7 already solves this after sending setboard */
}
+static int autoQueen; // [HGM] oneclick
+
int
HasPromotionChoice(int fromX, int fromY, int toX, int toY, char *promoChoice)
{
*promoChoice = PieceToChar(BlackFerz); // no choice
return FALSE;
}
- if(appData.alwaysPromoteToQueen) { // predetermined
+ if(autoQueen) { // predetermined
if(gameInfo.variant == VariantSuicide || gameInfo.variant == VariantLosers)
*promoChoice = PieceToChar(BlackKing); // in Suicide Q is the last thing we want
else *promoChoice = PieceToChar(BlackQueen);
return TRUE;
}
+Boolean
+OnlyMove(int *x, int *y, Boolean captures) {
+ DisambiguateClosure cl;
+ if (appData.zippyPlay) return FALSE;
+ switch(gameMode) {
+ case MachinePlaysBlack:
+ case IcsPlayingWhite:
+ case BeginningOfGame:
+ if(!WhiteOnMove(currentMove)) return FALSE;
+ break;
+ case MachinePlaysWhite:
+ case IcsPlayingBlack:
+ if(WhiteOnMove(currentMove)) return FALSE;
+ break;
+ default:
+ return FALSE;
+ }
+ cl.pieceIn = EmptySquare;
+ cl.rfIn = *y;
+ cl.ffIn = *x;
+ cl.rtIn = -1;
+ cl.ftIn = -1;
+ cl.promoCharIn = NULLCHAR;
+ Disambiguate(boards[currentMove], PosFlags(currentMove), &cl);
+ if( cl.kind == NormalMove ||
+ cl.kind == AmbiguousMove && captures && cl.captures == 1 ||
+ cl.kind == WhitePromotionQueen || cl.kind == BlackPromotionQueen ||
+ cl.kind == WhitePromotionKnight || cl.kind == BlackPromotionKnight ||
+ cl.kind == WhiteCapturesEnPassant || cl.kind == BlackCapturesEnPassant) {
+ fromX = cl.ff;
+ fromY = cl.rf;
+ *x = cl.ft;
+ *y = cl.rt;
+ return TRUE;
+ }
+ if(cl.kind != ImpossibleMove) return FALSE;
+ cl.pieceIn = EmptySquare;
+ cl.rfIn = -1;
+ cl.ffIn = -1;
+ cl.rtIn = *y;
+ cl.ftIn = *x;
+ cl.promoCharIn = NULLCHAR;
+ Disambiguate(boards[currentMove], PosFlags(currentMove), &cl);
+ if( cl.kind == NormalMove ||
+ cl.kind == AmbiguousMove && captures && cl.captures == 1 ||
+ cl.kind == WhitePromotionQueen || cl.kind == BlackPromotionQueen ||
+ cl.kind == WhitePromotionKnight || cl.kind == BlackPromotionKnight ||
+ cl.kind == WhiteCapturesEnPassant || cl.kind == BlackCapturesEnPassant) {
+ fromX = cl.ff;
+ fromY = cl.rf;
+ *x = cl.ft;
+ *y = cl.rt;
+ autoQueen = TRUE; // act as if autoQueen on when we click to-square
+ return TRUE;
+ }
+ return FALSE;
+}
+
FILE *lastLoadGameFP = NULL, *lastLoadPositionFP = NULL;
int lastLoadGameNumber = 0, lastLoadPositionNumber = 0;
int lastLoadGameUseList = FALSE;
}
}
- userOfferedDraw = FALSE; // [HGM] drawclaim: only if we do a legal move!
-
return moveType;
/* [HGM] <popupFix> in stead of calling FinishMove directly, this
function is made into one that returns an OK move type if FinishMove
if (appData.icsActive) {
if (gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||
gameMode == IcsExamining) {
+ if(userOfferedDraw && (signed char)boards[forwardMostMove][EP_STATUS] <= EP_DRAWS) {
+ SendToICS(ics_prefix); // [HGM] drawclaim: send caim and move on one line for FICS
+ SendToICS("draw ");
+ SendMoveToICS(moveType, fromX, fromY, toX, toY);
+ }
+ // also send plain move, in case ICS does not understand atomic claims
SendMoveToICS(moveType, fromX, fromY, toX, toY);
ics_user_moved = 1;
}
break;
}
+ userOfferedDraw = FALSE; // [HGM] drawclaim: after move made, and tested for claimable draw
+
if(bookHit) { // [HGM] book: simulate book reply
static char bookMove[MSG_SIZ]; // a bit generous?
static int second = 0, promotionChoice = 0;
char promoChoice = NULLCHAR;
+ if(appData.seekGraph && appData.icsActive && loggedOn &&
+ (gameMode == BeginningOfGame || gameMode == IcsIdle)) {
+ SeekGraphClick(clickType, xPix, yPix, 0);
+ return;
+ }
+
if (clickType == Press) ErrorPopDown();
MarkTargetSquares(1);
|| x == BOARD_RGHT+1 && y >= gameInfo.holdingsSize) )
return;
+ autoQueen = appData.alwaysPromoteToQueen;
+
if (fromX == -1) {
+ if(!appData.oneClick || !OnlyMove(&x, &y, FALSE)) {
if (clickType == Press) {
/* First square */
if (OKToStartUserMove(x, y)) {
}
}
return;
+ }
}
/* fromX != -1 */
!(fromP == BlackKing && toP == BlackRook && frc))) {
/* Clicked again on same color piece -- changed his mind */
second = (x == fromX && y == fromY);
+ if(!second || !OnlyMove(&x, &y, TRUE)) {
if (appData.highlightDragging) {
SetHighlights(x, y, -1, -1);
} else {
DragPieceBegin(xPix, yPix);
}
return;
+ }
}
// ignore clicks on holdings
if(x < BOARD_LEFT || x >= BOARD_RGHT) return;
if(x == BOARD_LEFT-2 && piece >= BlackPawn) {
n = PieceToNumber(piece - (int)BlackPawn);
- if(n > gameInfo.holdingsSize) { n = 0; piece = BlackPawn; }
+ if(n >= gameInfo.holdingsSize) { n = 0; piece = BlackPawn; }
boards[currentMove][BOARD_HEIGHT-1 - n][0] = piece;
boards[currentMove][BOARD_HEIGHT-1 - n][1]++;
} else
if(x == BOARD_RGHT+1 && piece < BlackPawn) {
n = PieceToNumber(piece);
- if(n > gameInfo.holdingsSize) { n = 0; piece = WhitePawn; }
+ if(n >= gameInfo.holdingsSize) { n = 0; piece = WhitePawn; }
boards[currentMove][n][BOARD_WIDTH-1] = piece;
boards[currentMove][n][BOARD_WIDTH-2]++;
}
}
}
+int RightClick(ClickType action, int x, int y, int *fromX, int *fromY)
+{ // front-end-free part taken out of PieceMenuPopup
+ int whichMenu; int xSqr, ySqr;
+
+ if(seekGraphUp) { // [HGM] seekgraph
+ if(action == Press) SeekGraphClick(Press, x, y, 2); // 2 indicates right-click: no pop-down on miss
+ if(action == Release) SeekGraphClick(Release, x, y, 2); // and no challenge on hit
+ return -2;
+ }
+
+ if((gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack)
+ && !appData.zippyPlay && appData.bgObserve) { // [HGM] bughouse: show background game
+ 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;
+ }
+
+ xSqr = EventToSquare(x, BOARD_WIDTH);
+ ySqr = EventToSquare(y, BOARD_HEIGHT);
+ if (action == Release) UnLoadPV(); // [HGM] pv
+ if (action != Press) return -2; // return code to be ignored
+ switch (gameMode) {
+ case IcsExamining:
+ if(xSqr < BOARD_LEFT || xSqr >= BOARD_RGHT) return -1;\r
+ case EditPosition:
+ if (xSqr == BOARD_LEFT-1 || xSqr == BOARD_RGHT) return -1;\r
+ if (xSqr < 0 || ySqr < 0) return -1;\r
+ whichMenu = 0; // edit-position menu
+ break;
+ case IcsObserving:
+ if(!appData.icsEngineAnalyze) return -1;
+ case IcsPlayingWhite:
+ case IcsPlayingBlack:
+ if(!appData.zippyPlay) goto noZip;
+ case AnalyzeMode:
+ case AnalyzeFile:
+ case MachinePlaysWhite:
+ case MachinePlaysBlack:
+ case TwoMachinesPlay: // [HGM] pv: use for showing PV
+ if (!appData.dropMenu) {
+ LoadPV(x, y);
+ return 2; // flag front-end to grab mouse events
+ }
+ if(gameMode == TwoMachinesPlay || gameMode == AnalyzeMode ||
+ gameMode == AnalyzeFile || gameMode == IcsObserving) return -1;
+ case EditGame:
+ noZip:
+ if (xSqr < 0 || ySqr < 0) return -1;
+ if (!appData.dropMenu || appData.testLegality &&
+ gameInfo.variant != VariantBughouse &&
+ gameInfo.variant != VariantCrazyhouse) return -1;
+ whichMenu = 1; // drop menu
+ break;
+ default:
+ return -1;
+ }
+
+ if (((*fromX = xSqr) < 0) ||
+ ((*fromY = ySqr) < 0)) {
+ *fromX = *fromY = -1;
+ return -1;
+ }
+ if (flipView)
+ *fromX = BOARD_WIDTH - 1 - *fromX;
+ else
+ *fromY = BOARD_HEIGHT - 1 - *fromY;
+
+ return whichMenu;
+}
+
void SendProgramStatsToFrontend( ChessProgramState * cps, ChessProgramStats * cpstats )
{
// char * hint = lastHint;
if (cps->sendTime == 2) cps->sendTime = 1;
if (cps->offeredDraw) cps->offeredDraw--;
-#if ZIPPY
- if ((gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack) &&
- first.initDone) {
- SendMoveToICS(moveType, fromX, fromY, toX, toY);
- ics_user_moved = 1;
- if(appData.autoKibitz && !appData.icsEngineAnalyze ) { /* [HGM] kibitz: send most-recent PV info to ICS */
- char buf[3*MSG_SIZ];
-
- sprintf(buf, "kibitz !!! %+.2f/%d (%.2f sec, %u nodes, %.0f knps) PV=%s\n",
- programStats.score / 100.,
- programStats.depth,
- programStats.time / 100.,
- (unsigned int)programStats.nodes,
- (unsigned int)programStats.nodes / (10*abs(programStats.time) + 1.),
- programStats.movelist);
- SendToICS(buf);
-if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats.nodes, programStats.nodes);
- }
- }
-#endif
/* currentMoveString is set as a side-effect of ParseOneMove */
strcpy(machineMove, currentMoveString);
strcat(machineMove, "\n");
strcpy(moveList[forwardMostMove], machineMove);
- /* [AS] Save move info and clear stats for next move */
- pvInfoList[ forwardMostMove ].score = programStats.score;
- pvInfoList[ forwardMostMove ].depth = programStats.depth;
- pvInfoList[ forwardMostMove ].time = programStats.time; // [HGM] PGNtime: take time from engine stats
- ClearProgramStats();
- thinkOutput[0] = NULLCHAR;
- hiddenThinkOutputState = 0;
-
MakeMove(fromX, fromY, toX, toY, promoChar);/*updates forwardMostMove*/
/* [AS] Adjudicate game if needed (note: remember that forwardMostMove now points past the last move) */
if(Adjudicate(cps)) return; // [HGM] adjudicate: for all automatic game ends
+#if ZIPPY
+ if ((gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack) &&
+ first.initDone) {
+ if(cps->offeredDraw && (signed char)boards[forwardMostMove][EP_STATUS] <= EP_DRAWS) {
+ SendToICS(ics_prefix); // [HGM] drawclaim: send caim and move on one line for FICS
+ SendToICS("draw ");
+ SendMoveToICS(moveType, fromX, fromY, toX, toY);
+ }
+ SendMoveToICS(moveType, fromX, fromY, toX, toY);
+ ics_user_moved = 1;
+ if(appData.autoKibitz && !appData.icsEngineAnalyze ) { /* [HGM] kibitz: send most-recent PV info to ICS */
+ char buf[3*MSG_SIZ];
+
+ sprintf(buf, "kibitz !!! %+.2f/%d (%.2f sec, %u nodes, %.0f knps) PV=%s\n",
+ programStats.score / 100.,
+ programStats.depth,
+ programStats.time / 100.,
+ (unsigned int)programStats.nodes,
+ (unsigned int)programStats.nodes / (10*abs(programStats.time) + 1.),
+ programStats.movelist);
+ SendToICS(buf);
+if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats.nodes, programStats.nodes);
+ }
+ }
+#endif
+
+ /* [AS] Save move info and clear stats for next move */
+ pvInfoList[ forwardMostMove-1 ].score = programStats.score;
+ pvInfoList[ forwardMostMove-1 ].depth = programStats.depth;
+ pvInfoList[ forwardMostMove-1 ].time = programStats.time; // [HGM] PGNtime: take time from engine stats
+ ClearProgramStats();
+ thinkOutput[0] = NULLCHAR;
+ hiddenThinkOutputState = 0;
+
bookHit = NULL;
if (gameMode == TwoMachinesPlay) {
/* [HGM] relaying draw offers moved to after reception of move */
gameMode = EditGame;
ModeHighlight();
}
- currentMove = --forwardMostMove;
+ currentMove = forwardMostMove-1;
DisplayMove(currentMove-1); /* before DisplayMoveError */
- SwitchClocks();
+ SwitchClocks(forwardMostMove-1); // [HGM] race
DisplayBothClocks();
sprintf(buf1, _("Illegal move \"%s\" (rejected by %s chess program)"),
parseList[currentMove], cps->which);
}
CopyBoard(boards[forwardMostMove+1], boards[forwardMostMove]);
ApplyMove(fromX, fromY, toX, toY, promoChar, boards[forwardMostMove+1]);
- forwardMostMove++; // [HGM] bare: moved to after ApplyMove, to make sure clock interrupt finds complete board
- SwitchClocks(); // uses forwardMostMove, so must be done after incrementing it !
+ // forwardMostMove++; // [HGM] bare: moved to after ApplyMove, to make sure clock interrupt finds complete board
+ SwitchClocks(forwardMostMove+1); // [HGM] race: incrementing move nr inside
timeRemaining[0][forwardMostMove] = whiteTimeRemaining;
timeRemaining[1][forwardMostMove] = blackTimeRemaining;
gameInfo.result = GameUnfinished;
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);
}
+ fromX = fromY = -1; // [HGM] abort any move the user is entering.
+
if (appData.icsActive && (whosays == GE_ENGINE || whosays >= GE_ENGINE1)) {
/* If we are playing on ICS, the server decides when the
game is over, but the engine can offer to draw, claim
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 ) {
int n;
if(x == BOARD_LEFT-2 && selection >= BlackPawn) {
n = PieceToNumber(selection - BlackPawn);
- if(n > gameInfo.holdingsSize) { n = 0; 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]++;
} else
if(x == BOARD_RGHT+1 && selection < BlackPawn) {
n = PieceToNumber(selection);
- if(n > gameInfo.holdingsSize) { n = 0; selection = WhitePawn; }
+ if(n >= gameInfo.holdingsSize) { n = 0; selection = WhitePawn; }
boards[0][n][BOARD_WIDTH-1] = selection;
boards[0][n][BOARD_WIDTH-2]++;
}
}
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) {
from the color that is *not* on move now.
*/
void
-SwitchClocks()
+SwitchClocks(int newMoveNr)
{
long lastTickLength;
TimeMark now;
if (StopClockTimer() && appData.clockMode) {
lastTickLength = SubtractTimeMarks(&now, &tickStartTM);
- if (WhiteOnMove(forwardMostMove)) {
+ if (!WhiteOnMove(forwardMostMove)) {
if(blackNPS >= 0) lastTickLength = 0;
blackTimeRemaining -= lastTickLength;
/* [HGM] PGNtime: save time for PGN file if engine did not give it */
}
flagged = CheckFlags();
}
+ forwardMostMove = newMoveNr; // [HGM] race: change stm when no timer interrupt scheduled
CheckTimeControl();
if (flagged || !appData.clockMode) return;
}
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);
+}