}
}
+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;
}
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 *toSqr;
for (k = 0; k < ranks; k++) {
{
int x, y;
Boolean saveAnimate;
- static int second = 0, promotionChoice = 0;
+ static int second = 0, promotionChoice = 0, dragging = 0;
char promoChoice = NULLCHAR;
if(appData.seekGraph && appData.icsActive && loggedOn &&
fromY = y;
second = 0;
MarkTargetSquares(0);
- DragPieceBegin(xPix, yPix);
+ DragPieceBegin(xPix, yPix); dragging = 1;
if (appData.highlightDragging) {
SetHighlights(x, y, -1, -1);
}
}
+ } else if(dragging) { // [HGM] from-square must have been reset due to game end since last press
+ DragPieceEnd(xPix, yPix); dragging = 0;
+ DrawPosition(FALSE, NULL);
}
return;
}
!(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(!second || appData.oneClick && !OnlyMove(&x, &y, TRUE)) {
if (appData.highlightDragging) {
SetHighlights(x, y, -1, -1);
} else {
}
if (OKToStartUserMove(x, y)) {
fromX = x;
- fromY = y;
+ fromY = y; dragging = 1;
MarkTargetSquares(0);
DragPieceBegin(xPix, yPix);
}
}
if (clickType == Release && x == fromX && y == fromY) {
- DragPieceEnd(xPix, yPix);
+ DragPieceEnd(xPix, yPix); dragging = 0;
if (appData.animateDragging) {
/* Undo animation damage if any */
DrawPosition(FALSE, NULL);
} else {
ClearHighlights();
}
- DragPieceEnd(xPix, yPix);
+ DragPieceEnd(xPix, yPix); dragging = 0;
/* Don't animate move and drag both */
appData.animate = FALSE;
}
case BlackRook:
NrBR++; break;
case WhiteQueen:
+ case WhiteCannon:
NrWQ++; break;
case BlackQueen:
+ case BlackCannon:
NrBQ++; break;
case EmptySquare:
break;
if( NrPieces == 2 || gameInfo.variant != VariantXiangqi &&
gameInfo.variant != VariantShatranj && // [HGM] baring will remain possible
(NrPieces == 3 && NrWN+NrBN+NrWB+NrBB == 1 ||
- NrPieces == NrBB+NrWB+2 && bishopsColor != 3)) // [HGM] all Bishops (Ferz!) same color
+ NrPieces == NrBB+NrWB+2 && bishopsColor != 3) // [HGM] all Bishops (Ferz!) same color
+ || gameInfo.variant == VariantXiangqi &&
+ (NrPieces == 3 && (NrWQ==1 && NrWB==0 && NrBB<2 || NrBQ==1 && NrBB==0 && NrWB<2) ||
+ NrPieces == 4 && NrWQ==1 && NrBQ==1 && NrWB==0 && NrBB==0))
{ /* KBK, KNK, KK of KBKB with like Bishops */
/* always flag draws, for judging claims */
|| 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 */
if( count == backwardMostMove )
count -= initialRulePlies;
count = forwardMostMove - count;
+ if(gameInfo.variant == VariantXiangqi && ( count >= 100 || count >= 2*appData.ruleMoves ) ) {
+ // adjust reversible move counter for checks in Xiangqi
+ int i = forwardMostMove - count, inCheck = 0, lastCheck;
+ if(i < backwardMostMove) i = backwardMostMove;
+ while(i <= forwardMostMove) {
+ lastCheck = inCheck; // check evasion does not count
+ inCheck = (MateTest(boards[i], PosFlags(i)) == MT_CHECK);
+ if(inCheck || lastCheck) count--; // check does not count
+ i++;
+ }
+ }
if( count >= 100)
boards[forwardMostMove][EP_STATUS] = EP_RULE_DRAW;
/* this is used to judge if draw claims are legal */
// 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
strcat(machineMove, "\n");
strcpy(moveList[forwardMostMove], machineMove);
+ /* [AS] Save move info*/
+ pvInfoList[ forwardMostMove ].score = programStats.score;
+ pvInfoList[ forwardMostMove ].depth = programStats.depth;
+ pvInfoList[ forwardMostMove ].time = programStats.time; // [HGM] PGNtime: take time from engine stats
+
MakeMove(fromX, fromY, toX, toY, promoChar);/*updates forwardMostMove*/
/* [AS] Adjudicate game if needed (note: remember that forwardMostMove now points past the last move) */
}
#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
+ /* [AS] Clear stats for next move */
ClearProgramStats();
thinkOutput[0] = NULLCHAR;
hiddenThinkOutputState = 0;
* 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( (boards[forwardMostMove][fromY][fromX] == WhitePawn ||
boards[forwardMostMove][fromY][fromX] == BlackPawn ) &&
boards[forwardMostMove][toY][toX] == EmptySquare
- && fromX != toX )
+ && fromX != toX && fromY != toY)
fprintf(serverMoves, ":%c%c:%c%c", AAA+fromX, ONE+fromY, AAA+toX, ONE+fromY);
// promotion suffix
if(promoChar != NULLCHAR)
/* [HGM] PGNvariant: automatically switch to variant given in PGN tag */
if(gameInfo.variant != oldVariant) {
startedFromPositionFile = FALSE; /* [HGM] loadPos: variant switch likely makes position invalid */
+ ResetFrontEnd(); // [HGM] might need other bitmaps. Cannot use Reset() because it clears gameInfo :-(
InitPosition(TRUE);
oldVariant = gameInfo.variant;
if (appData.debugMode)
}
continue;
}
- if (BoolFeature(&p, "smp", &cps->maxCores, cps)) continue;
/* End of additions by HGM */
/* unknown feature: complain and skip */
}
void
-NewSettingEvent(option, command, value)
+NewSettingEvent(option, feature, command, value)
char *command;
- int option, value;
+ int option, value, *feature;
{
char buf[MSG_SIZ];
if (gameMode == EditPosition) EditPositionDone(TRUE);
sprintf(buf, "%s%s %d\n", (option ? "option ": ""), command, value);
- SendToProgram(buf, &first);
+ if(feature == NULL || *feature) SendToProgram(buf, &first);
if (gameMode == TwoMachinesPlay) {
- SendToProgram(buf, &second);
+ if(feature == NULL || feature[(int*)&second - (int*)&first]) SendToProgram(buf, &second);
}
}