* Massachusetts.
*
* Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
- * 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014 Free Software Foundation, Inc.
+ * 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Free Software Foundation, Inc.
*
* Enhancements Copyright 2005 Alessandro Scotti
*
int promoDefaultAltered;
int keepInfo = 0; /* [HGM] to protect PGN tags in auto-step game analysis */
static int initPing = -1;
-static Boolean pieceDefs;
+int border; /* [HGM] width of board rim, needed to size seek graph */
+char bestMove[MSG_SIZ];
+int solvingTime, totalTime;
/* States for ics_getting_history */
#define H_FALSE 0
if(cps->tidy == NULL) cps->tidy = (char*) malloc(MSG_SIZ);
TidyProgramName(cps->program, cps->host, cps->tidy);
cps->matchWins = 0;
- ASSIGN(cps->variants, appData.variant);
+ ASSIGN(cps->variants, appData.noChessProgram ? "" : appData.variant);
cps->analysisSupport = 2; /* detect */
cps->analyzing = FALSE;
cps->initDone = FALSE;
DisplayFatalError(buf, 0, 2);
return;
+ case VariantNormal: /* definitely works! */
+ if(strcmp(appData.variant, "normal") && !appData.noChessProgram) { // [HGM] hope this is an engine-defined variant
+ safeStrCpy(engineVariant, appData.variant, MSG_SIZ);
+ return;
+ }
case VariantXiangqi: /* [HGM] repetition rules not implemented */
case VariantFairy: /* [HGM] TestLegality definitely off! */
case VariantGothic: /* [HGM] should work */
case VariantFalcon: /* [HGM] untested */
case VariantCrazyhouse: /* holdings not shown, ([HGM] fixed that!)
offboard interposition not understood */
- case VariantNormal: /* definitely works! */
case VariantWildCastle: /* pieces not automatically shuffled */
case VariantNoCastle: /* pieces not automatically shuffled */
case VariantFischeRandom: /* [HGM] works and shuffles pieces */
int wnum = -1;
VariantClass v = VariantNormal;
int i, found = FALSE;
- char buf[MSG_SIZ];
+ char buf[MSG_SIZ], c;
int len;
if (!e) return v;
/* [HGM] skip over optional board-size prefixes */
- if( sscanf(e, "%dx%d_", &i, &i) == 2 ||
- sscanf(e, "%dx%d+%d_", &i, &i, &i) == 3 ) {
+ if( sscanf(e, "%dx%d_%c", &i, &i, &c) == 3 ||
+ sscanf(e, "%dx%d+%d_%c", &i, &i, &i, &c) == 4 ) {
while( *e++ != '_');
}
} else
for (i=0; i<sizeof(variantNames)/sizeof(char*); i++) {
if (p = StrCaseStr(e, variantNames[i])) {
- if(p && i >= VariantShogi && isalpha(p[strlen(variantNames[i])])) continue;
+ if(p && i >= VariantShogi && (p != e || isalpha(p[strlen(variantNames[i])]))) continue;
v = (VariantClass) i;
found = TRUE;
break;
{
int i;
if(!seekGraphUp) return FALSE;
- h = BOARD_HEIGHT * (squareSize + lineGap) + lineGap;
- w = BOARD_WIDTH * (squareSize + lineGap) + lineGap;
+ h = BOARD_HEIGHT * (squareSize + lineGap) + lineGap + 2*border;
+ w = BOARD_WIDTH * (squareSize + lineGap) + lineGap + 2*border;
DrawSeekBackground(0, 0, w, h);
DrawSeekAxis(hMargin, h-1-vMargin, w-5, h-1-vMargin);
else SendToProgram(moveList[moveNum], cps);
} else
if(moveList[moveNum][4] == ';') { // [HGM] lion: move is double-step over intermediate square
- snprintf(buf, MSG_SIZ, "%c%d%c%d,%c%d%c%d\n", moveList[moveNum][0], moveList[moveNum][1] - '0', // convert to two moves
- moveList[moveNum][5], moveList[moveNum][6] - '0',
- moveList[moveNum][5], moveList[moveNum][6] - '0',
- moveList[moveNum][2], moveList[moveNum][3] - '0');
+ char *m = moveList[moveNum];
+ 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
+ m[5], m[6] - '0',
+ m[5], m[6] - '0',
+ m[2], m[3] - '0');
SendToProgram(buf, cps);
} else
if(BOARD_HEIGHT > 10) { // [HGM] big: convert ranks to double-digit where needed
case WhitePromotion:
case BlackPromotion:
if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier ||
- gameInfo.variant == VariantMakruk || gameInfo.variant == VariantASEAN)
+ gameInfo.variant == VariantMakruk)
snprintf(user_move, MSG_SIZ, "%c%c%c%c=%c\n",
AAA + fromX, ONE + fromY, AAA + toX, ONE + toY,
PieceToChar(WhiteFerz));
SendToICS(ics_type == ICS_ICC ? "tag result Game in progress\n" : "commit\n");
}
-int killX = -1, killY = -1; // [HGM] lion: used for passing e.p. capture square to MakeMove
+int killX = -1, killY = -1, kill2X = -1, kill2Y = -1; // [HGM] lion: used for passing e.p. capture square to MakeMove
+int legNr = 1;
void
-CoordsToComputerAlgebraic (int rf, int ff, int rt, int ft, char promoChar, char move[7])
+CoordsToComputerAlgebraic (int rf, int ff, int rt, int ft, char promoChar, char move[9])
{
if (rf == DROP_RANK) {
if(ff == EmptySquare) sprintf(move, "@@@@\n"); else // [HGM] pass
if (promoChar == 'x' || promoChar == NULLCHAR) {
sprintf(move, "%c%c%c%c\n",
AAA + ff, ONE + rf, AAA + ft, ONE + rt);
- if(killX >= 0 && killY >= 0) sprintf(move+4, ";%c%c\n", AAA + killX, ONE + killY);
+ 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);
+ }
} else {
sprintf(move, "%c%c%c%c%c\n",
AAA + ff, ONE + rf, AAA + ft, ONE + rt, promoChar);
if(step && !(toggleFlag && Partner(&promoSweep))) promoSweep -= step;
if(promoSweep == EmptySquare) promoSweep = BlackPawn; // wrap
else if((int)promoSweep == -1) promoSweep = WhiteKing;
- else if(promoSweep == BlackPawn && step < 0) promoSweep = WhitePawn;
- else if(promoSweep == WhiteKing && step > 0) promoSweep = BlackKing;
+ else if(promoSweep == BlackPawn && step < 0 && !toggleFlag) promoSweep = WhitePawn;
+ else if(promoSweep == WhiteKing && step > 0 && !toggleFlag) promoSweep = BlackKing;
if(!step) step = -1;
} while(PieceToChar(promoSweep) == '.' || PieceToChar(promoSweep) == '~' || promoSweep == pawn ||
!toggleFlag && PieceToChar(promoSweep) == '+' || // skip promoted versions of other
case BlackASideCastleFR:
/* End of code added by Tord */
case IllegalMove: /* bug or odd chess variant */
+ if(currentMoveString[1] == '@') { // illegal drop
+ *fromX = WhiteOnMove(moveNum) ?
+ (int) CharToPiece(ToUpper(currentMoveString[0])) :
+ (int) CharToPiece(ToLower(currentMoveString[0]));
+ goto drop;
+ }
*fromX = currentMoveString[0] - AAA;
*fromY = currentMoveString[1] - ONE;
*toX = currentMoveString[2] - AAA;
*fromX = *moveType == WhiteDrop ?
(int) CharToPiece(ToUpper(currentMoveString[0])) :
(int) CharToPiece(ToLower(currentMoveString[0]));
+ drop:
*fromY = DROP_RANK;
*toX = currentMoveString[2] - AAA;
*toY = currentMoveString[3] - ONE;
}
int
-SetCharTable (char *table, const char * map)
+ptclen (const char *s, char *escapes)
+{
+ int n = 0;
+ if(!*escapes) return strlen(s);
+ while(*s) n += (*s != ':' && !strchr(escapes, *s)), s++;
+ return n;
+}
+
+int
+SetCharTableEsc (unsigned char *table, const char * map, char * escapes)
/* [HGM] moved here from winboard.c because of its general usefulness */
/* Basically a safe strcpy that uses the last character as King */
{
int result = FALSE; int NrPieces;
- if( map != NULL && (NrPieces=strlen(map)) <= (int) EmptySquare
+ if( map != NULL && (NrPieces=ptclen(map, escapes)) <= (int) EmptySquare
&& NrPieces >= 12 && !(NrPieces&1)) {
- int i; /* [HGM] Accept even length from 12 to 34 */
+ int i, j = 0; /* [HGM] Accept even length from 12 to 88 */
for( i=0; i<(int) EmptySquare; i++ ) table[i] = '.';
for( i=0; i<NrPieces/2-1; i++ ) {
- table[i] = map[i];
- table[i + (int)BlackPawn - (int) WhitePawn] = map[i+NrPieces/2];
+ char *p;
+ if(map[j] == ':' && *escapes) i = CHUPROMOTED WhitePawn, j++;
+ table[i] = map[j++];
+ if(p = strchr(escapes, map[j])) j++, table[i] += 64*(p - escapes + 1);
}
- table[(int) WhiteKing] = map[NrPieces/2-1];
- table[(int) BlackKing] = map[NrPieces-1];
+ table[(int) WhiteKing] = map[j++];
+ for( i=0; i<NrPieces/2-1; i++ ) {
+ char *p;
+ if(map[j] == ':' && *escapes) i = CHUPROMOTED WhitePawn, j++;
+ table[WHITE_TO_BLACK i] = map[j++];
+ if(p = strchr(escapes, map[j])) j++, table[WHITE_TO_BLACK i] += 64*(p - escapes + 1);
+ }
+ table[(int) BlackKing] = map[j++];
result = TRUE;
}
return result;
}
+int
+SetCharTable (unsigned char *table, const char * map)
+{
+ return SetCharTableEsc(table, map, "");
+}
+
void
Prelude (Board board)
{ // [HGM] superchess: random selection of exo-pieces
gameInfo.boardHeight = 8;
gameInfo.holdingsSize = 0;
nrCastlingRights = -1; /* [HGM] Kludge to indicate default should be used */
- for(i=0; i<BOARD_FILES-2; i++)
+ for(i=0; i<BOARD_FILES-6; i++)
initialPosition[CASTLING][i] = initialRights[i] = NoRights; /* but no rights yet */
initialPosition[EP_STATUS] = EP_NONE;
+ initialPosition[TOUCHED_W] = initialPosition[TOUCHED_B] = 0;
SetCharTable(pieceToChar, "PNBRQ...........Kpnbrq...........k");
if(startVariant == gameInfo.variant) // [HGM] nicks: enable nicknames in original variant
SetCharTable(pieceNickName, appData.pieceNickNames);
case VariantFalcon:
pieces = FalconArray;
gameInfo.boardWidth = 10;
- SetCharTable(pieceToChar, "PNBRQ.............FKpnbrq.............fk");
+ SetCharTable(pieceToChar, "PNBRQ............FKpnbrq............fk");
break;
case VariantXiangqi:
pieces = XiangqiArray;
gameInfo.boardWidth = 12;
gameInfo.boardHeight = 12;
nrCastlingRights = 0;
- SetCharTable(pieceToChar, "P.BRQSEXOGCATHD.VMLIFN+.++.++++++++++.+++++K"
- "p.brqsexogcathd.vmlifn+.++.++++++++++.+++++k");
+ SetCharTableEsc(pieceToChar, "P.BRQSEXOGCATHD.VMLIFN:+.++.++++++++++.+++++K"
+ "p.brqsexogcathd.vmlifn:+.++.++++++++++.+++++k", SUFFIXES);
break;
case VariantCourier:
pieces = CourierArray;
/* User pieceToChar list overrules defaults */
if(appData.pieceToCharTable != NULL)
- SetCharTable(pieceToChar, appData.pieceToCharTable);
+ SetCharTableEsc(pieceToChar, appData.pieceToCharTable, SUFFIXES);
for( j=0; j<BOARD_WIDTH; j++ ) { ChessSquare s = EmptySquare;
{
ChessSquare result;
if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier ||
- gameInfo.variant == VariantMakruk || gameInfo.variant == VariantASEAN)
+ gameInfo.variant == VariantMakruk)
result = WhiteFerz; // no choice
+ else if(gameInfo.variant == VariantASEAN)
+ result = WhiteRook; // no choice
else if(gameInfo.variant == VariantSuicide || gameInfo.variant == VariantGiveaway)
result= WhiteKing; // in Suicide Q is the last thing we want
else if(gameInfo.variant == VariantSpartan)
int p = piece >= BlackPawn ? BLACK_TO_WHITE piece : piece;
promotionZoneSize = BOARD_HEIGHT/3;
highestPromotingPiece = (p >= WhiteLion || PieceToChar(piece + 22) == '.') ? WhitePawn : WhiteLion;
- } else if(gameInfo.variant == VariantShogi || gameInfo.variant == VariantChuChess) {
- promotionZoneSize = BOARD_HEIGHT/3;
+ } else if(gameInfo.variant == VariantShogi) {
+ promotionZoneSize = BOARD_HEIGHT/3 +(BOARD_HEIGHT == 8);
highestPromotingPiece = (int)WhiteAlfil;
} else if(gameInfo.variant == VariantMakruk || gameInfo.variant == VariantGrand || gameInfo.variant == VariantChuChess) {
promotionZoneSize = 3;
// we either have a choice what to promote to, or (in Shogi) whether to promote
if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier ||
- gameInfo.variant == VariantMakruk || gameInfo.variant == VariantASEAN) {
+ gameInfo.variant == VariantMakruk) {
ChessSquare p=BlackFerz; // no choice
while(p < EmptySquare) { //but make sure we use piece that exists
*promoChoice = PieceToChar(p++);
/* EditPosition, empty square, or different color piece;
click-click move is possible */
if (toX == -2 || toY == -2) {
- boards[0][fromY][fromX] = EmptySquare;
+ boards[0][fromY][fromX] = (boards[0][fromY][fromX] == EmptySquare ? DarkSquare : EmptySquare);
DrawPosition(FALSE, boards[currentMove]);
return;
} else if (toX >= 0 && toY >= 0) {
// holdings might not be sent yet in ICS play; we have to figure out which piece belongs here
if(fromX == 0) fromY = BOARD_HEIGHT-1 - fromY; // black holdings upside-down
fromX = fromX ? WhitePawn : BlackPawn; // first piece type in selected holdings
- while(PieceToChar(fromX) == '.' || PieceToNumber(fromX) != fromY && fromX != (int) EmptySquare) fromX++;
+ while(PieceToChar(fromX) == '.' || PieceToChar(fromX) == '+' || PieceToNumber(fromX) != fromY && fromX != (int) EmptySquare) fromX++;
fromY = DROP_RANK;
}
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');
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) || abs(ft-killX) < 2 && abs(rt-killY) < 2))
+ if(rf == fromY && ff == fromX && (killX < 0 ? !(rt == rf && ft == ff) && legNr & 1 : rt == killY && ft == killX || legNr & 2))
(*m)[rt][ft] = 1 + (board[rt][ft] != EmptySquare
|| kind == WhiteCapturesEnPassant
- || kind == BlackCapturesEnPassant) + 3*(kind == FirstLeg && killX < 0);
- else if(flags & F_MANDATORY_CAPTURE && board[rt][ft] != EmptySquare) (*m)[rt][ft] = 3;
+ || kind == BlackCapturesEnPassant) + 3*(kind == FirstLeg && killX < 0), legal[rt][ft] = 1;
+ else if(flags & F_MANDATORY_CAPTURE && board[rt][ft] != EmptySquare) (*m)[rt][ft] = 3, legal[rt][ft] = 1;
}
static int hoverSavedValid;
if(IS_SHOGI(gameInfo.variant) || gameInfo.variant == VariantXiangqi ||
gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat ||
gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier ||
- gameInfo.variant == VariantMakruk || gameInfo.variant == VariantASEAN) return FALSE;
+ gameInfo.variant == VariantMakruk) return FALSE;
return (piece == BlackPawn && y <= zone ||
piece == WhitePawn && y >= BOARD_HEIGHT-1-zone ||
- piece == BlackLance && y == 1 ||
- piece == WhiteLance && y == BOARD_HEIGHT-2 );
+ piece == BlackLance && y <= zone ||
+ piece == WhiteLance && y >= BOARD_HEIGHT-1-zone );
}
void
char buf[MSG_SIZ]; // Inform engine of what user does
int r, f;
if(action[0] == 'l') // mark any target square of a lifted piece as legal to-square, clear markers
- for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) legal[r][f] = 1, marker[r][f] = 0;
+ for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++)
+ legal[r][f] = !pieceDefs || !appData.markers, marker[r][f] = 0;
if(!first.highlight || gameMode == EditPosition) return;
snprintf(buf, MSG_SIZ, "%s %c%d%s\n", action, x+AAA, y+ONE-'0', controlKey && action[0]=='p' ? "," : "");
SendToProgram(buf, &first);
}
+Boolean right; // instructs front-end to use button-1 events as if they were button 3
+
void
LeftClick (ClickType clickType, int xPix, int yPix)
{
ChessSquare piece;
static TimeMark lastClickTime, prevClickTime;
- if(SeekGraphClick(clickType, xPix, yPix, 0)) return;
-
- prevClickTime = lastClickTime; GetTimeMark(&lastClickTime);
-
- if (clickType == Press) ErrorPopDown();
- lastClickType = clickType, lastLeftX = xPix, lastLeftY = yPix; // [HGM] alien: remember state
-
x = EventToSquare(xPix, BOARD_WIDTH);
y = EventToSquare(yPix, BOARD_HEIGHT);
if (!flipView && y >= 0) {
x = BOARD_WIDTH - 1 - x;
}
+ if(appData.monoMouse && gameMode == EditPosition && fromX < 0 && clickType == Press && boards[currentMove][y][x] == EmptySquare) {
+ static int dummy;
+ RightClick(clickType, xPix, yPix, &dummy, &dummy);
+ right = TRUE;
+ return;
+ }
+
+ if(SeekGraphClick(clickType, xPix, yPix, 0)) return;
+
+ prevClickTime = lastClickTime; GetTimeMark(&lastClickTime);
+
+ if (clickType == Press) ErrorPopDown();
+ lastClickType = clickType, lastLeftX = xPix, lastLeftY = yPix; // [HGM] alien: remember state
+
if(promoSweep != EmptySquare) { // up-click during sweep-select of promo-piece
defaultPromoChoice = promoSweep;
promoSweep = EmptySquare; // terminate sweep
return;
}
}
-
+printf("to click %d,%d\n",x,y);
/* fromX != -1 */
if (clickType == Press && gameMode != EditPosition) {
ChessSquare fromP;
toP = boards[currentMove][y][x];
frc = appData.fischerCastling || gameInfo.variant == VariantSChess;
if( (killX < 0 || x != fromX || y != fromY) && // [HGM] lion: do not interpret igui as deselect!
+ marker[y][x] == 0 && // if engine told we can move to here, do it even if own piece
((WhitePawn <= fromP && fromP <= WhiteKing &&
WhitePawn <= toP && toP <= WhiteKing &&
!(fromP == WhiteKing && toP == WhiteRook && frc) &&
else gatingPiece = doubleClick ? fromP : EmptySquare;
fromX = x;
fromY = y; dragging = 1;
- ReportClick("lift", x, y);
+ if(!second) ReportClick("lift", x, y);
MarkTargetSquares(0);
DragPieceBegin(xPix, yPix, FALSE);
if(appData.sweepSelect && CanPromote(piece = boards[currentMove][y][x], y)) {
// ignore clicks on holdings
if(x < BOARD_LEFT || x >= BOARD_RGHT) return;
}
+printf("A type=%d\n",clickType);
- if (clickType == Release && x == fromX && y == fromY && killX < 0) {
+ if(x == fromX && y == fromY && gameMode == EditPosition && SubtractTimeMarks(&lastClickTime, &prevClickTime) < 200) {
+ gatingPiece = boards[currentMove][fromY][fromX]; // prepare to copy rather than move
+ return;
+ }
+
+ if (clickType == Release && x == fromX && y == fromY && killX < 0 && !sweepSelecting) {
DragPieceEnd(xPix, yPix); dragging = 0;
if(clearFlag) {
// a deferred attempt to click-click move an empty square on top of a piece
/* Undo animation damage if any */
DrawPosition(FALSE, NULL);
}
- if (second || sweepSelecting) {
+ if (second) {
/* Second up/down in same square; just abort move */
- if(sweepSelecting) DrawPosition(FALSE, boards[currentMove]);
- second = sweepSelecting = 0;
+ second = 0;
fromX = fromY = -1;
gatingPiece = EmptySquare;
MarkTargetSquares(1);
}
clearFlag = 0;
-
- if(gameMode != EditPosition && !appData.testLegality && !legal[y][x] && (x != killX || y != killY) && !sweepSelecting) {
+printf("B\n");
+ if(gameMode != EditPosition && !appData.testLegality && !legal[y][x] &&
+ fromX >= BOARD_LEFT && fromX < BOARD_RGHT && (x != killX || y != killY) && !sweepSelecting) {
if(dragging) DragPieceEnd(xPix, yPix), dragging = 0;
DisplayMessage(_("only marked squares are legal"),"");
DrawPosition(TRUE, NULL);
return; // ignore to-click
}
-
+printf("(%d,%d)-(%d,%d) %d %d\n",fromX,fromY,toX,toY,x,y);
/* we now have a different from- and (possibly off-board) to-square */
/* Completed move */
if(!sweepSelecting) {
if(marker[y][x] == 5) { // [HGM] lion: this was the release of a to-click or drag on a cyan square
dragging *= 2; // flag button-less dragging if we are dragging
MarkTargetSquares(1);
- if(x == killX && y == killY) killX = killY = -1; else {
+ 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
ReportClick("put", x, y); // and inform engine
ReportClick("lift", x, y);
if(gatingPiece != EmptySquare && gameInfo.variant == VariantSChess) promoChoice = ToLower(PieceToChar(gatingPiece));
- if (HasPromotionChoice(fromX, fromY, toX, toY, &promoChoice, appData.sweepSelect)) {
+ if(legal[toY][toX] == 2) promoChoice = ToLower(PieceToChar(defaultPromoChoice)); // highlight-induced promotion
+
+ if (legal[toY][toX] == 2 && !appData.sweepSelect || HasPromotionChoice(fromX, fromY, toX, toY, &promoChoice, appData.sweepSelect)) {
SetHighlights(fromX, fromY, toX, toY);
MarkTargetSquares(1);
if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat || gameInfo.variant == VariantGrand) {
if(cps->alphaRank) AlphaRank(machineMove, 4);
// [HGM] lion: (some very limited) support for Alien protocol
- killX = killY = -1;
+ killX = killY = kill2X = kill2Y = -1;
if(machineMove[strlen(machineMove)-1] == ',') { // move ends in coma: non-final leg of composite move
safeStrCpy(firstLeg, machineMove, 20); // just remember it for processing when second leg arrives
return;
- } else if(firstLeg[0]) { // there was a previous leg;
- // only support case where same piece makes two step (and don't even test that!)
+ }
+ if(p = strchr(machineMove, ',')) { // we got both legs in one (happens on book move)
+ safeStrCpy(firstLeg, machineMove, 20); // kludge: fake we received the first leg earlier, and clip it off
+ safeStrCpy(machineMove, firstLeg + (p - machineMove) + 1, 20);
+ }
+ if(firstLeg[0]) { // there was a previous leg;
+ // only support case where same piece makes two step
char buf[20], *p = machineMove+1, *q = buf+1, f;
safeStrCpy(buf, machineMove, 20);
while(isdigit(*q)) q++; // find start of to-square
safeStrCpy(machineMove, firstLeg, 20);
- while(isdigit(*p)) p++;
+ 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)
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;
snprintf(buf1, MSG_SIZ*10, _("Illegal move \"%s\" from %s machine"),
machineMove, _(cps->which));
DisplayMoveError(buf1);
- snprintf(buf1, MSG_SIZ*10, "Xboard: Forfeit due to invalid move: %s (%c%c%c%c via %c%c) res=%d",
- machineMove, fromX+AAA, fromY+ONE, toX+AAA, toY+ONE, killX+AAA, killY+ONE, moveType);
+ snprintf(buf1, MSG_SIZ*10, "Xboard: Forfeit due to invalid move: %s (%c%c%c%c via %c%c, %c%c) res=%d",
+ machineMove, fromX+AAA, fromY+ONE, toX+AAA, toY+ONE, killX+AAA, killY+ONE, kill2X+AAA, kill2Y+ONE, moveType);
if (gameMode == TwoMachinesPlay) {
GameEnds(machineWhite ? BlackWins : WhiteWins,
buf1, GE_XBOARD);
free(fen);
GameEnds(GameUnfinished, NULL, GE_XBOARD);
}
+ if(appData.epd) {
+ if(solvingTime >= 0) {
+ snprintf(buf1, MSG_SIZ, "%d. %4.2fs\n", matchGame, solvingTime/100.);
+ totalTime += solvingTime; first.matchWins++;
+ } else {
+ snprintf(buf1, MSG_SIZ, "%d. wrong (%s)\n", matchGame, parseList[backwardMostMove]);
+ second.matchWins++;
+ }
+ OutputKibitz(2, buf1);
+ GameEnds(GameUnfinished, NULL, GE_XBOARD);
+ }
/* [AS] Adjudicate game if needed (note: remember that forwardMostMove now points past the last move) */
if( gameMode == TwoMachinesPlay && appData.adjudicateLossThreshold != 0 && forwardMostMove >= adjudicateLossPlies ) {
if( count & 1 ) {
score = -score; /* Flip score for winning side */
}
-printf("score=%d count=%d\n",score,count);
+
if( score > appData.adjudicateLossThreshold ) {
break;
}
if(appData.icsActive || forwardMostMove != 0 || cps != &first) return;
*buf = NULLCHAR;
if(sscanf(message, "setup (%s", buf) == 1) {
- s = 8 + strlen(buf), buf[s-9] = NULLCHAR, SetCharTable(pieceToChar, buf);
+ s = 8 + strlen(buf), buf[s-9] = NULLCHAR, SetCharTableEsc(pieceToChar, buf, SUFFIXES);
ASSIGN(appData.pieceToCharTable, buf);
}
- if(startedFromSetupPosition) return;
dummy = sscanf(message+s, "%dx%d+%d_%s", &w, &h, &hand, varName);
if(dummy >= 3) {
while(message[s] && message[s++] != ' ');
appData.NrFiles = w; appData.NrRanks = h; appData.holdingsSize = hand;
if(dummy == 4) gameInfo.variant = StringToVariant(varName); // parent variant
InitPosition(1); // calls InitDrawingSizes to let new parameters take effect
- if(*buf) SetCharTable(pieceToChar, buf); // do again, for it was spoiled by InitPosition
+ if(*buf) SetCharTableEsc(pieceToChar, buf, SUFFIXES); // do again, for it was spoiled by InitPosition
+ startedFromSetupPosition = FALSE;
}
}
+ if(startedFromSetupPosition) return;
ParseFEN(boards[0], &dummy, message+s, FALSE);
DrawPosition(TRUE, boards[0]);
+ CopyBoard(initialPosition, boards[0]);
startedFromSetupPosition = TRUE;
return;
}
- if(sscanf(message, "piece %c %s", &promoChar, buf1) == 2) {
- ChessSquare piece = CharToPiece(promoChar);
- if(piece < EmptySquare && !appData.testLegality) { ASSIGN(pieceDesc[piece], buf1); pieceDefs = TRUE; }
+ if(sscanf(message, "piece %s %s", buf2, buf1) == 2) {
+ ChessSquare piece = WhitePawn;
+ char *p=buf2, *q, *s = SUFFIXES, ID = *p;
+ if(*p == '+') piece = CHUPROMOTED WhitePawn, ID = *++p;
+ if(q = strchr(s, p[1])) ID += 64*(q - s + 1), p++;
+ piece += CharToPiece(ID) - WhitePawn;
+ if(cps != &first || appData.testLegality && *engineVariant == NULLCHAR
+ /* always accept definition of */ && piece != WhiteFalcon && piece != BlackFalcon
+ /* wild-card pieces. */ && piece != WhiteCobra && piece != BlackCobra
+ /* For variants we don't have */ && gameInfo.variant != VariantBerolina
+ /* correct rules for, we cannot */ && gameInfo.variant != VariantCylinder
+ /* enforce legality on our own! */ && gameInfo.variant != VariantUnknown
+ && gameInfo.variant != VariantGreat
+ && gameInfo.variant != VariantFairy ) return;
+ if(piece < EmptySquare) {
+ pieceDefs = TRUE;
+ ASSIGN(pieceDesc[piece], buf1);
+ if(isupper(*p) && p[1] == '&') { ASSIGN(pieceDesc[WHITE_TO_BLACK piece], buf1); }
+ }
return;
}
/* [HGM] Allow engine to set up a position. Don't ask me why one would
return;
}
if(!strncmp(message, "highlight ", 10)) {
- if(appData.testLegality && appData.markers) return;
+ if(appData.testLegality && !*engineVariant && appData.markers) return;
MarkByFEN(message+10); // [HGM] alien: allow engine to mark board squares
return;
}
buf1[0] = NULLCHAR;
if (sscanf(message, "%d%c %d %d " u64Display " %[^\n]\n",
&plylev, &plyext, &curscore, &time, &nodes, buf1) >= 5) {
+ char score_buf[MSG_SIZ];
if(nodes>>32 == u64Const(0xFFFFFFFF)) // [HGM] negative node count read
nodes += u64Const(0x100000000);
if(appData.pvSAN[cps==&second]) pv = PvToSAN(buf1);
+ 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);
+ }
+
if(serverMoves && (time > 100 || time == 0 && plylev > 7)) {
char buf[MSG_SIZ];
FILE *f;
[AS] Protect the thinkOutput buffer from overflow... this
is only useful if buf1 hasn't overflowed first!
*/
- snprintf(thinkOutput, sizeof(thinkOutput)/sizeof(thinkOutput[0]), "[%d]%c%+.2f %s%s",
+ if(curscore >= MATE_SCORE)
+ snprintf(score_buf, MSG_SIZ, "#%d", curscore - MATE_SCORE);
+ else if(curscore <= -MATE_SCORE)
+ snprintf(score_buf, MSG_SIZ, "#%d", curscore + MATE_SCORE);
+ else
+ snprintf(score_buf, MSG_SIZ, "%+.2f", ((double) curscore) / 100.0);
+ snprintf(thinkOutput, sizeof(thinkOutput)/sizeof(thinkOutput[0]), "[%d]%c%s %s%s",
plylev,
(gameMode == TwoMachinesPlay ?
ToUpper(cps->twoMachinesColor[0]) : ' '),
- ((double) curscore) / 100.0,
+ score_buf,
prefixHint ? lastHint : "",
prefixHint ? " " : "" );
void
ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board)
{
- ChessSquare captured = board[toY][toX], piece, king; int p, oldEP = EP_NONE, berolina = 0;
+ ChessSquare captured = board[toY][toX], piece, pawn, king, killed, killed2; int p, rookX, oldEP, epRank, berolina = 0;
int promoRank = gameInfo.variant == VariantMakruk || gameInfo.variant == VariantGrand || gameInfo.variant == VariantChuChess ? 3 : 1;
/* [HGM] compute & store e.p. status and castling rights for new position */
/* we can always do that 'in place', now pointers to these rights are passed to ApplyMove */
if(gameInfo.variant == VariantBerolina) berolina = EP_BEROLIN_A;
- oldEP = (signed char)board[EP_STATUS];
+ oldEP = (signed char)board[EP_FILE]; epRank = board[EP_RANK];
board[EP_STATUS] = EP_NONE;
+ board[EP_FILE] = board[EP_RANK] = 100;
if (fromY == DROP_RANK) {
/* must be first */
// ChessSquare victim;
int i;
- if( killX >= 0 && killY >= 0 ) // [HGM] lion: Lion trampled over something
+ if( killX >= 0 && killY >= 0 ) { // [HGM] lion: Lion trampled over something
// victim = board[killY][killX],
+ killed = board[killY][killX],
board[killY][killX] = EmptySquare,
board[EP_STATUS] = EP_CAPTURE;
+ if( kill2X >= 0 && kill2Y >= 0)
+ killed2 = board[kill2Y][kill2X], board[kill2Y][kill2X] = EmptySquare;
+ }
if( board[toY][toX] != EmptySquare ) {
board[EP_STATUS] = EP_CAPTURE;
}
}
- if( board[fromY][fromX] == WhiteLance || board[fromY][fromX] == BlackLance ) {
- if( gameInfo.variant != VariantSuper && gameInfo.variant != VariantShogi )
- board[EP_STATUS] = EP_PAWN_MOVE; // Lance is Pawn-like in most variants
- } else
- if( board[fromY][fromX] == WhitePawn ) {
+ pawn = board[fromY][fromX];
+ if( pawn == WhiteLance || pawn == BlackLance ) {
+ if( gameInfo.variant != VariantSuper && gameInfo.variant != VariantChu ) {
+ if(gameInfo.variant == VariantSpartan) board[EP_STATUS] = EP_PAWN_MOVE; // in Spartan no e.p. rights must be set
+ else pawn += WhitePawn - WhiteLance; // Lance is Pawn-like in most variants, so let Pawn code treat it by this kludge
+ }
+ }
+ if( pawn == WhitePawn ) {
if(fromY != toY) // [HGM] Xiangqi sideway Pawn moves should not count as 50-move breakers
board[EP_STATUS] = EP_PAWN_MOVE;
- if( toY-fromY==2) {
+ if( toY-fromY>=2) {
+ board[EP_FILE] = (fromX + toX)/2; board[EP_RANK] = toY - 1 | 128*(toY - fromY > 2);
if(toX>BOARD_LEFT && board[toY][toX-1] == BlackPawn &&
gameInfo.variant != VariantBerolina || toX < fromX)
board[EP_STATUS] = toX | berolina;
board[EP_STATUS] = toX;
}
} else
- if( board[fromY][fromX] == BlackPawn ) {
+ if( pawn == BlackPawn ) {
if(fromY != toY) // [HGM] Xiangqi sideway Pawn moves should not count as 50-move breakers
board[EP_STATUS] = EP_PAWN_MOVE;
- if( toY-fromY== -2) {
+ if( toY-fromY<= -2) {
+ board[EP_FILE] = (fromX + toX)/2; board[EP_RANK] = toY + 1 | 128*(fromY - toY > 2);
if(toX>BOARD_LEFT && board[toY][toX-1] == WhitePawn &&
gameInfo.variant != VariantBerolina || toX < fromX)
board[EP_STATUS] = toX | berolina;
}
}
+ if(fromY == 0) board[TOUCHED_W] |= 1<<fromX; else // new way to keep track of virginity
+ if(fromY == BOARD_HEIGHT-1) board[TOUCHED_B] |= 1<<fromX;
+ if(toY == 0) board[TOUCHED_W] |= 1<<toX; else
+ if(toY == BOARD_HEIGHT-1) board[TOUCHED_B] |= 1<<toX;
+
for(i=0; i<nrCastlingRights; i++) {
if(board[CASTLING][i] == fromX && castlingRank[i] == fromY ||
board[CASTLING][i] == toX && castlingRank[i] == toY
if(gameInfo.variant == VariantKnightmate)
king += (int) WhiteUnicorn - (int) WhiteKing;
+ if(pieceDesc[piece] && killX >= 0 && strchr(pieceDesc[piece], 'O') // Betza castling-enabled
+ && (piece < BlackPawn ? killed < BlackPawn : killed >= BlackPawn)) { // and tramples own
+ board[toY][toX] = piece; board[fromY][fromX] = EmptySquare;
+ board[toY][toX + (killX < fromX ? 1 : -1)] = killed;
+ board[EP_STATUS] = EP_NONE; // capture was fake!
+ } else
/* Code added by Tord: */
/* FRC castling assumed when king captures friendly rook. [HGM] or RxK for S-Chess */
if (board[fromY][fromX] == WhiteKing && board[toY][toX] == WhiteRook ||
board[fromY][fromX] == WhiteRook && board[toY][toX] == WhiteKing) {
+ board[EP_STATUS] = EP_NONE; // capture was fake!
board[fromY][fromX] = EmptySquare;
board[toY][toX] = EmptySquare;
if((toX > fromX) != (piece == WhiteRook)) {
}
} else if (board[fromY][fromX] == BlackKing && board[toY][toX] == BlackRook ||
board[fromY][fromX] == BlackRook && board[toY][toX] == BlackKing) {
+ board[EP_STATUS] = EP_NONE;
board[fromY][fromX] = EmptySquare;
board[toY][toX] = EmptySquare;
if((toX > fromX) != (piece == BlackRook)) {
} 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
+ board[fromY][toX-1] = board[fromY][rookX];
+ board[fromY][rookX] = EmptySquare;
board[fromY][fromX] = EmptySquare;
board[toY][toX] = king;
- board[toY][toX-1] = board[fromY][BOARD_RGHT-1];
- board[fromY][BOARD_RGHT-1] = 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
+ board[fromY][toX+1] = board[fromY][rookX];
+ board[fromY][rookX] = EmptySquare;
board[fromY][fromX] = EmptySquare;
board[toY][toX] = king;
- board[toY][toX+1] = board[fromY][BOARD_LEFT];
- board[fromY][BOARD_LEFT] = EmptySquare;
} else if ((board[fromY][fromX] == WhitePawn && gameInfo.variant != VariantXiangqi ||
board[fromY][fromX] == WhiteLance && gameInfo.variant != VariantSuper && gameInfo.variant != VariantChu)
&& toY >= BOARD_HEIGHT-promoRank && promoChar // defaulting to Q is done elsewhere
board[toY][toX] = (ChessSquare) (PROMOTED board[toY][toX]);
board[fromY][fromX] = EmptySquare;
} else if ((fromY >= BOARD_HEIGHT>>1)
- && (oldEP == toX || oldEP == EP_UNKNOWN || appData.testLegality)
+ && (oldEP == toX || oldEP == EP_UNKNOWN || appData.testLegality || abs(toX - fromX) > 4)
&& (toX != fromX)
&& gameInfo.variant != VariantXiangqi
&& gameInfo.variant != VariantBerolina
- && (board[fromY][fromX] == WhitePawn)
+ && (pawn == WhitePawn)
&& (board[toY][toX] == EmptySquare)) {
board[fromY][fromX] = EmptySquare;
- board[toY][toX] = WhitePawn;
- captured = board[toY - 1][toX];
- board[toY - 1][toX] = EmptySquare;
+ board[toY][toX] = piece;
+ if(toY == epRank - 128 + 1)
+ captured = board[toY - 2][toX], board[toY - 2][toX] = EmptySquare;
+ else
+ captured = board[toY - 1][toX], board[toY - 1][toX] = EmptySquare;
} else if ((fromY == BOARD_HEIGHT-4)
&& (toX == fromX)
&& gameInfo.variant == VariantBerolina
} 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++);
+ board[fromY][toX-1] = board[fromY][rookX];
+ board[fromY][rookX] = EmptySquare;
board[fromY][fromX] = EmptySquare;
board[toY][toX] = king;
- board[toY][toX-1] = board[fromY][BOARD_RGHT-1];
- board[fromY][BOARD_RGHT-1] = 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--);
+ board[fromY][toX+1] = board[fromY][rookX];
+ board[fromY][rookX] = EmptySquare;
board[fromY][fromX] = EmptySquare;
board[toY][toX] = king;
- board[toY][toX+1] = board[fromY][BOARD_LEFT];
- board[fromY][BOARD_LEFT] = EmptySquare;
} else if (fromY == 7 && fromX == 3
&& board[fromY][fromX] == BlackKing
&& toY == 7 && toX == 5) {
board[toY][toX] = (ChessSquare) (PROMOTED board[toY][toX]);
board[fromY][fromX] = EmptySquare;
} else if ((fromY < BOARD_HEIGHT>>1)
- && (oldEP == toX || oldEP == EP_UNKNOWN || appData.testLegality)
+ && (oldEP == toX || oldEP == EP_UNKNOWN || appData.testLegality || abs(toX - fromX) > 4)
&& (toX != fromX)
&& gameInfo.variant != VariantXiangqi
&& gameInfo.variant != VariantBerolina
- && (board[fromY][fromX] == BlackPawn)
+ && (pawn == BlackPawn)
&& (board[toY][toX] == EmptySquare)) {
board[fromY][fromX] = EmptySquare;
- board[toY][toX] = BlackPawn;
- captured = board[toY + 1][toX];
- board[toY + 1][toX] = EmptySquare;
+ board[toY][toX] = piece;
+ if(toY == epRank - 128 - 1)
+ captured = board[toY + 2][toX], board[toY + 2][toX] = EmptySquare;
+ else
+ captured = board[toY + 1][toX], board[toY + 1][toX] = EmptySquare;
} else if ((fromY == 3)
&& (toX == fromX)
&& gameInfo.variant == VariantBerolina
p = (int) captured;
if (p >= (int) BlackPawn) {
p -= (int)BlackPawn;
- if(gameInfo.variant == VariantShogi && DEMOTED p >= 0) {
- /* in Shogi restore piece to its original first */
+ if(DEMOTED p >= 0 && PieceToChar(p) == '+') {
+ /* Restore shogi-promoted piece to its original first */
captured = (ChessSquare) (DEMOTED captured);
p = DEMOTED p;
}
board[p][BOARD_WIDTH-1] = BLACK_TO_WHITE captured;
} else {
p -= (int)WhitePawn;
- if(gameInfo.variant == VariantShogi && DEMOTED p >= 0) {
+ if(DEMOTED p >= 0 && PieceToChar(p) == '+') {
captured = (ChessSquare) (DEMOTED captured);
p = DEMOTED p;
}
b = SupportedVariant(cps->variants, gameInfo.variant, gameInfo.boardWidth,
gameInfo.boardHeight, gameInfo.holdingsSize, cps->protocolVersion, cps->tidy);
if (b == NULL) {
- DisplayFatalError(variantError, 0, 1);
+ VariantClass v;
+ char c, *q = cps->variants, *p = strchr(q, ',');
+ if(p) *p = NULLCHAR;
+ v = StringToVariant(q);
+ DisplayError(variantError, 0);
+ if(v != VariantUnknown && cps == &first) {
+ int w, h, s;
+ if(sscanf(q, "%dx%d+%d_%c", &w, &h, &s, &c) == 4) // get size overrides the engine needs with it (if any)
+ appData.NrFiles = w, appData.NrRanks = h, appData.holdingsSize = s, q = strchr(q, '_') + 1;
+ ASSIGN(appData.variant, q);
+ Reset(TRUE, FALSE);
+ }
+ if(p) *p = ',';
return;
}
if (appData.debugMode)
fprintf(debugFP, "Parsed %s into IllegalMove %s\n",
yy_text, currentMoveString);
- fromX = currentMoveString[0] - AAA;
- fromY = currentMoveString[1] - ONE;
+ if(currentMoveString[1] == '@') {
+ fromX = CharToPiece(WhiteOnMove(currentMove) ? ToUpper(currentMoveString[0]) : ToLower(currentMoveString[0]));
+ fromY = DROP_RANK;
+ } else {
+ fromX = currentMoveString[0] - AAA;
+ fromY = currentMoveString[1] - ONE;
+ }
toX = currentMoveString[2] - AAA;
toY = currentMoveString[3] - ONE;
promoChar = currentMoveString[4];
int numPGNTags = 0;
int err, pos = -1;
GameMode oldGameMode;
- VariantClass oldVariant = gameInfo.variant; /* [HGM] PGNvariant */
+ VariantClass v, oldVariant = gameInfo.variant; /* [HGM] PGNvariant */
+ char oldName[MSG_SIZ];
+
+ safeStrCpy(oldName, engineVariant, MSG_SIZ); v = oldVariant;
if (appData.debugMode)
fprintf(debugFP, "LoadGame(): on entry, gameMode %d\n", gameMode);
if (!err) numPGNTags++;
/* [HGM] PGNvariant: automatically switch to variant given in PGN tag */
- if(gameInfo.variant != oldVariant) {
+ if(gameInfo.variant != oldVariant && (gameInfo.variant != VariantNormal || gameInfo.variantName == NULL || *gameInfo.variantName == NULLCHAR)) {
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);
return FALSE;
}
CopyBoard(boards[0], initial_position);
+ if(*engineVariant) // [HGM] for now, assume FEN in engine-defined variant game is default initial position
+ CopyBoard(initialPosition, initial_position);
if (blackPlaysFirst) {
currentMove = forwardMostMove = backwardMostMove = 1;
CopyBoard(boards[1], initial_position);
StartChessProgram(&first);
}
InitChessProgram(&first, FALSE);
+ if(gameInfo.variant == VariantUnknown && *oldName) {
+ safeStrCpy(engineVariant, oldName, MSG_SIZ);
+ gameInfo.variant = v;
+ }
SendToProgram("force\n", &first);
if (startedFromSetupPosition) {
SendBoard(&first, forwardMostMove);
DisplayError(_("Position not found in file"), 0);
return FALSE;
}
- // [HGM] FEN can begin with digit, any piece letter valid in this variant, or a + for Shogi promoted pieces
- fenMode = line[0] >= '0' && line[0] <= '9' || line[0] == '+' || CharToPiece(line[0]) != EmptySquare;
+ // [HGM] FEN can begin with digit, any piece letter valid in this variant, or a + for Shogi promoted pieces (or * for blackout)
+ fenMode = line[0] >= '0' && line[0] <= '9' || line[0] == '+' || line[0] == '*' || CharToPiece(line[0]) != EmptySquare;
if (pn >= 2) {
if (fenMode || line[0] == '#') pn--;
}
if (fenMode) {
+ char *p;
if (!ParseFEN(initial_position, &blackPlaysFirst, line, TRUE)) {
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);
+ } else *bestMove = NULLCHAR;
} else {
(void) fgets(line, MSG_SIZ, f);
(void) fgets(line, MSG_SIZ, f);
if(!SupportedVariant(second.variants, gameInfo.variant, gameInfo.boardWidth,
gameInfo.boardHeight, gameInfo.holdingsSize, second.protocolVersion, second.tidy)) {
- startingEngine = FALSE;
+ startingEngine = matchMode = FALSE;
DisplayError("second engine does not play this", 0);
+ gameMode = TwoMachinesPlay; ModeHighlight(); // Needed to make sure menu item is unchecked
+ EditGameEvent(); // switch back to EditGame mode
return;
}
switch (selection) {
case ClearBoard:
+ fromX = fromY = killX = killY = -1; // [HGM] abort any move entry in progress
+ MarkTargetSquares(1);
CopyBoard(currentBoard, boards[0]);
CopyBoard(menuBoard, initialPosition);
if (gameMode == IcsExamining && ics_type == ICS_FICS) {
AAA + x, ONE + y);
SendToICS(buf);
}
- } else {
+ } else if(boards[0][y][x] != DarkSquare) {
if(boards[0][y][x] != p) nonEmpty++;
boards[0][y][x] = p;
}
seekGraphUp = FALSE;
MarkTargetSquares(1);
+ fromX = fromY = killX = killY = -1; // [HGM] abort any move entry in progress
if (gameMode == PlayFromGameFile && !pausing)
PauseEvent();
if (gameMode == EditPosition) return;
seekGraphUp = FALSE;
MarkTargetSquares(1);
+ fromX = fromY = killX = killY = -1; // [HGM] abort any move entry in progress
if (currentMove <= backwardMostMove) {
ClearHighlights();
DrawPosition(full_redraw, boards[currentMove]);
piece = (ChessSquare)(CHUDEMOTED piece);
}
*p++ = (piece == DarkSquare ? '*' : PieceToChar(piece));
+ if(*p = PieceSuffix(piece)) p++;
if(p[-1] == '~') {
/* [HGM] flag promoted pieces as '<promoted>~' (Crazyhouse) */
p[-1] = PieceToChar((ChessSquare)(CHUDEMOTED piece));
while(*p++ = *q++); if(q != overrideCastling+1) p[-1] = ' '; else --p;
} else {
if(nrCastlingRights) {
+ int handW=0, handB=0;
+ if(gameInfo.variant == VariantSChess) { // for S-Chess, all virgin backrank pieces must be listed
+ for(i=0; i<BOARD_HEIGHT; i++) handW += boards[move][i][BOARD_RGHT]; // count white held pieces
+ for(i=0; i<BOARD_HEIGHT; i++) handB += boards[move][i][BOARD_LEFT-1]; // count black held pieces
+ }
q = p;
if(appData.fischerCastling) {
+ if(handW) { // in shuffle S-Chess simply dump all virgin pieces
+ for(i=BOARD_RGHT-1; i>=BOARD_LEFT; i--)
+ if(boards[move][VIRGIN][i] & VIRGIN_W) *p++ = i + AAA + 'A' - 'a';
+ } else {
/* [HGM] write directly from rights */
if(boards[move][CASTLING][2] != NoRights &&
boards[move][CASTLING][0] != NoRights )
if(boards[move][CASTLING][2] != NoRights &&
boards[move][CASTLING][1] != NoRights )
*p++ = boards[move][CASTLING][1] + AAA + 'A' - 'a';
+ }
+ if(handB) {
+ for(i=BOARD_RGHT-1; i>=BOARD_LEFT; i--)
+ if(boards[move][VIRGIN][i] & VIRGIN_B) *p++ = i + AAA;
+ } else {
if(boards[move][CASTLING][5] != NoRights &&
boards[move][CASTLING][3] != NoRights )
*p++ = boards[move][CASTLING][3] + AAA;
if(boards[move][CASTLING][5] != NoRights &&
boards[move][CASTLING][4] != NoRights )
*p++ = boards[move][CASTLING][4] + AAA;
+ }
} else {
/* [HGM] write true castling rights */
boards[move][CASTLING][2] != NoRights ) k = 1, *p++ = 'K';
q = (boards[move][CASTLING][1] == BOARD_LEFT &&
boards[move][CASTLING][2] != NoRights );
- if(gameInfo.variant == VariantSChess) { // for S-Chess, indicate all vrgin backrank pieces
- for(i=j=0; i<BOARD_HEIGHT; i++) j += boards[move][i][BOARD_RGHT]; // count white held pieces
- for(i=BOARD_RGHT-1-k; i>=BOARD_LEFT+q && j; i--)
+ if(handW) { // for S-Chess with pieces in hand, list virgin pieces between K and Q
+ for(i=BOARD_RGHT-1-k; i>=BOARD_LEFT+q; i--)
if((boards[move][0][i] != WhiteKing || k+q == 0) &&
boards[move][VIRGIN][i] & VIRGIN_W) *p++ = i + AAA + 'A' - 'a';
}
boards[move][CASTLING][5] != NoRights ) k = 1, *p++ = 'k';
q = (boards[move][CASTLING][4] == BOARD_LEFT &&
boards[move][CASTLING][5] != NoRights );
- if(gameInfo.variant == VariantSChess) {
- for(i=j=0; i<BOARD_HEIGHT; i++) j += boards[move][i][BOARD_LEFT-1]; // count black held pieces
- for(i=BOARD_RGHT-1-k; i>=BOARD_LEFT+q && j; i--)
+ if(handB) {
+ for(i=BOARD_RGHT-1-k; i>=BOARD_LEFT+q; i--)
if((boards[move][BOARD_HEIGHT-1][i] != BlackKing || k+q == 0) &&
boards[move][VIRGIN][i] & VIRGIN_B) *p++ = i + AAA;
}
Boolean
ParseFEN (Board board, int *blackPlaysFirst, char *fen, Boolean autoSize)
{
- int i, j, k, w=0, subst=0, shuffle=0;
+ int i, j, k, w=0, subst=0, shuffle=0, wKingRank = -1, bKingRank = -1;
char *p, c;
int emptycount, virgin[BOARD_FILES];
ChessSquare piece;
if (i != 0 && i != BOARD_HEIGHT-1) return FALSE; // only on back-rank
board[i][(j++)+gameInfo.holdingsWidth] = ClearBoard; p++; subst++; // placeHolder
} else if (*p == '+' || isalpha(*p)) {
+ char *q, *s = SUFFIXES;
if (j >= gameInfo.boardWidth) return FALSE;
if(*p=='+') {
- piece = CharToPiece(*++p);
+ char c = *++p;
+ if(q = strchr(s, p[1])) p++;
+ piece = CharToPiece(c + (q ? 64*(q - s + 1) : 0));
if(piece == EmptySquare) return FALSE; /* unknown piece */
piece = (ChessSquare) (CHUPROMOTED piece ); p++;
if(PieceToChar(piece) != '+') return FALSE; /* unpromotable piece */
- } else piece = CharToPiece(*p++);
+ } else {
+ char c = *p++;
+ if(q = strchr(s, *p)) p++;
+ piece = CharToPiece(c + (q ? 64*(q - s + 1) : 0));
+ }
if(piece==EmptySquare) return FALSE; /* unknown piece */
if(*p == '~') { /* [HGM] make it a promoted piece for Crazyhouse */
p++;
}
board[i][(j++)+gameInfo.holdingsWidth] = piece;
+ if(piece == WhiteKing) wKingRank = i;
+ if(piece == BlackKing) bKingRank = i;
} else {
return FALSE;
}
/* [HGM] We NO LONGER ignore the rest of the FEN notation */
/* return the extra info in global variiables */
+ while(*p==' ') p++;
+
+ if(!isdigit(*p) && *p != '-') { // we seem to have castling rights. Make sure they are on the rank the King actually is.
+ if(wKingRank >= 0) for(i=0; i<3; i++) castlingRank[i] = wKingRank;
+ if(bKingRank >= 0) for(i=3; i<6; i++) castlingRank[i] = bKingRank;
+ }
+
/* set defaults in case FEN is incomplete */
board[EP_STATUS] = EP_UNKNOWN;
for(i=0; i<nrCastlingRights; i++ ) {
&& board[castlingRank[5]][initialRights[5]] != BlackKing) board[CASTLING][5] = NoRights;
FENrulePlies = 0;
- while(*p==' ') p++;
if(nrCastlingRights) {
int fischer = 0;
if(gameInfo.variant == VariantSChess) for(i=0; i<BOARD_FILES; i++) virgin[i] = 0;
int c = *p++, whiteKingFile=NoRights, blackKingFile=NoRights;
for(i=BOARD_LEFT; i<BOARD_RGHT; i++) {
- if(board[BOARD_HEIGHT-1][i] == BlackKing) blackKingFile = i;
- if(board[0 ][i] == WhiteKing) whiteKingFile = i;
+ if(board[castlingRank[5]][i] == BlackKing) blackKingFile = i;
+ if(board[castlingRank[2]][i] == WhiteKing) whiteKingFile = i;
}
if(gameInfo.variant == VariantTwoKings || gameInfo.variant == VariantKnightmate)
whiteKingFile = blackKingFile = BOARD_WIDTH >> 1; // for these variant scanning fails
&& board[BOARD_HEIGHT-1][blackKingFile] != BlackKing) blackKingFile = NoRights;
switch(c) {
case'K':
- for(i=BOARD_RGHT-1; board[0][i]!=WhiteRook && i>whiteKingFile; i--);
+ for(i=BOARD_RGHT-1; board[castlingRank[2]][i]!=WhiteRook && i>whiteKingFile; i--);
board[CASTLING][0] = i != whiteKingFile ? i : NoRights;
board[CASTLING][2] = whiteKingFile;
if(board[CASTLING][0] != NoRights) virgin[board[CASTLING][0]] |= VIRGIN_W;
if(whiteKingFile != BOARD_WIDTH>>1|| i != BOARD_RGHT-1) fischer = 1;
break;
case'Q':
- for(i=BOARD_LEFT; i<BOARD_RGHT && board[0][i]!=WhiteRook && i<whiteKingFile; i++);
+ for(i=BOARD_LEFT; i<BOARD_RGHT && board[castlingRank[2]][i]!=WhiteRook && i<whiteKingFile; i++);
board[CASTLING][1] = i != whiteKingFile ? i : NoRights;
board[CASTLING][2] = whiteKingFile;
if(board[CASTLING][1] != NoRights) virgin[board[CASTLING][1]] |= VIRGIN_W;
if(whiteKingFile != BOARD_WIDTH>>1|| i != BOARD_LEFT) fischer = 1;
break;
case'k':
- for(i=BOARD_RGHT-1; board[BOARD_HEIGHT-1][i]!=BlackRook && i>blackKingFile; i--);
+ for(i=BOARD_RGHT-1; board[castlingRank[5]][i]!=BlackRook && i>blackKingFile; i--);
board[CASTLING][3] = i != blackKingFile ? i : NoRights;
board[CASTLING][5] = blackKingFile;
if(board[CASTLING][3] != NoRights) virgin[board[CASTLING][3]] |= VIRGIN_B;
if(blackKingFile != BOARD_WIDTH>>1|| i != BOARD_RGHT-1) fischer = 1;
break;
case'q':
- for(i=BOARD_LEFT; i<BOARD_RGHT && board[BOARD_HEIGHT-1][i]!=BlackRook && i<blackKingFile; i++);
+ for(i=BOARD_LEFT; i<BOARD_RGHT && board[castlingRank[5]][i]!=BlackRook && i<blackKingFile; i++);
board[CASTLING][4] = i != blackKingFile ? i : NoRights;
board[CASTLING][5] = blackKingFile;
if(board[CASTLING][4] != NoRights) virgin[board[CASTLING][4]] |= VIRGIN_B;