* Massachusetts.
*
* Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
- * 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Free Software Foundation, Inc.
+ * 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Free
+ * Software Foundation, Inc.
*
* Enhancements Copyright 2005 Alessandro Scotti
*
int keepInfo = 0; /* [HGM] to protect PGN tags in auto-step game analysis */
static int initPing = -1;
int border; /* [HGM] width of board rim, needed to size seek graph */
-char bestMove[MSG_SIZ];
+char bestMove[MSG_SIZ], avoidMove[MSG_SIZ];
int solvingTime, totalTime;
/* States for ics_getting_history */
by this function.
*/
int
-PosFlags (index)
+PosFlags (int index)
{
int flags = F_ALL_CASTLE_OK;
if ((index % 2) == 0) flags |= F_WHITE_ON_MOVE;
SwapEngines(i);
ReplaceEngine(cps, i);
FloatToFront(&appData.recentEngineList, engineLine);
+ if(gameMode == BeginningOfGame) Reset(TRUE, TRUE);
return;
}
p = engineName;
}
matchMode = mode;
matchGame = roundNr = 1;
- first.matchWins = second.matchWins = 0; // [HGM] match: needed in later matches
+ first.matchWins = second.matchWins = totalTime = 0; // [HGM] match: needed in later matches
NextMatchGame();
}
CopyBoard(filePosition, boards[0]);
CopyBoard(initialPosition, boards[0]);
}
+ } else if(*appData.fen != NULLCHAR) {
+ if(ParseFEN(filePosition, &blackPlaysFirst, appData.fen, TRUE) && !blackPlaysFirst) {
+ startedFromPositionFile = TRUE;
+ Reset(TRUE, TRUE);
+ }
}
if (initialMode == AnalyzeMode) {
if (appData.noChessProgram) {
DisplayFatalError(_("Error reading from keyboard"), error, 1);
} else if (gotEof++ > 0) {
RemoveInputSource(isr);
- DisplayFatalError(_("Got end of file from keyboard"), 0, 0);
+ DisplayFatalError(_("Got end of file from keyboard"), 0, 666); // [HGM] 666 is kludge to alert front end
}
}
#if ZIPPY
if (loggedOn == TRUE)
if (ZippyControl(buf, &backup) || ZippyConverse(buf, &backup) ||
- (appData.zippyPlay && ZippyMatch(buf, &backup)));
+ (appData.zippyPlay && ZippyMatch(buf, &backup)))
+ ;
#endif
} // [DM] 'else { ' deleted
if (
fprintf(debugFP, "Sending premove:\n");
SendToICS(str);
} else if (gotPremove) {
+ int oldFMM = forwardMostMove;
gotPremove = 0;
ClearPremoveHighlights();
if (appData.debugMode)
UserMoveEvent(premoveFromX, premoveFromY,
premoveToX, premoveToY,
premovePromoChar);
+ if(forwardMostMove == oldFMM) { // premove was rejected, highlight last opponent move
+ if(moveList[oldFMM-1][1] != '@')
+ SetHighlights(moveList[oldFMM-1][0]-AAA, moveList[oldFMM-1][1]-ONE,
+ moveList[oldFMM-1][2]-AAA, moveList[oldFMM-1][3]-ONE);
+ else // (drop)
+ SetHighlights(-1, -1, moveList[oldFMM-1][2]-AAA, moveList[oldFMM-1][3]-ONE);
+ }
}
}
} else
if(moveList[moveNum][4] == ';') { // [HGM] lion: move is double-step over intermediate square
char *m = moveList[moveNum];
+ static char c[2];
+ *c = m[7]; // promoChar
if((boards[moveNum][m[6]-ONE][m[5]-AAA] < BlackPawn) == (boards[moveNum][m[1]-ONE][m[0]-AAA] < BlackPawn)) // move is kludge to indicate castling
snprintf(buf, MSG_SIZ, "%c%d%c%d,%c%d%c%d\n", m[0], m[1] - '0', // convert to two moves
m[2], m[3] - '0',
m[5], m[6] - '0',
m[2] + (m[0] > m[5] ? 1 : -1), m[3] - '0');
- else
- snprintf(buf, MSG_SIZ, "%c%d%c%d,%c%d%c%d\n", m[0], m[1] - '0', // convert to two moves
+ else if(*c && m[8]) { // kill square followed by 2 characters: 2nd kill square rather than promo suffix
+ *c = m[9];
+ snprintf(buf, MSG_SIZ, "%c%d%c%d,%c%d%c%d,%c%d%c%d%s\n", m[0], m[1] - '0', // convert to three moves
+ m[7], m[8] - '0',
+ m[7], m[8] - '0',
m[5], m[6] - '0',
m[5], m[6] - '0',
- m[2], m[3] - '0');
+ m[2], m[3] - '0', c);
+ } else
+ snprintf(buf, MSG_SIZ, "%c%d%c%d,%c%d%c%d%s\n", m[0], m[1] - '0', // convert to two moves
+ m[5], m[6] - '0',
+ m[5], m[6] - '0',
+ m[2], m[3] - '0', c);
SendToProgram(buf, cps);
} else
if(BOARD_HEIGHT > 10) { // [HGM] big: convert ranks to double-digit where needed
AAA + ff, ONE + rf, AAA + ft, ONE + rt);
if(killX >= 0 && killY >= 0) {
sprintf(move+4, ";%c%c\n", AAA + killX, ONE + killY);
- if(kill2X >= 0 && kill2Y >= 0) sprintf(move+7, "%c%c\n", AAA + killX, ONE + killY);
+ if(kill2X >= 0 && kill2Y >= 0) sprintf(move+7, "%c%c\n", AAA + kill2X, ONE + kill2Y);
}
} else {
sprintf(move, "%c%c%c%c%c\n",
AAA + ff, ONE + rf, AAA + ft, ONE + rt, promoChar);
+ if(killX >= 0 && killY >= 0) {
+ sprintf(move+4, ";%c%c\n", AAA + killX, ONE + killY);
+ if(kill2X >= 0 && kill2Y >= 0) sprintf(move+7, "%c%c%c\n", AAA + kill2X, ONE + kill2Y, promoChar);
+ }
}
}
}
*toX = currentMoveString[2] - AAA;
*toY = currentMoveString[3] - ONE;
*promoChar = currentMoveString[4];
+ if(*promoChar == ';') *promoChar = currentMoveString[7 + 2*(currentMoveString[8] != 0)];
if (*fromX < BOARD_LEFT || *fromX >= BOARD_RGHT || *fromY < 0 || *fromY >= BOARD_HEIGHT ||
*toX < BOARD_LEFT || *toX >= BOARD_RGHT || *toY < 0 || *toY >= BOARD_HEIGHT) {
if (appData.debugMode) {
{
int n = 0;
if(!*escapes) return strlen(s);
- while(*s) n += (*s != '/' && *s != '-' && *s != '^' && *s != '*' && !strchr(escapes, *s)), s++;
+ while(*s) n += (*s != '/' && *s != '-' && *s != '^' && *s != '*' && !strchr(escapes, *s)) - 2*(*s == '='), s++;
return n;
}
table[i+offs] = map[j++];
if(p = strchr(escapes, map[j])) j++, table[i+offs] += 64*(p - escapes + 1);
if(c) partner[i+offs] = table[i+offs], table[i+offs] = c;
+ if(*escapes && map[j] == '=') pieceNickName[i+offs] = map[++j], j++;
}
table[(int) WhiteKing] = map[j++];
for( ii=offs=0; ii<NrPieces/2-1; ii++ ) {
table[i+offs] = map[j++];
if(p = strchr(escapes, map[j])) j++, table[i+offs] += 64*(p - escapes + 1);
if(c) partner[i+offs] = table[i+offs], table[i+offs] = c;
+ if(*escapes && map[j] == '=') pieceNickName[i+offs] = map[++j], j++;
}
table[(int) BlackKing] = map[j++];
initialRights[i] = filePosition[CASTLING][i];
startedFromSetupPosition = TRUE;
}
+ if(*appData.men) LoadPieceDesc(appData.men);
CopyBoard(boards[0], initialPosition);
*promoChoice = PieceToChar(p++);
if(*promoChoice != '.') break;
}
- return FALSE;
+ if(!*engineVariant) return FALSE; // if used as parent variant there might be promotion choice
}
// no sense asking what we must promote to if it is going to explode...
if(gameInfo.variant == VariantAtomic && boards[currentMove][toY][toX] != EmptySquare) {
case PlayFromGameFile:
if(!shiftKey || !appData.variations) return FALSE; // [HGM] allow starting variation in this mode
case EditGame:
+ case AnalyzeMode:
if (!white_piece && WhiteOnMove(currentMove)) {
DisplayMoveError(_("It is White's turn"));
return FALSE;
ChessMove lastLoadGameStart = EndOfFile;
int doubleClick;
Boolean addToBookFlag;
+static Board rightsBoard, nullBoard;
void
-UserMoveEvent(int fromX, int fromY, int toX, int toY, int promoChar)
+UserMoveEvent (int fromX, int fromY, int toX, int toY, int promoChar)
{
ChessMove moveType;
ChessSquare pup;
"fromY %d, toX %d, toY %d\n",
fromX, fromY, toX, toY);
}
+ DrawPosition(TRUE, boards[currentMove]); // [HGM] repair animation damage done by premove (in particular emptying from-square)
return;
}
break;
"fromY %d, toX %d, toY %d\n",
fromX, fromY, toX, toY);
}
+ DrawPosition(TRUE, boards[currentMove]);
return;
}
break;
if(!appData.pieceMenu && toX == fromX && toY == fromY && boards[0][rf][ff] != EmptySquare) {
ChessSquare p = boards[0][rf][ff];
if(PieceToChar(p) == '+') gatingPiece = CHUDEMOTED(p); else
- if(PieceToChar(CHUPROMOTED(p)) =='+') gatingPiece = CHUPROMOTED(p);
+ if(PieceToChar(CHUPROMOTED(p)) =='+') gatingPiece = CHUPROMOTED(p); else
+ if(p == WhiteKing || p == BlackKing || p == WhiteRook || p == BlackRook) {
+ int n = rightsBoard[toY][toX] ^= 1; // toggle virginity of K or R
+ DisplayMessage("", n ? _("rights granted") : _("rights revoked"));
+ gatingPiece = p;
+ }
}
boards[0][toY][toX] = boards[0][fromY][fromX];
+ rightsBoard[toY][toX] = 0; // revoke rights on moving
if(fromX == BOARD_LEFT-2) { // handle 'moves' out of holdings
if(boards[0][fromY][0] != EmptySquare) {
if(boards[0][fromY][1]) boards[0][fromY][1]--;
if(ExcludeOneMove(fromY, fromX, toY, toX, promoChar, '*')) // toggle
ClearPremoveHighlights(); // was included
else ClearHighlights(), SetPremoveHighlights(ff, rf, ft, rt); // exclusion indicated by premove highlights
+ DrawPosition(FALSE, NULL);
return;
}
if(addToBookFlag) { // adding moves to book
char buf[MSG_SIZ], move[MSG_SIZ];
CoordsToAlgebraic(boards[currentMove], PosFlags(currentMove), fromY, fromX, toY, toX, promoChar, move);
- if(killX >= 0) snprintf(move, MSG_SIZ, "%c%dx%c%d-%c%d", fromX + AAA, fromY + ONE - '0', killX + AAA, killY + ONE - '0', toX + AAA, toY + ONE - '0');
+ if(killX >= 0) snprintf(move, MSG_SIZ, "%c%dx%c%d-%c%d%c", fromX + AAA, fromY + ONE - '0',
+ killX + AAA, killY + ONE - '0', toX + AAA, toY + ONE - '0', promoChar);
snprintf(buf, MSG_SIZ, " 0.0%% 1 %s\n", move);
AddBookMove(buf);
addToBookFlag = FALSE;
{
typedef char Markers[BOARD_RANKS][BOARD_FILES];
Markers *m = (Markers *) closure;
- if(rf == fromY && ff == fromX && (killX < 0 ? !(rt == rf && ft == ff) && legNr & 1 : rt == killY && ft == killX || legNr & 2))
+ if(rf == fromY && ff == fromX && (killX < 0 ? !(rt == rf && ft == ff) && legNr & 1 :
+ kill2X < 0 ? rt == killY && ft == killX || legNr & 2 : rt == killY && ft == killX || legNr & 4))
(*m)[rt][ft] = 1 + (board[rt][ft] != EmptySquare
|| kind == WhiteCapturesEnPassant
- || kind == BlackCapturesEnPassant) + 3*(kind == FirstLeg && killX < 0), legal[rt][ft] = 3;
+ || kind == BlackCapturesEnPassant) + 3*(kind == FirstLeg && (killX < 0 & legNr || legNr & 2 && kill2X < 0)), legal[rt][ft] = 3;
else if(flags & F_MANDATORY_CAPTURE && board[rt][ft] != EmptySquare) (*m)[rt][ft] = 3, legal[rt][ft] = 3;
}
if(clear) { // no reason to ever suppress clearing
for(x=0; x<BOARD_WIDTH; x++) for(y=0; y<BOARD_HEIGHT; y++) sum += marker[y][x], marker[y][x] = 0;
hoverSavedValid = 0;
- if(!sum) return; // nothing was cleared,no redraw needed
+ if(!sum || clear < 0) return; // nothing was cleared,no redraw needed
} else {
int capt = 0;
if(!appData.markers || !appData.highlightDragging || appData.icsActive && gameInfo.variant < VariantShogi ||
// some variants have fixed promotion piece, no promotion at all, or another selection mechanism
if(IS_SHOGI(gameInfo.variant) || gameInfo.variant == VariantXiangqi ||
gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat ||
- gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier ||
- gameInfo.variant == VariantMakruk) return FALSE;
+ (gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier ||
+ gameInfo.variant == VariantMakruk) && !*engineVariant) return FALSE;
return (piece == BlackPawn && y <= zone ||
piece == WhitePawn && y >= BOARD_HEIGHT-1-zone ||
piece == BlackLance && y <= zone ||
{
int x, y;
Boolean saveAnimate;
- static int second = 0, promotionChoice = 0, clearFlag = 0, sweepSelecting = 0;
+ static int second = 0, promotionChoice = 0, clearFlag = 0, sweepSelecting = 0, flashing = 0, saveFlash;
char promoChoice = NULLCHAR;
ChessSquare piece;
static TimeMark lastClickTime, prevClickTime;
+ if(flashing) return;
+
x = EventToSquare(xPix, BOARD_WIDTH);
y = EventToSquare(yPix, BOARD_HEIGHT);
if (!flipView && y >= 0) {
if(gameMode == AnalyzeMode && (pausing || controlKey) && first.excludeMoves) { // use pause state to exclude moves
doubleClick = TRUE; gatingPiece = boards[currentMove][y][x];
}
- fromX = x; fromY = y; toX = toY = killX = killY = -1;
+ fromX = x; fromY = y; toX = toY = killX = killY = kill2X = kill2Y = -1;
if(!appData.oneClick || !OnlyMove(&x, &y, FALSE) ||
// even if only move, we treat as normal when this would trigger a promotion popup, to allow sweep selection
appData.sweepSelect && CanPromote(boards[currentMove][fromY][fromX], fromY) && originalY != y) {
!(fromP == BlackKing && toP == BlackRook && frc)))) {
/* Clicked again on same color piece -- changed his mind */
second = (x == fromX && y == fromY);
- killX = killY = -1;
+ killX = killY = kill2X = kill2Y = -1;
if(second && gameMode == AnalyzeMode && SubtractTimeMarks(&lastClickTime, &prevClickTime) < 200) {
second = FALSE; // first double-click rather than scond click
doubleClick = first.excludeMoves; // used by UserMoveEvent to recognize exclude moves
}
promoDefaultAltered = FALSE;
- MarkTargetSquares(1);
+ if(!second) MarkTargetSquares(1);
if(!(second && appData.oneClick && OnlyMove(&x, &y, TRUE))) {
if (appData.highlightDragging) {
SetHighlights(x, y, -1, -1);
second = 0;
fromX = fromY = -1;
gatingPiece = EmptySquare;
- MarkTargetSquares(1);
ClearHighlights();
gotPremove = 0;
ClearPremoveHighlights();
+ MarkTargetSquares(-1);
+ DrawPosition(FALSE, NULL); // make user highlights are drawn (and deferred marker clearing)
} else {
/* First upclick in same square; start click-click mode */
SetHighlights(x, y, -1, -1);
return;
}
if(x == killX && y == killY) { // second click on this square, which was selected as first-leg target
- killX = killY = -1; // this informs us no second leg is coming, so treat as to-click without intermediate
+ killX = kill2X; killY = kill2Y; kill2X = kill2Y = -1; // this informs us no second leg is coming, so treat as to-click without intermediate
} else
if(marker[y][x] == 5) return; // [HGM] lion: to-click on cyan square; defer action to release
if(legal[y][x] == 2 || HasPromotionChoice(fromX, fromY, toX, toY, &promoChoice, FALSE)) {
if(gameInfo.variant != VariantChuChess && PieceToChar(CHUPROMOTED(piece)) == '+') promoSweep = CHUPROMOTED(piece);
selectFlag = 0; lastX = xPix; lastY = yPix;
ReportClick("put", x, y); // extra put to prompt engine for 'choice' command
+ saveFlash = appData.flashCount; appData.flashCount = 0;
Sweep(0); // Pawn that is going to promote: preview promotion piece
sweepSelecting = 1;
DisplayMessage("", _("Pull pawn backwards to under-promote"));
} else {
ClearHighlights();
}
+ MarkTargetSquares(1);
} else if(sweepSelecting) { // this must be the up-click corresponding to the down-click that started the sweep
sweepSelecting = 0; appData.animate = FALSE; // do not animate, a selected piece already on to-square
- *promoRestrict = 0;
+ *promoRestrict = 0; appData.flashCount = saveFlash;
if (appData.animate || appData.highlightLastMove) {
SetHighlights(fromX, fromY, toX, toY);
} else {
ClearHighlights();
}
+ MarkTargetSquares(1);
} else {
#if 0
// [HGM] this must be done after the move is made, as with arrow it could lead to a board redraw with piece still on from square
if(x == killX && y == killY) killX = kill2X, killY = kill2Y, kill2X = kill2Y = -1; // cancel last kill
else {
kill2X = killX; kill2Y = killY;
- killX = x; killY = y; //remeber this square as intermediate
+ killX = x; killY = y; // remember this square as intermediate
ReportClick("put", x, y); // and inform engine
ReportClick("lift", x, y);
MarkTargetSquares(0);
DragPieceEnd(xPix, yPix); dragging = 0;
/* Don't animate move and drag both */
appData.animate = FALSE;
+ MarkTargetSquares(-1); // -1 defers displaying marker change to prevent piece reappearing on from-square!
}
// moves into holding are invalid for now (except in EditPosition, adapting to-square)
PromotionPopUp(promoChoice);
} else {
int oldMove = currentMove;
+ flashing = 1; // prevent recursive calling (by release of to-click) while flashing piece
UserMoveEvent(fromX, fromY, toX, toY, promoChoice);
if (!appData.highlightLastMove || gotPremove) ClearHighlights();
- if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
+ if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY), DrawPosition(FALSE, NULL);
if(saveAnimate && !appData.animate && currentMove != oldMove && // drag-move was performed
Explode(boards[currentMove-1], fromX, fromY, toX, toY))
DrawPosition(TRUE, boards[currentMove]);
- MarkTargetSquares(1);
fromX = fromY = -1;
+ flashing = 0;
}
appData.animate = saveAnimate;
if (appData.animate || appData.animateDragging) {
/* Undo animation damage if needed */
- DrawPosition(FALSE, NULL);
+// DrawPosition(FALSE, NULL);
}
}
}
void
+Wheel (int dir, int x, int y)
+{
+ if(gameMode == EditPosition) {
+ int xSqr = EventToSquare(x, BOARD_WIDTH);
+ int ySqr = EventToSquare(y, BOARD_HEIGHT);
+ if(ySqr < 0 || xSqr < BOARD_LEFT || xSqr >= BOARD_RGHT) return;
+ if(flipView) xSqr = BOARD_WIDTH - 1 - xSqr; else ySqr = BOARD_HEIGHT - 1 - ySqr;
+ do {
+ boards[currentMove][ySqr][xSqr] += dir;
+ if((int) boards[currentMove][ySqr][xSqr] < WhitePawn) boards[currentMove][ySqr][xSqr] = BlackKing;
+ if((int) boards[currentMove][ySqr][xSqr] > BlackKing) boards[currentMove][ySqr][xSqr] = WhitePawn;
+ } while(PieceToChar(boards[currentMove][ySqr][xSqr]) == '.');
+ DrawPosition(FALSE, boards[currentMove]);
+ } else if(dir > 0) ForwardEvent(); else BackwardEvent();
+}
+
+void
SendProgramStatsToFrontend (ChessProgramState * cps, ChessProgramStats * cpstats)
{
// char * hint = lastHint;
if(stats.pv && stats.pv[0]) safeStrCpy(lastPV[stats.which], stats.pv, sizeof(lastPV[stats.which])/sizeof(lastPV[stats.which][0])); // [HGM] pv: remember last PV of each
+ if( gameMode == AnalyzeMode && stats.pv && stats.pv[0]
+ && appData.analysisBell && stats.time >= 100*appData.analysisBell ) RingBell();
+
SetProgramStats( &stats );
}
void
HandleMachineMove (char *message, ChessProgramState *cps)
{
- static char firstLeg[20];
+ static char firstLeg[20], legs;
char machineMove[MSG_SIZ], buf1[MSG_SIZ*10], buf2[MSG_SIZ];
char realname[MSG_SIZ];
int fromX, fromY, toX, toY;
// [HGM] lion: (some very limited) support for Alien protocol
killX = killY = kill2X = kill2Y = -1;
if(machineMove[strlen(machineMove)-1] == ',') { // move ends in coma: non-final leg of composite move
+ if(legs++) return; // middle leg contains only redundant info, ignore (but count it)
safeStrCpy(firstLeg, machineMove, 20); // just remember it for processing when second leg arrives
return;
}
if(p = strchr(machineMove, ',')) { // we got both legs in one (happens on book move)
+ char *q = strchr(p+1, ','); // second comma?
safeStrCpy(firstLeg, machineMove, 20); // kludge: fake we received the first leg earlier, and clip it off
+ if(q) legs = 2, p = q; else legs = 1; // with 3-leg move we clipof first two legs!
safeStrCpy(machineMove, firstLeg + (p - machineMove) + 1, 20);
}
if(firstLeg[0]) { // there was a previous leg;
while(isdigit(*q)) q++; // find start of to-square
safeStrCpy(machineMove, firstLeg, 20);
while(isdigit(*p)) p++; // to-square of first leg (which is now copied to machineMove)
- if(*p == *buf) // if first-leg to not equal to second-leg from first leg says unmodified (assume it ia King move of castling)
+ if(legs == 2) sscanf(p, "%c%d", &f, &kill2Y), kill2X = f - AAA, kill2Y -= ONE - '0'; // in 3-leg move 2nd kill is to-sqr of 1st leg
+ else if(*p == *buf) // if first-leg to not equal to second-leg from first leg says unmodified (assume it is King move of castling)
safeStrCpy(p, q, 20); // glue to-square of second leg to from-square of first, to process over-all move
sscanf(buf, "%c%d", &f, &killY); killX = f - AAA; killY -= ONE - '0'; // pass intermediate square to MakeMove in global
- firstLeg[0] = NULLCHAR;
+ firstLeg[0] = NULLCHAR; legs = 0;
}
if (!ParseOneMove(machineMove, forwardMostMove, &moveType,
}
if(appData.epd) {
if(solvingTime >= 0) {
- snprintf(buf1, MSG_SIZ, "%d. %4.2fs\n", matchGame, solvingTime/100.);
- totalTime += solvingTime; first.matchWins++;
+ snprintf(buf1, MSG_SIZ, "%d. %4.2fs: %s ", matchGame, solvingTime/100., parseList[backwardMostMove]);
+ totalTime += solvingTime; first.matchWins++; solvingTime = -1;
} else {
- snprintf(buf1, MSG_SIZ, "%d. wrong (%s)\n", matchGame, parseList[backwardMostMove]);
- second.matchWins++;
+ snprintf(buf1, MSG_SIZ, "%d. %s?%s ", matchGame, parseList[backwardMostMove], solvingTime == -2 ? " ???" : "");
+ if(solvingTime == -2) second.matchWins++;
}
OutputKibitz(2, buf1);
GameEnds(GameUnfinished, NULL, GE_XBOARD);
if(initPing == cps->lastPong) {
if(gameInfo.variant == VariantUnknown) {
DisplayError(_("Engine did not send setup for non-standard variant"), 0);
- *engineVariant = NULLCHAR; appData.variant = VariantNormal; // back to normal as error recovery?
+ *engineVariant = NULLCHAR; ASSIGN(appData.variant, "normal"); // back to normal as error recovery?
GameEnds(GameUnfinished, NULL, GE_XBOARD);
}
initPing = -1;
if (!ignore) {
ChessProgramStats tempStats = programStats; // [HGM] info: filter out info lines
+ int solved = 0;
buf1[0] = NULLCHAR;
if (sscanf(message, "%d%c %d %d " u64Display " %[^\n]\n",
&plylev, &plyext, &curscore, &time, &nodes, buf1) >= 5) {
if(*bestMove) { // rememer time best EPD move was first found
int ff1, tf1, fr1, tr1, ff2, tf2, fr2, tr2; char pp1, pp2;
- ChessMove mt;
- int ok = ParseOneMove(bestMove, forwardMostMove, &mt, &ff1, &fr1, &tf1, &tr1, &pp1);
- ok &= ParseOneMove(pv, forwardMostMove, &mt, &ff2, &fr2, &tf2, &tr2, &pp2);
- solvingTime = (ok && ff1==ff2 && fr1==fr2 && tf1==tf2 && tr1==tr2 && pp1==pp2 ? time : -1);
+ ChessMove mt; char *p = bestMove;
+ int ok = ParseOneMove(pv, forwardMostMove, &mt, &ff2, &fr2, &tf2, &tr2, &pp2);
+ solved = 0;
+ while(ok && *p && ParseOneMove(p, forwardMostMove, &mt, &ff1, &fr1, &tf1, &tr1, &pp1)) {
+ if(ff1==ff2 && fr1==fr2 && tf1==tf2 && tr1==tr2 && pp1==pp2) {
+ solvingTime = (solvingTime < 0 ? time : solvingTime);
+ solved = 1;
+ break;
+ }
+ while(*p && *p != ' ') p++;
+ while(*p == ' ') p++;
+ }
+ if(!solved) solvingTime = -1;
+ }
+ if(*avoidMove && !solved) {
+ int ff1, tf1, fr1, tr1, ff2, tf2, fr2, tr2; char pp1, pp2;
+ ChessMove mt; char *p = avoidMove, solved = 1;
+ int ok = ParseOneMove(pv, forwardMostMove, &mt, &ff2, &fr2, &tf2, &tr2, &pp2);
+ while(ok && *p && ParseOneMove(p, forwardMostMove, &mt, &ff1, &fr1, &tf1, &tr1, &pp1)) {
+ if(ff1==ff2 && fr1==fr2 && tf1==tf2 && tr1==tr2 && pp1==pp2) {
+ solved = 0; solvingTime = -2;
+ break;
+ }
+ while(*p && *p != ' ') p++;
+ while(*p == ' ') p++;
+ }
+ if(solved && !*bestMove) solvingTime = (solvingTime < 0 ? time : solvingTime);
}
if(serverMoves && (time > 100 || time == 0 && plylev > 7)) {
[AS] Protect the thinkOutput buffer from overflow... this
is only useful if buf1 hasn't overflowed first!
*/
+ if((gameMode == AnalyzeMode && appData.whitePOV || appData.scoreWhite) && !WhiteOnMove(forwardMostMove)) curscore *= -1;
if(curscore >= MATE_SCORE)
snprintf(score_buf, MSG_SIZ, "#%d", curscore - MATE_SCORE);
else if(curscore <= -MATE_SCORE)
if(toY == BOARD_HEIGHT-1) board[VIRGIN][toX] &= ~VIRGIN_B;
}
- if (fromX == toX && fromY == toY) return;
+ if (fromX == toX && fromY == toY && killX < 0) return;
piece = board[fromY][fromX]; /* [HGM] remember, for Shogi promotion */
king = piece < (int) BlackPawn ? WhiteKing : BlackKing; /* [HGM] Knightmate simplify testing for castling */
} else if (board[fromY][fromX] == king
&& fromX != BOARD_LEFT && fromX != BOARD_RGHT-1 // [HGM] cylinder */
&& toY == fromY && toX > fromX+1) {
- for(rookX=fromX+1; board[toY][rookX] == EmptySquare && rookX < BOARD_RGHT-1; rookX++); // castle with nearest piece
+ for(rookX=fromX+1; board[toY][rookX] == EmptySquare && rookX < BOARD_RGHT-1; rookX++)
+ ; // castle with nearest piece
board[fromY][toX-1] = board[fromY][rookX];
board[fromY][rookX] = EmptySquare;
board[fromY][fromX] = EmptySquare;
} else if (board[fromY][fromX] == king
&& fromX != BOARD_LEFT && fromX != BOARD_RGHT-1 // [HGM] cylinder */
&& toY == fromY && toX < fromX-1) {
- for(rookX=fromX-1; board[toY][rookX] == EmptySquare && rookX > 0; rookX--); // castle with nearest piece
+ for(rookX=fromX-1; board[toY][rookX] == EmptySquare && rookX > 0; rookX--)
+ ; // castle with nearest piece
board[fromY][toX+1] = board[fromY][rookX];
board[fromY][rookX] = EmptySquare;
board[fromY][fromX] = EmptySquare;
} else if (board[fromY][fromX] == king
&& fromX != BOARD_LEFT && fromX != BOARD_RGHT-1 // [HGM] cylinder */
&& toY == fromY && toX > fromX+1) {
- for(rookX=toX+1; board[toY][rookX] == EmptySquare && rookX < BOARD_RGHT - 1; rookX++);
+ for(rookX=toX+1; board[toY][rookX] == EmptySquare && rookX < BOARD_RGHT - 1; rookX++)
+ ;
board[fromY][toX-1] = board[fromY][rookX];
board[fromY][rookX] = EmptySquare;
board[fromY][fromX] = EmptySquare;
} else if (board[fromY][fromX] == king
&& fromX != BOARD_LEFT && fromX != BOARD_RGHT-1 // [HGM] cylinder */
&& toY == fromY && toX < fromX-1) {
- for(rookX=toX-1; board[toY][rookX] == EmptySquare && rookX > 0; rookX--);
+ for(rookX=toX-1; board[toY][rookX] == EmptySquare && rookX > 0; rookX--)
+ ;
board[fromY][toX+1] = board[fromY][rookX];
board[fromY][rookX] = EmptySquare;
board[fromY][fromX] = EmptySquare;
ChessSquare p = boards[forwardMostMove][toY][toX];
// forwardMostMove++; // [HGM] bare: moved downstream
+ if(kill2X >= 0) x = kill2X, y = kill2Y; else
if(killX >= 0 && killY >= 0) x = killX, y = killY; // [HGM] lion: make SAN move to intermediate square, if there is one
(void) CoordsToAlgebraic(boards[forwardMostMove],
PosFlags(forwardMostMove),
- fromY, fromX, y, x, promoChar,
+ fromY, fromX, y, x, (killX < 0)*promoChar,
s);
+ if(kill2X >= 0 && kill2Y >= 0)
+ sprintf(s + strlen(s), "x%c%d", killX + AAA, killY + ONE - '0'); // 2nd leg of 3-leg move is always capture
if(killX >= 0 && killY >= 0)
- sprintf(s + strlen(s), "%c%c%d", p == EmptySquare || toX == fromX && toY == fromY ? '-' : 'x', toX + AAA, toY + ONE - '0');
+ sprintf(s + strlen(s), "%c%c%d%c", p == EmptySquare || toX == fromX && toY == fromY || toX== kill2X && toY == kill2Y ? '-' : 'x',
+ toX + AAA, toY + ONE - '0', promoChar);
if(serverMoves != NULL) { /* [HGM] write moves on file for broadcasting (should be separate routine, really) */
int timeLeft; static int lastLoadFlag=0; int king, piece;
currentMove = forwardMostMove;
}
- killX = killY = -1; // [HGM] lion: used up
+ killX = killY = kill2X = kill2Y = -1; // [HGM] lion: used up
if (instant) return;
b = SupportedVariant(cps->variants, gameInfo.variant, gameInfo.boardWidth,
gameInfo.boardHeight, gameInfo.holdingsSize, cps->protocolVersion, cps->tidy);
+
if (b == NULL) {
VariantClass v;
char c, *q = cps->variants, *p = strchr(q, ',');
p++; q++;
}
if(*p) { // difference
- while(*p && *p++ != '\n');
- while(*q && *q++ != '\n');
+ while(*p && *p++ != '\n')
+ ;
+ while(*q && *q++ != '\n')
+ ;
changed = nPlayers;
changes = 1 + (strcmp(p, q) != 0);
}
res = LoadGameOrPosition(matchGame); // setup game
appData.noChessProgram = FALSE; // LoadGameOrPosition might call Reset too!
if(!res) return; // abort when bad game/pos file
+ if(appData.epd) {// in EPD mode we make sure first engine is to move
+ firstWhite = !(forwardMostMove & 1);
+ first.twoMachinesColor = firstWhite ? "white\n" : "black\n"; // perform actual color assignement
+ second.twoMachinesColor = firstWhite ? "black\n" : "white\n";
+ }
TwoMachinesEvent();
}
result, resultDetails ? resultDetails : "(null)", whosays);
}
- fromX = fromY = killX = killY = -1; // [HGM] abort any move the user is entering. // [HGM] lion
+ fromX = fromY = killX = killY = kill2X = kill2Y = -1; // [HGM] abort any move the user is entering. // [HGM] lion
if(pausing) PauseEvent(); // can happen when we abort a paused game (New Game or Quit)
return;
} else {
gameMode = nextGameMode;
+ if(appData.epd) {
+ snprintf(buf, MSG_SIZ, "-------------------------------------- ");
+ OutputKibitz(2, buf);
+ snprintf(buf, MSG_SIZ, _("Average solving time %4.2f sec (total time %4.2f sec) "), totalTime/(100.*first.matchWins), totalTime/100.);
+ OutputKibitz(2, buf);
+ snprintf(buf, MSG_SIZ, _("%d avoid-moves played "), second.matchWins);
+ if(second.matchWins) OutputKibitz(2, buf);
+ snprintf(buf, MSG_SIZ, _("Solved %d out of %d (%3.1f%%) "), first.matchWins, nextGame-1, first.matchWins*100./(nextGame-1));
+ OutputKibitz(2, buf);
+ }
snprintf(buf, MSG_SIZ, _("Match %s vs. %s: final score %d-%d-%d"),
first.tidy, second.tidy,
first.matchWins, second.matchWins,
lastHint[0] = NULLCHAR;
ClearGameInfo(&gameInfo);
gameInfo.variant = StringToVariant(appData.variant);
- if(gameInfo.variant == VariantNormal && strcmp(appData.variant, "normal")) gameInfo.variant = VariantUnknown;
+ if(gameInfo.variant == VariantNormal && strcmp(appData.variant, "normal")) {
+ gameInfo.variant = VariantUnknown;
+ strncpy(engineVariant, appData.variant, MSG_SIZ);
+ }
ics_user_moved = ics_clock_paused = FALSE;
ics_getting_history = H_FALSE;
ics_gamenum = -1;
ClearPremoveHighlights();
gotPremove = FALSE;
alarmSounded = FALSE;
- killX = killY = -1; // [HGM] lion
+ killX = killY = kill2X = kill2Y = -1; // [HGM] lion
GameEnds(EndOfFile, NULL, GE_PLAYER);
if(appData.serverMovesName != NULL) {
SetHighlights(-1, -1, toX, toY);
}
} else {
- int viaX = moveList[currentMove][5] - AAA;
- int viaY = moveList[currentMove][6] - ONE;
fromX = moveList[currentMove][0] - AAA;
fromY = moveList[currentMove][1] - ONE;
HistorySet(parseList, backwardMostMove, forwardMostMove, currentMove); /* [AS] */
if(moveList[currentMove][4] == ';') { // multi-leg
- ChessSquare piece = boards[currentMove][viaY][viaX];
- AnimateMove(boards[currentMove], fromX, fromY, viaX, viaY);
- boards[currentMove][viaY][viaX] = boards[currentMove][fromY][fromX];
- AnimateMove(boards[currentMove], fromX=viaX, fromY=viaY, toX, toY);
- boards[currentMove][viaY][viaX] = piece;
- } else
+ killX = moveList[currentMove][5] - AAA;
+ killY = moveList[currentMove][6] - ONE;
+ }
AnimateMove(boards[currentMove], fromX, fromY, toX, toY);
+ killX = killY = -1;
if (appData.highlightLastMove) {
SetHighlights(fromX, fromY, toX, toY);
toX = currentMoveString[2] - AAA;
toY = currentMoveString[3] - ONE;
promoChar = currentMoveString[4];
- if(promoChar == ';') promoChar = NULLCHAR;
+ if(promoChar == ';') promoChar = currentMoveString[7];
break;
case WhiteDrop:
thinkOutput[0] = NULLCHAR;
MakeMove(fromX, fromY, toX, toY, promoChar);
- killX = killY = -1; // [HGM] lion: used up
+ killX = killY = kill2X = kill2Y = -1; // [HGM] lion: used up
currentMove = forwardMostMove;
return TRUE;
}
char buf[MSG_SIZ];
int gn = gameNumber;
ListGame *lg = NULL;
- int numPGNTags = 0;
+ int numPGNTags = 0, i;
int err, pos = -1;
GameMode oldGameMode;
VariantClass v, oldVariant = gameInfo.variant; /* [HGM] PGNvariant */
if (gameMode != BeginningOfGame) {
Reset(FALSE, TRUE);
}
- killX = killY = -1; // [HGM] lion: in case we did not Reset
+ killX = killY = kill2X = kill2Y = -1; // [HGM] lion: in case we did not Reset
gameFileFP = f;
if (lastLoadGameFP != NULL && lastLoadGameFP != f) {
if (appData.debugMode)
fprintf(debugFP, "Parsed game start '%s' (%d)\n", yy_text, (int) cm);
+ for(i=0; i<EmptySquare; i++) { FREE(pieceDesc[i]); pieceDesc[i] = NULL; } // reset VariantMen
+
if (cm == XBoardGame) {
/* Skip any header junk before position diagram and/or move 1 */
for (;;) {
return FALSE;
}
CopyBoard(boards[0], initial_position);
- if(*engineVariant) // [HGM] for now, assume FEN in engine-defined variant game is default initial position
+ if(*engineVariant || gameInfo.variant == VariantFairy) // [HGM] for now, assume FEN in engine-defined variant game is default initial position
CopyBoard(initialPosition, initial_position);
if (blackPlaysFirst) {
currentMove = forwardMostMove = backwardMostMove = 1;
DisplayError(_("Bad FEN position in file"), 0);
return FALSE;
}
- if((p = strstr(line, ";")) && (p = strstr(p+1, "bm "))) { // EPD with best move
- sscanf(p+3, "%s", bestMove);
+ if((strchr(line, ';')) && (p = strstr(line, " bm "))) { // EPD with best move
+ sscanf(p+4, "%[^;]", bestMove);
} else *bestMove = NULLCHAR;
+ if((strchr(line, ';')) && (p = strstr(line, " am "))) { // EPD with avoid move
+ sscanf(p+4, "%[^;]", avoidMove);
+ } else *avoidMove = NULLCHAR;
} else {
(void) fgets(line, MSG_SIZ, f);
(void) fgets(line, MSG_SIZ, f);
ScheduleDelayedEvent(TwoMachinesEventIfReady, 10);
return;
}
+ if(!appData.epd) {
if(WaitForEngine(&second, TwoMachinesEventIfReady)) return; // (if needed:) started up second engine, so wait for features
if(!SupportedVariant(second.variants, gameInfo.variant, gameInfo.boardWidth,
ScheduleDelayedEvent(TwoMachinesEventIfReady, 10);
return;
}
+ }
GetTimeMark(&now); // [HGM] matchpause: implement match pause after engine load
if(appData.matchPause>10000 || appData.matchPause<10)
appData.matchPause = 10000; /* [HGM] make pause adjustable */
// we are now committed to starting the game
stalling = 0;
DisplayMessage("", "");
+ if(!appData.epd) {
if (startedFromSetupPosition) {
SendBoard(&second, backwardMostMove);
if (appData.debugMode) {
for (i = backwardMostMove; i < forwardMostMove; i++) {
SendMoveToProgram(i, &second);
}
+ }
gameMode = TwoMachinesPlay;
pausing = startingEngine = FALSE;
snprintf(buf, MSG_SIZ, "name %s\n", second.tidy);
SendToProgram(buf, &first);
}
+ if(!appData.epd) {
SendToProgram(second.computerString, &second);
if (second.sendName) {
snprintf(buf, MSG_SIZ, "name %s\n", first.tidy);
SendToProgram(buf, &second);
}
+ }
ResetClocks();
if (!first.sendTime || !second.sendTime) {
SetGameInfo();
}
-
void
EditPositionEvent ()
{
+ int i;
if (gameMode == EditPosition) {
EditGameEvent();
return;
gameMode = EditPosition;
ModeHighlight();
SetGameInfo();
+ CopyBoard(rightsBoard, nullBoard);
if (currentMove > 0)
CopyBoard(boards[0], boards[currentMove]);
+ for(i=0; i<nrCastlingRights; i++) if(boards[0][CASTLING][i] != NoRights)
+ rightsBoard[castlingRank[i]][boards[0][CASTLING][i]] = 1; // copy remaining rights
blackPlaysFirst = !WhiteOnMove(currentMove);
ResetClocks();
ChessSquare piece = boards[0][y][x];
static Board erasedBoard, currentBoard, menuBoard, nullBoard;
static int lastVariant;
+ int baseRank = BOARD_HEIGHT-1, hasRights = 0;
if (gameMode != EditPosition && gameMode != IcsExamining) return;
switch (selection) {
case ClearBoard:
- fromX = fromY = killX = killY = -1; // [HGM] abort any move entry in progress
+ fromX = fromY = killX = killY = kill2X = kill2Y = -1; // [HGM] abort any move entry in progress
MarkTargetSquares(1);
CopyBoard(currentBoard, boards[0]);
CopyBoard(menuBoard, initialPosition);
selection = (ChessSquare)((int)selection - (int)WhiteQueen + (int)WhiteFerz);
goto defaultlabel;
+ case WhiteRook:
+ baseRank = 0;
+ case BlackRook:
+ if(y == baseRank && (x == BOARD_LEFT || x == BOARD_RGHT-1 || appData.fischerCastling)) hasRights = 1;
+ goto defaultlabel;
+
case WhiteKing:
+ baseRank = 0;
case BlackKing:
if(gameInfo.variant == VariantXiangqi)
selection = (ChessSquare)((int)selection - (int)WhiteKing + (int)WhiteWazir);
if(gameInfo.variant == VariantKnightmate)
selection = (ChessSquare)((int)selection - (int)WhiteKing + (int)WhiteUnicorn);
+ if(y == baseRank && (x == BOARD_WIDTH>>1 || appData.fischerCastling)) hasRights = 1;
default:
defaultlabel:
if (gameMode == IcsExamining) {
PieceToChar(selection), AAA + x, ONE + y);
SendToICS(buf);
} else {
+ rightsBoard[y][x] = hasRights;
if(x < BOARD_LEFT || x >= BOARD_RGHT) {
int n;
if(x == BOARD_LEFT-2 && selection >= BlackPawn) {
seekGraphUp = FALSE;
MarkTargetSquares(1);
- fromX = fromY = killX = killY = -1; // [HGM] abort any move entry in progress
+ fromX = fromY = killX = killY = kill2X = kill2Y = -1; // [HGM] abort any move entry in progress
if (gameMode == PlayFromGameFile && !pausing)
PauseEvent();
SetHighlights(-1, -1, toX, toY);
}
} else {
- int viaX = moveList[target - 1][5] - AAA;
- int viaY = moveList[target - 1][6] - ONE;
fromX = moveList[target - 1][0] - AAA;
fromY = moveList[target - 1][1] - ONE;
if (target == currentMove + 1) {
if(moveList[target - 1][4] == ';') { // multi-leg
- ChessSquare piece = boards[currentMove][viaY][viaX];
- AnimateMove(boards[currentMove], fromX, fromY, viaX, viaY);
- boards[currentMove][viaY][viaX] = boards[currentMove][fromY][fromX];
- AnimateMove(boards[currentMove], viaX, viaY, toX, toY);
- boards[currentMove][viaY][viaX] = piece;
- } else
+ killX = moveList[target - 1][5] - AAA;
+ killY = moveList[target - 1][6] - ONE;
+ }
AnimateMove(boards[currentMove], fromX, fromY, toX, toY);
+ killX = killY = -1;
}
if (appData.highlightLastMove) {
SetHighlights(fromX, fromY, toX, toY);
if (gameMode == EditPosition) return;
seekGraphUp = FALSE;
MarkTargetSquares(1);
- fromX = fromY = killX = killY = -1; // [HGM] abort any move entry in progress
+ fromX = fromY = killX = killY = kill2X = kill2Y = -1; // [HGM] abort any move entry in progress
if (currentMove <= backwardMostMove) {
ClearHighlights();
DrawPosition(full_redraw, boards[currentMove]);
for (i = BOARD_HEIGHT - 1; i >= 0; i--) {
for (j = BOARD_LEFT; j < BOARD_RGHT; j++) {
char c = PieceToChar(boards[move][i][j]);
- fputc(c == 'x' ? '.' : c, fp);
+ fputc(c == '?' ? '.' : c, fp);
fputc(j == BOARD_RGHT - 1 ? '\n' : ' ', fp);
}
}
pvInfoList[index-1].score = score;
pvInfoList[index-1].time = 10*time; // centi-sec
if(*sep == '}') *sep = 0; else *--sep = '{';
- if(p != text) { while(*p++ = *sep++); sep = text; } // squeeze out space between PV and comment, and return both
+ if(p != text) {
+ while(*p++ = *sep++)
+ ;
+ sep = text;
+ } // squeeze out space between PV and comment, and return both
}
return sep;
}
}
if(q = overrideCastling) { // [HGM] FRC: override castling & e.p fields for non-compliant engines
- while(*p++ = *q++); if(q != overrideCastling+1) p[-1] = ' '; else --p;
+ while(*p++ = *q++)
+ ;
+ if(q != overrideCastling+1) p[-1] = ' '; else --p;
} else {
if(haveRights) {
int handW=0, handB=0;