* 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
*
extern char installDir[MSG_SIZ];
VariantClass startVariant; /* [HGM] nicks: initial variant */
Boolean abortMatch;
+int deadRanks, handSize, handOffsets;
extern int tinyLayout, smallLayout;
ChessProgramStats programStats;
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;
ChessSquare SpartanArray[2][BOARD_FILES] = {
{ WhiteRook, WhiteKnight, WhiteBishop, WhiteQueen,
WhiteKing, WhiteBishop, WhiteKnight, WhiteRook },
- { BlackAlfil, BlackMarshall, BlackKing, BlackDragon,
- BlackDragon, BlackKing, BlackAngel, BlackAlfil }
+ { BlackAlfil, BlackDragon, BlackKing, BlackTower,
+ BlackTower, BlackKing, BlackAngel, BlackAlfil }
};
ChessSquare fairyArray[2][BOARD_FILES] = { /* [HGM] Queen side differs from King side */
BlackFerz, BlackWazir, BlackBishop, BlackAlfil, BlackKnight, BlackRook }
};
ChessSquare ChuArray[6][BOARD_FILES] = {
- { WhiteLance, WhiteUnicorn, WhiteMan, WhiteFerz, WhiteWazir, WhiteKing,
- WhiteAlfil, WhiteWazir, WhiteFerz, WhiteMan, WhiteUnicorn, WhiteLance },
- { BlackLance, BlackUnicorn, BlackMan, BlackFerz, BlackWazir, BlackAlfil,
- BlackKing, BlackWazir, BlackFerz, BlackMan, BlackUnicorn, BlackLance },
- { WhiteCannon, EmptySquare, WhiteBishop, EmptySquare, WhiteNightrider, WhiteMarshall,
- WhiteAngel, WhiteNightrider, EmptySquare, WhiteBishop, EmptySquare, WhiteCannon },
- { BlackCannon, EmptySquare, BlackBishop, EmptySquare, BlackNightrider, BlackAngel,
- BlackMarshall, BlackNightrider, EmptySquare, BlackBishop, EmptySquare, BlackCannon },
- { WhiteFalcon, WhiteSilver, WhiteRook, WhiteCardinal, WhiteDragon, WhiteLion,
- WhiteQueen, WhiteDragon, WhiteCardinal, WhiteRook, WhiteSilver, WhiteFalcon },
- { BlackFalcon, BlackSilver, BlackRook, BlackCardinal, BlackDragon, BlackQueen,
- BlackLion, BlackDragon, BlackCardinal, BlackRook, BlackSilver, BlackFalcon }
+ { WhiteLance, WhiteCat, WhiteCopper, WhiteFerz, WhiteWazir, WhiteKing,
+ WhiteAlfil, WhiteWazir, WhiteFerz, WhiteCopper, WhiteCat, WhiteLance },
+ { BlackLance, BlackCat, BlackCopper, BlackFerz, BlackWazir, BlackAlfil,
+ BlackKing, BlackWazir, BlackFerz, BlackCopper, BlackCat, BlackLance },
+ { WhiteAxe, EmptySquare, WhiteBishop, EmptySquare, WhiteClaw, WhiteMarshall,
+ WhiteAngel, WhiteClaw, EmptySquare, WhiteBishop, EmptySquare, WhiteAxe },
+ { BlackAxe, EmptySquare, BlackBishop, EmptySquare, BlackClaw, BlackAngel,
+ BlackMarshall, BlackClaw, EmptySquare, BlackBishop, EmptySquare, BlackAxe },
+ { WhiteDagger, WhiteSword, WhiteRook, WhiteCardinal, WhiteDragon, WhiteLion,
+ WhiteQueen, WhiteDragon, WhiteCardinal, WhiteRook, WhiteSword, WhiteDagger },
+ { BlackDagger, BlackSword, BlackRook, BlackCardinal, BlackDragon, BlackQueen,
+ BlackLion, BlackDragon, BlackCardinal, BlackRook, BlackSword, BlackDagger }
};
#else // !(BOARD_FILES>=12)
#define CourierArray CapablancaArray
ASSIGN(*list, tidy+1);
}
-char *insert, *wbOptions; // point in ChessProgramNames were we should insert new engine
+char *insert, *wbOptions, *currentEngine[2]; // point in ChessProgramNames were we should insert new engine
void
Load (ChessProgramState *cps, int i)
{
char *p, *q, buf[MSG_SIZ], command[MSG_SIZ], buf2[MSG_SIZ], buf3[MSG_SIZ], jar;
if(engineLine && engineLine[0]) { // an engine was selected from the combo box
+ ASSIGN(currentEngine[i], engineLine);
snprintf(buf, MSG_SIZ, "-fcp %s", engineLine);
SwapEngines(i); // kludge to parse -f* / -first* like it is -s* / -second*
ParseArgsFromString(resetOptions); appData.pvSAN[0] = FALSE;
SwapEngines(i);
ReplaceEngine(cps, i);
FloatToFront(&appData.recentEngineList, engineLine);
+ if(gameMode == BeginningOfGame) Reset(TRUE, TRUE);
return;
}
p = engineName;
q = firstChessProgramNames;
if(nickName[0]) snprintf(buf, MSG_SIZ, "\"%s\" -fcp ", nickName); else buf[0] = NULLCHAR;
quote = strchr(p, '"') ? '\'' : '"'; // use single quotes around engine command if it contains double quotes
- snprintf(buf+strlen(buf), MSG_SIZ-strlen(buf), "%c%s%c -fd \"%s\"%s%s%s%s%s%s%s%s\n",
+ snprintf(buf+strlen(buf), MSG_SIZ-strlen(buf), "%c%s%c -fd \"%s\"%s%s%s%s%s%s%s%s",
quote, p, quote, appData.directory[i],
useNick ? " -fn \"" : "",
useNick ? nickName : "",
isUCI ? (isUCI == TRUE ? " -fUCI" : gameInfo.variant == VariantShogi ? " -fUSI" : " -fUCCI") : "",
storeVariant ? " -variant " : "",
storeVariant ? VariantName(gameInfo.variant) : "");
- if(wbOptions && wbOptions[0]) snprintf(buf+strlen(buf)-1, MSG_SIZ-strlen(buf), " %s\n", wbOptions);
- firstChessProgramNames = malloc(len = strlen(q) + strlen(buf) + 1);
+ if(wbOptions && wbOptions[0]) snprintf(buf+strlen(buf), MSG_SIZ-strlen(buf), " %s", wbOptions);
+ firstChessProgramNames = malloc(len = strlen(q) + strlen(buf) + 2);
if(insert != q) insert[-1] = NULLCHAR;
- snprintf(firstChessProgramNames, len, "%s\n%s%s", q, buf, insert);
+ snprintf(firstChessProgramNames, len, "%s\n%s\n%s", q, buf, insert);
if(q) free(q);
FloatToFront(&appData.recentEngineList, buf);
+ ASSIGN(currentEngine[i], buf);
}
ReplaceEngine(cps, i);
}
}
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();
}
-char *comboLine = NULL; // [HGM] recent: WinBoard's first-engine combobox line
-
void
InitBackEnd3 P((void))
{
char buf[MSG_SIZ];
int err, len;
+ ParseFeatures(appData.features[0], &first);
if(!appData.icsActive && !appData.noChessProgram && !appData.matchMode && // mode involves only first engine
!strcmp(appData.variant, "normal") && // no explicit variant request
appData.NrRanks == -1 && appData.NrFiles == -1 && appData.holdingsSize == -1 && // no size overrides requested
free(programVersion);
programVersion = (char*) malloc(8 + strlen(PACKAGE_STRING) + strlen(first.tidy));
sprintf(programVersion, "%s + %s", PACKAGE_STRING, first.tidy);
- FloatToFront(&appData.recentEngineList, comboLine ? comboLine : appData.firstChessProgram);
+ FloatToFront(&appData.recentEngineList, currentEngine[0] ? currentEngine[0] : appData.firstChessProgram);
}
if (appData.icsActive) {
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( (int)lowestPiece >= BlackPawn ) {
holdingsColumn = 0;
countsColumn = 1;
- holdingsStartRow = BOARD_HEIGHT-1;
+ holdingsStartRow = handSize-1;
direction = -1;
} else {
holdingsColumn = BOARD_WIDTH-1;
direction = 1;
}
- for(i=0; i<BOARD_HEIGHT; i++) { /* clear holdings */
+ for(i=0; i<BOARD_RANKS-1; i++) { /* clear holdings */
board[i][holdingsColumn] = EmptySquare;
board[i][countsColumn] = (ChessSquare) 0;
}
#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);
+ }
}
}
parse, currentMove);
if (sscanf(parse, " game %d", &gamenum) == 1) {
if(gamenum == ics_gamenum) { // [HGM] bughouse: old code if part of foreground game
+ new_piece[0] = NULLCHAR;
+ sscanf(parse, "game %d white [%s black [%s <- %s",
+ &gamenum, white_holding, black_holding,
+ new_piece);
+ white_holding[strlen(white_holding)-1] = NULLCHAR;
+ black_holding[strlen(black_holding)-1] = NULLCHAR;
if (gameInfo.variant == VariantNormal) {
/* [HGM] We seem to switch variant during a game!
* Presumably no holdings were displayed, so we have
switch(gameInfo.boardWidth) { // base guess on board width
case 9: newVariant = VariantShogi; break;
case 10: newVariant = VariantGreat; break;
- default: newVariant = VariantCrazyhouse; break;
+ default: newVariant = VariantCrazyhouse;
+ if(strchr(white_holding, 'E') || strchr(black_holding, 'E') ||
+ strchr(white_holding, 'H') || strchr(black_holding, 'H') )
+ newVariant = VariantSChess;
}
VariantSwitch(boards[currentMove], newVariant); /* temp guess */
/* Get a move list just to see the header, which
SendToICS(str);
}
}
- new_piece[0] = NULLCHAR;
- sscanf(parse, "game %d white [%s black [%s <- %s",
- &gamenum, white_holding, black_holding,
- new_piece);
- white_holding[strlen(white_holding)-1] = NULLCHAR;
- black_holding[strlen(black_holding)-1] = NULLCHAR;
/* [HGM] copy holdings to board holdings area */
CopyHoldings(boards[forwardMostMove], white_holding, WhitePawn);
CopyHoldings(boards[forwardMostMove], black_holding, BlackPawn);
} else if (count == 0) {
RemoveInputSource(isr);
- DisplayFatalError(_("Connection closed by ICS"), 0, 0);
+ DisplayFatalError(_("Connection closed by ICS"), 0, 6666);
} else {
DisplayFatalError(_("Error reading from ICS"), error, 1);
}
boards[moveNum][EP_STATUS] = EP_NONE;
if(str[0] == 'P') boards[moveNum][EP_STATUS] = EP_PAWN_MOVE;
if(strchr(move_str, 'x')) boards[moveNum][EP_STATUS] = EP_CAPTURE;
- if(double_push != -1) boards[moveNum][EP_STATUS] = double_push + BOARD_LEFT;
+ if(double_push != -1) {
+ int dir = WhiteOnMove(moveNum) ? 1 : -1, last = BOARD_HEIGHT-1;
+ boards[moveNum][EP_FILE] = // also set new e.p. variables
+ boards[moveNum][EP_STATUS] = double_push + BOARD_LEFT;
+ boards[moveNum][EP_RANK] = (last + 3*dir)/2;
+ boards[moveNum][LAST_TO] = 128*(last + dir) + boards[moveNum][EP_FILE];
+ } else boards[moveNum][EP_FILE] = boards[moveNum][EP_RANK] = 100;
if (ics_getting_history == H_GOT_REQ_HEADER ||
&& gameInfo.variant != VariantGrand&& gameInfo.variant != VariantSChess) // inherit info that ICS does not give from previous board
for(k=0; k<ranks; k++) for(j=BOARD_LEFT; j<BOARD_RGHT; j++) {
ChessSquare old, new = boards[moveNum][k][j];
- if(fromY == DROP_RANK && k==toY && j==toX) continue; // dropped pieces always stand for themselves
+ if(new == EmptySquare || fromY == DROP_RANK && k==toY && j==toX) continue; // dropped pieces always stand for themselves
old = (k==toY && j==toX) ? boards[moveNum-1][fromY][fromX] : boards[moveNum-1][k][j]; // trace back mover
if(old == new) continue;
if(old == PROMOTED(new)) boards[moveNum][k][j] = old;// prevent promoted pieces to revert to primordial ones
if(old < WhiteCannon || old >= BlackPawn && old < BlackCannon)
boards[moveNum][k][j] = PROMOTED(old); // choose correct type of Gold in promotion
else boards[moveNum][k][j] = old; // preserve type of Gold
- } else if((old == WhitePawn || old == BlackPawn) && new != EmptySquare) // Pawn promotions (but not e.p.capture!)
+ } else if(old == WhitePawn || old == BlackPawn) // Pawn promotions (but not e.p.capture!)
boards[moveNum][k][j] = PROMOTED(new); // use non-primordial representation of chosen piece
}
} else {
} 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]; if(*c == '\n') *c = NULLCHAR; // 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] != '\n') { // kill square followed by 2 characters: 2nd kill square rather than promo suffix
+ *c = m[9]; if(*c == '\n') *c = NULLCHAR;
+ 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', 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');
+ 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%c\n", AAA + killX, ONE + killY, promoChar);
+ 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 != '*' && !strchr(escapes, *s)), s++;
+ while(*s) n += (*s != '/' && *s != '-' && *s != '^' && *s != '*' && !strchr(escapes, *s)) - 2*(*s == '='), s++;
return n;
}
-static int pieceOrder[] = {
- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, // P N B R Q F E A C W M
- 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, // O H I J G D V L S U Lion
- 45, 23, 24, 25, 26, 27, 28, 29, 46, 31, 32, // Sword Zebra Camel Tower Wolf Dragon Duck Axe Leopard Gnu Cub
- 44, 51, 56, 57, 58, 59, 60, 61, 62, 63, 34, // Whale Pegasus Wizard Copper Iron Viking Flag Amazon Wheel Shield Claw
- 33, 55, 53, 42, 37, 48, 39, 40, 41, 22, 30, // +P +N =B =R +L +S +E +Ph +Kn Butterfly Hat
- 38, 43, 35, 36, 49, 47, 52, 50, 54, 64, 65 // +V +M =H =D Princess HSword +GB HCrown Wheer Shierd King
-};
-
int
SetCharTableEsc (unsigned char *table, const char * map, char * escapes)
/* [HGM] moved here from winboard.c because of its general usefulness */
if( map != NULL && (NrPieces=ptclen(map, escapes)) <= (int) EmptySquare
&& NrPieces >= 12 && !(NrPieces&1)) {
- int i, ii, j = 0; /* [HGM] Accept even length from 12 to 88 */
+ int i, ii, offs, j = 0; /* [HGM] Accept even length from 12 to 88 */
for( i=0; i<(int) EmptySquare; i++ ) table[i] = '.';
- for( ii=0; ii<NrPieces/2-1; ii++ ) {
+ for( i=offs=0; i<NrPieces/2-1; i++ ) {
char *p, c=0;
- i = pieceOrder[ii];
+ if(map[j] == '/') offs = WhitePBishop - i, j++;
if(*escapes && (map[j] == '*' || map[j] == '-' || map[j] == '^')) c = map[j++];
- table[i] = map[j++];
- if(p = strchr(escapes, map[j])) j++, table[i] += 64*(p - escapes + 1);
- if(c) partner[i] = table[i], table[i] = c;
+ 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=0; ii<NrPieces/2-1; ii++ ) {
+ for( ii=offs=0; ii<NrPieces/2-1; ii++ ) {
char *p, c=0;
- i = WHITE_TO_BLACK pieceOrder[ii];
+ if(map[j] == '/') offs = WhitePBishop - ii, j++;
+ i = WHITE_TO_BLACK ii;
if(*escapes && (map[j] == '*' || map[j] == '-' || map[j] == '^')) c = map[j++];
- table[i] = map[j++];
- if(p = strchr(escapes, map[j])) j++, table[i] += 64*(p - escapes + 1);
- if(c) partner[i] = table[i], table[i] = c;
+ 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++];
int p;
for(p=0; p<EmptySquare; p++) if(table[p] == partner[i]) break;
if(c == '^') table[i] = '+';
- if(p < EmptySquare) promoPartner[p] = i, promoPartner[i] = p; // marry them
+ if(p < EmptySquare) {
+ if(promoPartner[promoPartner[p]] == p) promoPartner[promoPartner[p]] = promoPartner[p]; // divorce old partners
+ if(promoPartner[promoPartner[i]] == i) promoPartner[promoPartner[i]] = promoPartner[i];
+ promoPartner[p] = i, promoPartner[i] = p; // and marry this couple
+ }
} else if(c == '*') {
table[i] = partner[i];
promoPartner[i] = (i < BlackPawn ? WhiteTokin : BlackTokin); // promotes to Tokin
j = seed%4; seed /= 4;
p = board[0][BOARD_LEFT+j]; board[0][BOARD_LEFT+j] = EmptySquare; k = PieceToNumber(p);
board[k][BOARD_WIDTH-1] = p; board[k][BOARD_WIDTH-2]++;
- board[BOARD_HEIGHT-1-k][0] = WHITE_TO_BLACK p; board[BOARD_HEIGHT-1-k][1]++;
+ board[handSize-1-k][0] = WHITE_TO_BLACK p; board[handSize-1-k][1]++;
j = seed%3 + (seed%3 >= j); seed /= 3;
p = board[0][BOARD_LEFT+j]; board[0][BOARD_LEFT+j] = EmptySquare; k = PieceToNumber(p);
board[k][BOARD_WIDTH-1] = p; board[k][BOARD_WIDTH-2]++;
- board[BOARD_HEIGHT-1-k][0] = WHITE_TO_BLACK p; board[BOARD_HEIGHT-1-k][1]++;
+ board[handSize-1-k][0] = WHITE_TO_BLACK p; board[handSize-1-k][1]++;
j = seed%3; seed /= 3;
p = board[0][BOARD_LEFT+j+5]; board[0][BOARD_LEFT+j+5] = EmptySquare; k = PieceToNumber(p);
board[k][BOARD_WIDTH-1] = p; board[k][BOARD_WIDTH-2]++;
- board[BOARD_HEIGHT-1-k][0] = WHITE_TO_BLACK p; board[BOARD_HEIGHT-1-k][1]++;
+ board[handSize-1-k][0] = WHITE_TO_BLACK p; board[handSize-1-k][1]++;
j = seed%2 + (seed%2 >= j); seed /= 2;
p = board[0][BOARD_LEFT+j+5]; board[0][BOARD_LEFT+j+5] = EmptySquare; k = PieceToNumber(p);
board[k][BOARD_WIDTH-1] = p; board[k][BOARD_WIDTH-2]++;
- board[BOARD_HEIGHT-1-k][0] = WHITE_TO_BLACK p; board[BOARD_HEIGHT-1-k][1]++;
+ board[handSize-1-k][0] = WHITE_TO_BLACK p; board[handSize-1-k][1]++;
j = seed%4; seed /= 4; put(board, exoPieces[3], 0, j, ANY);
j = seed%3; seed /= 3; put(board, exoPieces[2], 0, j, ANY);
j = seed%2; seed /= 2; put(board, exoPieces[1], 0, j, ANY);
gameInfo.boardWidth = 12;
gameInfo.boardHeight = 12;
nrCastlingRights = 0;
- SetCharTableEsc(pieceToChar, "P.BRQSEXOGCATHD.VMLIFN/^P.^B^R.^S^E^X^O^G^C^A^T^H^D.^V^M^L^I^FK"
- "p.brqsexogcathd.vmlifn/^p.^b^r.^s^e^x^o^g^c^a^t^h^d.^v^m^l^i^fk", SUFFIXES);
+// SetCharTableEsc(pieceToChar, "P.BRQSEXOGCATHD.VMLIFN.........^T..^L......^A^H/^F^G^M.^E^X^O^I.^P.^B^R..^D^S^C^VK"
+ // "p.brqsexogcathd.vmlifn.........^t..^l......^a^h/^f^g^m.^e^x^o^i.^p.^b^r..^d^s^c^vk", SUFFIXES);
+ SetCharTableEsc(pieceToChar, "P.BRQSEXOG...HD..^DLI^HNV........^T..^L.C...A^AFT/^F^G^M.^E^X^O^I.^P.^B^R..M^S^C^VK"
+ "p.brqsexog...hd..^dli^hnv........^t..^l.c...a^aft/^f^g^m.^e^x^o^i.^p.^b^r..m^s^c^vk", SUFFIXES);
break;
case VariantCourier:
pieces = CourierArray;
break;
case VariantSpartan:
pieces = SpartanArray;
- SetCharTable(pieceToChar, "PNBRQ................K......lwg.....c...h..k");
+ SetCharTable(pieceToChar, "PNBRQ.....................K......lw......g...h......ck");
break;
case VariantLion:
pieces = lionArray;
shuffleOpenings = 1;
break;
case VariantNoCastle:
- pieces = FIDEArray;
- nrCastlingRights = 0;
/* !!?unconstrained back-rank shuffle */
shuffleOpenings = 1;
+ case VariantSuicide:
+ pieces = FIDEArray;
+ nrCastlingRights = 0;
break;
}
}
if(appData.holdingsSize >= 0) {
i = appData.holdingsSize;
- if(i > gameInfo.boardHeight) i = gameInfo.boardHeight;
+// if(i > gameInfo.boardHeight) i = gameInfo.boardHeight;
gameInfo.holdingsSize = i;
}
if(gameInfo.holdingsSize) gameInfo.holdingsWidth = 2;
if(BOARD_HEIGHT > BOARD_RANKS || BOARD_WIDTH > BOARD_FILES)
DisplayFatalError(_("Recompile to support this BOARD_RANKS or BOARD_FILES!"), 0, 2);
+ if(!handSize) handSize = BOARD_HEIGHT;
pawnRow = gameInfo.boardHeight - 7; /* seems to work in all common variants */
if(pawnRow < 1) pawnRow = 1;
if(gameInfo.variant == VariantMakruk || gameInfo.variant == VariantASEAN ||
if(gameInfo.variant == VariantGreat) { // promotion commoners
initialPosition[PieceToNumber(WhiteMan)][BOARD_WIDTH-1] = WhiteMan;
initialPosition[PieceToNumber(WhiteMan)][BOARD_WIDTH-2] = 9;
- initialPosition[BOARD_HEIGHT-1-PieceToNumber(WhiteMan)][0] = BlackMan;
- initialPosition[BOARD_HEIGHT-1-PieceToNumber(WhiteMan)][1] = 9;
+ initialPosition[handSize-1-PieceToNumber(WhiteMan)][0] = BlackMan;
+ initialPosition[handSize-1-PieceToNumber(WhiteMan)][1] = 9;
}
if( gameInfo.variant == VariantSChess ) {
initialPosition[1][0] = BlackMarshall;
initialPosition[1][1] = initialPosition[2][1] =
initialPosition[6][BOARD_WIDTH-2] = initialPosition[5][BOARD_WIDTH-2] = 1;
}
+ initialPosition[CHECK_COUNT] = (gameInfo.variant == Variant3Check ? 0x303 : 0);
if (appData.debugMode) {
fprintf(debugFP, "shuffleOpenings = %d\n", shuffleOpenings);
}
initialRights[i] = filePosition[CASTLING][i];
startedFromSetupPosition = TRUE;
}
+ if(*appData.men) LoadPieceDesc(appData.men);
CopyBoard(boards[0], initialPosition);
* deprecated "black" command.
*/
if (!WhiteOnMove(moveNum)) // [HGM] but better a deprecated command than an illegal move...
- SendToProgram(boards[0][1][BOARD_LEFT] == WhitePawn ? "a2a3\n" : "black\n", cps);
+ SendToProgram(boards[0][1][BOARD_LEFT] == WhitePawn && !pieceDesc[WhitePawn] ? "a2a3\n" : "black\nforce\n", cps);
if(!cps->extendedEdit) left = BOARD_LEFT, right = BOARD_RGHT; // only board proper
!(fromX >=0 && fromY >= 0 && toX >= 0 && toY >= 0) ) // invalid move
return FALSE;
+ if(legal[toY][toX] == 4) return FALSE;
+
piece = boards[currentMove][fromY][fromX];
if(gameInfo.variant == VariantChu) {
- promotionZoneSize = BOARD_HEIGHT/3;
+ promotionZoneSize = (BOARD_HEIGHT - deadRanks)/3;
+ if(legal[toY][toX] == 6) return FALSE; // no promotion if highlights deny it
highestPromotingPiece = (PieceToChar(piece) == '+' || PieceToChar(CHUPROMOTED(piece)) != '+') ? WhitePawn : WhiteKing;
} else if(gameInfo.variant == VariantShogi) {
- promotionZoneSize = BOARD_HEIGHT/3 +(BOARD_HEIGHT == 8);
+ promotionZoneSize = (BOARD_HEIGHT- deadRanks)/3 +(BOARD_HEIGHT == 8);
highestPromotingPiece = (int)WhiteAlfil;
+ if(PieceToChar(piece) != '+' || PieceToChar(CHUPROMOTED(piece)) == '+') highestPromotingPiece = piece;
} else if(gameInfo.variant == VariantMakruk || gameInfo.variant == VariantGrand || gameInfo.variant == VariantChuChess) {
promotionZoneSize = 3;
}
if(fromY < promotionZoneSize && gameInfo.variant == VariantChuChess) return FALSE;
highestPromotingPiece = WHITE_TO_BLACK highestPromotingPiece;
} else {
- if( toY < BOARD_HEIGHT - promotionZoneSize &&
- fromY < BOARD_HEIGHT - promotionZoneSize) return FALSE;
- if(fromY >= BOARD_HEIGHT - promotionZoneSize && gameInfo.variant == VariantChuChess)
+ if( toY < BOARD_HEIGHT - deadRanks - promotionZoneSize &&
+ fromY < BOARD_HEIGHT - deadRanks - promotionZoneSize) return FALSE;
+ if(fromY >= BOARD_HEIGHT - deadRanks - promotionZoneSize && gameInfo.variant == VariantChuChess)
return FALSE;
}
return FALSE;
}
} else {
- if(toY == BOARD_HEIGHT-1 && piece == WhitePawn ||
- toY == BOARD_HEIGHT-1 && piece == WhiteQueen ||
- toY >= BOARD_HEIGHT-2 && piece == WhiteKnight) {
+ if(toY == BOARD_HEIGHT-deadRanks-1 && piece == WhitePawn ||
+ toY == BOARD_HEIGHT-deadRanks-1 && piece == WhiteQueen ||
+ toY >= BOARD_HEIGHT-deadRanks-2 && piece == WhiteKnight) {
*promoChoice = '+';
return FALSE;
}
*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) {
int
PieceForSquare (int x, int y)
{
- if (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT)
- return -1;
- else
+ if (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) return -1;
+ if(x == BOARD_RGHT+1 && handOffsets & 1) y += handSize - BOARD_HEIGHT;
+ if(x == BOARD_LEFT-2 && !(handOffsets & 2)) y += handSize - BOARD_HEIGHT;
return boards[currentMove][y][x];
}
+ChessSquare
+More (Board board, int col, int start, int end)
+{
+ int k;
+ for(k=start; k<end; k++) if(board[k][col]) return (col == 1 ? WhiteMonarch : BlackMonarch); // arrow image
+ return EmptySquare;
+}
+
+void
+DrawPosition (int repaint, Board board)
+{
+ Board compactedBoard;
+ if(handSize > BOARD_HEIGHT && board) {
+ int k;
+ CopyBoard(compactedBoard, board);
+ if(handOffsets & 1) {
+ for(k=0; k<BOARD_HEIGHT; k++) {
+ compactedBoard[k][BOARD_WIDTH-1] = board[k+handSize-BOARD_HEIGHT][BOARD_WIDTH-1];
+ compactedBoard[k][BOARD_WIDTH-2] = board[k+handSize-BOARD_HEIGHT][BOARD_WIDTH-2];
+ }
+ compactedBoard[0][BOARD_WIDTH-1] = More(board, BOARD_WIDTH-2, 0, handSize-BOARD_HEIGHT+1);
+ } else compactedBoard[BOARD_HEIGHT-1][BOARD_WIDTH-1] = More(board, BOARD_WIDTH-2, BOARD_HEIGHT-1, handSize);
+ if(!(handOffsets & 2)) {
+ for(k=0; k<BOARD_HEIGHT; k++) {
+ compactedBoard[k][0] = board[k+handSize-BOARD_HEIGHT][0];
+ compactedBoard[k][1] = board[k+handSize-BOARD_HEIGHT][1];
+ }
+ compactedBoard[0][0] = More(board, 1, 0, handSize-BOARD_HEIGHT+1);
+ } else compactedBoard[BOARD_HEIGHT-1][0] = More(board, 1, BOARD_HEIGHT-1, handSize);
+ DrawPositionX(TRUE, compactedBoard);
+ } else DrawPositionX(repaint, board);
+}
+
int
OKToStartUserMove (int x, int y)
{
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;
return;
} else if (toX >= 0 && toY >= 0) {
if(!appData.pieceMenu && toX == fromX && toY == fromY && boards[0][rf][ff] != EmptySquare) {
- ChessSquare q, p = boards[0][rf][ff];
- if(p >= BlackPawn) p = BLACK_TO_WHITE p;
- if(CHUPROMOTED(p) < BlackPawn) p = q = CHUPROMOTED(boards[0][rf][ff]);
- else p = CHUDEMOTED (q = boards[0][rf][ff]);
- if(PieceToChar(q) == '+') gatingPiece = p;
- }
+ ChessSquare p = boards[0][rf][ff];
+ if(PieceToChar(p) == '+') gatingPiece = CHUDEMOTED(p); else
+ if(PieceToChar(CHUPROMOTED(p)) =='+') gatingPiece = CHUPROMOTED(p); else
+ if(p == WhiteKing || p == BlackKing || p == WhiteRook || p == BlackRook || p == WhitePawn || p == BlackPawn) {
+ int n = rightsBoard[toY][toX] ^= 1; // toggle virginity of K or R
+ DisplayMessage("", n ? _("rights granted") : _("rights revoked"));
+ gatingPiece = p;
+ }
+ } else rightsBoard[toY][toX] = 0; // revoke rights on moving
boards[0][toY][toX] = boards[0][fromY][fromX];
if(fromX == BOARD_LEFT-2) { // handle 'moves' out of holdings
if(boards[0][fromY][0] != EmptySquare) {
}
} else
boards[0][fromY][fromX] = gatingPiece;
+ ClearHighlights();
DrawPosition(FALSE, boards[currentMove]);
return;
}
if(appData.debugMode) fprintf(debugFP, "Drop move %d, curr=%d, x=%d,y=%d, p=%d\n",
moveType, currentMove, fromX, fromY, boards[currentMove][fromY][fromX]);
// 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
+ if(fromX == 0) fromY = handSize-1 - fromY; // black holdings upside-down
fromX = fromX ? WhitePawn : BlackPawn; // first piece type in selected holdings
while(PieceToChar(fromX) == '.' || PieceToChar(fromX) == '+' || PieceToNumber(fromX) != fromY && fromX != (int) EmptySquare) fromX++;
fromY = DROP_RANK;
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;
if(WhiteOnMove(currentMove)) {
if(!boards[currentMove][k][BOARD_WIDTH-2]) return 0;
} else {
- if(!boards[currentMove][BOARD_HEIGHT-1-k][1]) return 0;
+ if(!boards[currentMove][handSize-1-k][1]) return 0;
}
}
{
int r, f;
if(!appData.markers || !appData.highlightDragging) return;
- for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) legal[r][f] = 0;
- r=BOARD_HEIGHT-1; f=BOARD_LEFT;
+ for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) legal[r][f] = marker[r][f] = 0;
+ r=BOARD_HEIGHT-1-deadRanks; f=BOARD_LEFT;
while(*fen) {
int s = 0;
- marker[r][f] = 0;
if(*fen == 'M') legal[r][f] = 2; else // request promotion choice
- if(*fen >= 'A' && *fen <= 'Z') legal[r][f] = 3; else
+ if(*fen == 'B') legal[r][f] = 4; else // request auto-promotion to victim
+ if(*fen >= 'A' && *fen <= 'Z') legal[r][f] = 6; else
if(*fen >= 'a' && *fen <= 'z') *fen += 'A' - 'a';
if(*fen == '/' && f > BOARD_LEFT) f = BOARD_LEFT, r--; else
if(*fen == 'T') marker[r][f++] = 0; else
{
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 ||
if(PosFlags(0) & F_MANDATORY_CAPTURE) {
for(x=0; x<BOARD_WIDTH; x++) for(y=0; y<BOARD_HEIGHT; y++) if(marker[y][x]>1) capt++;
if(capt)
- for(x=0; x<BOARD_WIDTH; x++) for(y=0; y<BOARD_HEIGHT; y++) if(marker[y][x] == 1) marker[y][x] = 0;
+ for(x=0; x<BOARD_WIDTH; x++) for(y=0; y<BOARD_HEIGHT; y++) if(marker[y][x] == 1) marker[y][x] = legal[y][x] = 0;
}
}
DrawPosition(FALSE, NULL);
// 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 == WhitePawn && y >= BOARD_HEIGHT-1-deadRanks-zone ||
piece == BlackLance && y <= zone ||
- piece == WhiteLance && y >= BOARD_HEIGHT-1-zone );
+ piece == WhiteLance && y >= BOARD_HEIGHT-1-deadRanks-zone );
}
void
}
Boolean right; // instructs front-end to use button-1 events as if they were button 3
+Boolean deferChoice;
+int createX = -1, createY = -1; // square where we last created a piece in EditPosition mode
void
LeftClick (ClickType clickType, int xPix, int yPix)
{
int x, y;
- Boolean saveAnimate;
- static int second = 0, promotionChoice = 0, clearFlag = 0, sweepSelecting = 0;
+ static Boolean saveAnimate;
+ 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;
+
+ if(!deferChoice) { // when called for a retry, skip everything to the point where we left off
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) {
+ // map clicks in offsetted holdings back to true coords (or switch the offset)
+ if(x == BOARD_RGHT+1) {
+ if(handOffsets & 1) {
+ if(y == 0) { handOffsets &= ~1; DrawPosition(TRUE, boards[currentMove]); return; }
+ y += handSize - BOARD_HEIGHT;
+ } else if(y == BOARD_HEIGHT-1) { handOffsets |= 1; DrawPosition(TRUE, boards[currentMove]); return; }
+ }
+ if(x == BOARD_LEFT-2) {
+ if(!(handOffsets & 2)) {
+ if(y == 0) { handOffsets |= 2; DrawPosition(TRUE, boards[currentMove]); return; }
+ y += handSize - BOARD_HEIGHT;
+ } else if(y == BOARD_HEIGHT-1) { handOffsets &= ~2; DrawPosition(TRUE, boards[currentMove]); return; }
+ }
+
+ if(appData.monoMouse && gameMode == EditPosition && fromX < 0 && clickType == Press &&
+ (boards[currentMove][y][x] == EmptySquare || x == createX && y == createY) ) {
static int dummy;
RightClick(clickType, xPix, yPix, &dummy, &dummy);
right = TRUE;
return;
}
+ createX = createY = -1;
+
if(SeekGraphClick(clickType, xPix, yPix, 0)) return;
prevClickTime = lastClickTime; GetTimeMark(&lastClickTime);
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; *promoRestrict = NULLCHAR;
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; *promoRestrict = NULLCHAR;
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)
if(x == BOARD_LEFT-2 && piece >= BlackPawn) {
n = PieceToNumber(piece - (int)BlackPawn);
if(n >= gameInfo.holdingsSize) { n = 0; piece = BlackPawn; }
- boards[currentMove][BOARD_HEIGHT-1 - n][0] = piece;
- boards[currentMove][BOARD_HEIGHT-1 - n][1]++;
+ boards[currentMove][handSize-1 - n][0] = piece;
+ boards[currentMove][handSize-1 - n][1]++;
} else
if(x == BOARD_RGHT+1 && piece < BlackPawn) {
n = PieceToNumber(piece);
}
// off-board moves should not be highlighted
- if(x < 0 || y < 0) ClearHighlights();
- else ReportClick("put", x, y);
+ if(x < 0 || y < 0) {
+ ClearHighlights();
+ DrawPosition(FALSE, NULL);
+ } else ReportClick("put", x, y);
if(gatingPiece != EmptySquare && gameInfo.variant == VariantSChess) promoChoice = ToLower(PieceToChar(gatingPiece));
-
- if(legal[toY][toX] == 2) promoChoice = ToLower(PieceToChar(defaultPromoChoice)); // highlight-induced promotion
+ }
+
+ if(legal[toY][toX] == 2) { // highlight-induced promotion
+ if(piece == defaultPromoChoice) promoChoice = NULLCHAR; // deferral
+ else promoChoice = ToLower(PieceToChar(defaultPromoChoice));
+ } else if(legal[toY][toX] == 4) { // blue target square: engine must supply promotion choice
+ if(!*promoRestrict) { // but has not done that yet
+ deferChoice = TRUE; // set up retry for when it does
+ return; // and wait for that
+ }
+ promoChoice = ToLower(*promoRestrict); // force engine's choice
+ deferChoice = FALSE;
+ }
if (legal[toY][toX] == 2 && !appData.sweepSelect || HasPromotionChoice(fromX, fromY, toX, toY, &promoChoice, appData.sweepSelect)) {
SetHighlights(fromX, fromY, toX, toY);
DisplayMessage("Click in holdings to choose piece", "");
return;
}
+ DrawPosition(FALSE, NULL); // shows piece on from-square during promo popup
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);
+ else DrawPosition(FALSE, NULL);
fromX = fromY = -1;
+ flashing = 0;
}
appData.animate = saveAnimate;
if (appData.animate || appData.animateDragging) {
/* Undo animation damage if needed */
- DrawPosition(FALSE, NULL);
+// DrawPosition(FALSE, NULL);
}
}
if (xSqr == BOARD_LEFT-1 || xSqr == BOARD_RGHT) return -1;
if (xSqr < 0 || ySqr < 0) return -1;
if(appData.pieceMenu) { whichMenu = 0; break; } // edit-position menu
+ if(flipView) xSqr = BOARD_WIDTH - 1 - xSqr; else ySqr = BOARD_HEIGHT - 1 - ySqr;
+ if(xSqr == createX && ySqr == createY && xSqr != BOARD_LEFT-2 && xSqr != BOARD_RGHT+1) {
+ ChessSquare p = boards[currentMove][ySqr][xSqr];
+ do { if(++p == EmptySquare) p = WhitePawn; } while(PieceToChar(p) == '.');
+ boards[currentMove][ySqr][xSqr] = p; DrawPosition(FALSE, boards[currentMove]);
+ return -2;
+ }
pieceSweep = shiftKey ? BlackPawn : WhitePawn; // [HGM] sweep: prepare selecting piece by mouse sweep
- toX = xSqr; toY = ySqr; lastX = x, lastY = y;
- if(flipView) toX = BOARD_WIDTH - 1 - toX; else toY = BOARD_HEIGHT - 1 - toY;
+ createX = toX = xSqr; createY = toY = ySqr; lastX = x, lastY = y;
NextPiece(0);
return 2; // grab
case IcsObserving:
}
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 );
}
return 1;
}
} else moveCount = 6;
+
+ if(gameInfo.variant == VariantMakruk && // Makruk counting rules
+ (nrW == 1 || nrB == 1 || nr[WhitePawn] + nr[BlackPawn] == 0)) { // which only kick in when pawnless or bare King
+ int maxcnt, his, mine, c, wom = WhiteOnMove(forwardMostMove);
+ count = forwardMostMove;
+ while(count >= backwardMostMove) {
+ int np = nr[WhitePawn] + nr[BlackPawn];
+ if(wom) mine = nrW, his = nrB, c = BlackPawn;
+ else mine = nrB, his = nrW, c = WhitePawn;
+ if(mine > 1 && np) { count++; break; }
+ if(mine > 1) maxcnt = 64; else
+ maxcnt = (nr[WhiteRook+c] > 1 ? 8 : nr[WhiteRook+c] ? 16 : nr[WhiteMan+c] > 1 ? 22 :
+ nr[WhiteKnight+c] > 1 ? 32 : nr[WhiteMan+c] ? 44 : 64) - his - 1;
+ while(boards[count][EP_STATUS] != EP_CAPTURE && count > backwardMostMove) count--; // seek previous character
+ if(count == backwardMostMove) break;
+ if(forwardMostMove - count >= 2*maxcnt + 1 - (mine == 1)) break;
+ Count(boards[--count], nr, &nrW, &nrB, &staleW, &staleB, &bishopColor);
+ }
+ if(forwardMostMove - count >= 2*maxcnt + 1 - (mine == 1)) {
+ boards[forwardMostMove][EP_STATUS] = EP_RULE_DRAW;
+ if(canAdjudicate && appData.ruleMoves >= 0) {
+ GameEnds( GameIsDrawn, "Xboard adjudication: counting rule", GE_XBOARD );
+ return 1;
+ }
+ }
+ }
}
// Repetition draws and 50-move rule can be applied independently of legality testing
i++;
}
}
- if( count >= 100)
+ if( count >= 100 && gameInfo.variant != VariantMakruk) // do not accept 50-move claims in Makruk
boards[forwardMostMove][EP_STATUS] = EP_RULE_DRAW;
/* this is used to judge if draw claims are legal */
if(canAdjudicate && appData.ruleMoves > 0 && count >= 2*appData.ruleMoves) {
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);
while(message[s] && message[s++] != ' ');
if(BOARD_HEIGHT != h || BOARD_WIDTH != w + 4*(hand != 0) || gameInfo.holdingsSize != hand ||
dummy == 4 && gameInfo.variant != StringToVariant(varName) ) { // engine wants to change board format or variant
+// if(hand <= h) deadRanks = 0; else deadRanks = hand - h, h = hand; // adapt board to over-sized holdings
+ if(hand > h) handSize = hand; else handSize = h;
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(sscanf(message, "piece %s %s", buf2, buf1) == 2) {
ChessSquare piece = WhitePawn;
- char *p=message+6, *q, *s = SUFFIXES, ID = *p;
- if(*p == '+') piece = CHUPROMOTED(WhitePawn), ID = *++p;
+ char *p=message+6, *q, *s = SUFFIXES, ID = *p, promoted = 0;
+ if(*p == '+') promoted++, ID = *++p;
if(q = strchr(s, p[1])) ID += 64*(q - s + 1), p++;
- piece += CharToPiece(ID & 255) - WhitePawn;
+ piece = CharToPiece(ID & 255); if(promoted) piece = CHUPROMOTED(piece);
if(cps != &first || appData.testLegality && *engineVariant == NULLCHAR
/* always accept definition of */ && piece != WhiteFalcon && piece != BlackFalcon
/* wild-card pieces. */ && piece != WhiteCobra && piece != BlackCobra
}
return;
}
- if(sscanf(message, "choice %s", promoRestrict) == 1 && promoSweep != EmptySquare) {
- promoSweep = CharToPiece(currentMove&1 ? ToLower(*promoRestrict) : ToUpper(*promoRestrict));
- Sweep(0);
+ if(!appData.testLegality && sscanf(message, "choice %s", promoRestrict) == 1) {
+ if(deferChoice) {
+ LeftClick(Press, 0, 0); // finish the click that was interrupted
+ } else if(promoSweep != EmptySquare) {
+ promoSweep = CharToPiece(currentMove&1 ? ToLower(*promoRestrict) : ToUpper(*promoRestrict));
+ if(strlen(promoRestrict) > 1) Sweep(0);
+ }
return;
}
/* [HGM] Allow engine to set up a position. Don't ask me why one would
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)
ParseGameHistory (char *game)
{
ChessMove moveType;
- int fromX, fromY, toX, toY, boardIndex;
+ int fromX, fromY, toX, toY, boardIndex, mask;
char promoChar;
char *p, *q;
char buf[MSG_SIZ];
strcat(moveList[boardIndex], "\n");
boardIndex++;
ApplyMove(fromX, fromY, toX, toY, promoChar, boards[boardIndex]);
+ mask = (WhiteOnMove(boardIndex) ? 0xFF : 0xFF00);
switch (MateTest(boards[boardIndex], PosFlags(boardIndex)) ) {
case MT_NONE:
case MT_STALEMATE:
default:
break;
case MT_CHECK:
- if(!IS_SHOGI(gameInfo.variant))
- strcat(parseList[boardIndex - 1], "+");
- break;
+ if(boards[boardIndex][CHECK_COUNT]) boards[boardIndex][CHECK_COUNT] -= mask & 0x101;
+ if(!boards[boardIndex][CHECK_COUNT] || boards[boardIndex][CHECK_COUNT] & mask) {
+ if(!IS_SHOGI(gameInfo.variant)) strcat(parseList[boardIndex - 1], "+");
+ break;
+ }
case MT_CHECKMATE:
case MT_STAINMATE:
strcat(parseList[boardIndex - 1], "#");
void
ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board)
{
- ChessSquare captured = board[toY][toX], piece, pawn, king, killed, killed2; int p, rookX, oldEP, epRank, berolina = 0;
+ ChessSquare captured = board[toY][toX], piece, pawn, king, killed, killed2; int p, rookX, oldEP, epRank, epFile, lastFile, lastRank, 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_FILE]; epRank = board[EP_RANK];
+ oldEP = (signed char)board[EP_STATUS]; epRank = board[EP_RANK]; epFile = board[EP_FILE]; lastFile = board[LAST_TO] & 255,lastRank = board[LAST_TO] >> 8;
board[EP_STATUS] = EP_NONE;
- board[EP_FILE] = board[EP_RANK] = 100;
+ board[EP_FILE] = board[EP_RANK] = 100, board[LAST_TO] = 0x4040;
if (fromY == DROP_RANK) {
/* must be first */
}
pawn = board[fromY][fromX];
+ if(pieceDesc[pawn] && strchr(pieceDesc[pawn], 'e')) { // piece with user-defined e.p. capture
+ if(captured == EmptySquare && toX == epFile && (toY == (epRank & 127) || toY + (pawn < BlackPawn ? -1 : 1) == epRank - 128)) {
+ captured = board[lastRank][lastFile]; // remove victim
+ board[lastRank][lastFile] = EmptySquare;
+ pawn = EmptySquare; // kludge to suppress old e.p. code
+ }
+ }
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
if(toX<BOARD_RGHT-1 && board[toY][toX+1] == BlackPawn &&
gameInfo.variant != VariantBerolina || toX > fromX)
board[EP_STATUS] = toX;
+ board[LAST_TO] = toX + 256*toY;
}
} else
if( pawn == BlackPawn ) {
if(toX<BOARD_RGHT-1 && board[toY][toX+1] == WhitePawn &&
gameInfo.variant != VariantBerolina || toX > fromX)
board[EP_STATUS] = toX;
+ board[LAST_TO] = toX + 256*toY;
}
}
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;
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 || abs(toX - fromX) > 4)
+ && (epFile == toX || oldEP == EP_UNKNOWN || appData.testLegality || abs(toX - fromX) > 4)
&& (toX != fromX)
&& gameInfo.variant != VariantXiangqi
&& gameInfo.variant != VariantBerolina
&& (pawn == WhitePawn)
&& (board[toY][toX] == EmptySquare)) {
+ if(lastFile == 100) lastFile = (board[fromY][toX] == BlackPawn ? toX : fromX), lastRank = fromY; // assume FIDE e.p. if victim present
board[fromY][fromX] = 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;
+ captured = board[lastRank][lastFile], board[lastRank][lastFile] = 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++);
+ 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;
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 || abs(toX - fromX) > 4)
+ && (epFile == toX && epRank == toY || oldEP == EP_UNKNOWN || appData.testLegality || abs(toX - fromX) > 4)
&& (toX != fromX)
&& gameInfo.variant != VariantXiangqi
&& gameInfo.variant != VariantBerolina
&& (pawn == BlackPawn)
&& (board[toY][toX] == EmptySquare)) {
+ if(lastFile == 100) lastFile = (board[fromY][toX] == WhitePawn ? toX : fromX), lastRank = fromY;
board[fromY][fromX] = 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;
+ captured = board[lastRank][lastFile], board[lastRank][lastFile] = EmptySquare;
} else if ((fromY == 3)
&& (toX == fromX)
&& gameInfo.variant == VariantBerolina
p -= (int)BlackPawn;
p = PieceToNumber((ChessSquare)p);
if(p >= gameInfo.holdingsSize) p = 0;
- if(--board[BOARD_HEIGHT-1-p][1] <= 0)
- board[BOARD_HEIGHT-1-p][0] = EmptySquare;
- if((int)board[BOARD_HEIGHT-1-p][1] < 0)
- board[BOARD_HEIGHT-1-p][1] = 0;
+ if(--board[handSize-1-p][1] <= 0)
+ board[handSize-1-p][0] = EmptySquare;
+ if((int)board[handSize-1-p][1] < 0)
+ board[handSize-1-p][1] = 0;
}
}
if (captured != EmptySquare && gameInfo.holdingsSize > 0
}
p = PieceToNumber((ChessSquare)p);
if(p >= gameInfo.holdingsSize) { p = 0; captured = WhitePawn; }
- board[BOARD_HEIGHT-1-p][1]++;
- board[BOARD_HEIGHT-1-p][0] = WHITE_TO_BLACK captured;
+ board[handSize-1-p][1]++;
+ board[handSize-1-p][0] = WHITE_TO_BLACK captured;
}
}
} else if (gameInfo.variant == VariantAtomic) {
if(!--board[k][BOARD_WIDTH-2])
board[k][BOARD_WIDTH-1] = EmptySquare;
} else {
- if(!--board[BOARD_HEIGHT-1-k][1])
- board[BOARD_HEIGHT-1-k][0] = EmptySquare;
+ if(!--board[handSize-1-k][1])
+ board[handSize-1-k][0] = EmptySquare;
}
}
}
void
MakeMove (int fromX, int fromY, int toX, int toY, int promoChar)
{
- int x = toX, y = toY;
+ int x = toX, y = toY, mask;
char *s = parseList[forwardMostMove];
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;
}
CoordsToComputerAlgebraic(fromY, fromX, toY, toX, promoChar,
moveList[forwardMostMove - 1]);
+ mask = (WhiteOnMove(forwardMostMove) ? 0xFF : 0xFF00);
switch (MateTest(boards[forwardMostMove], PosFlags(forwardMostMove)) ) {
case MT_NONE:
case MT_STALEMATE:
default:
break;
case MT_CHECK:
- if(!IS_SHOGI(gameInfo.variant))
- strcat(parseList[forwardMostMove - 1], "+");
- break;
+ if(boards[forwardMostMove][CHECK_COUNT]) boards[forwardMostMove][CHECK_COUNT] -= mask & 0x101;
+ if(!boards[forwardMostMove][CHECK_COUNT] || boards[forwardMostMove][CHECK_COUNT] & mask) {
+ if(!IS_SHOGI(gameInfo.variant)) strcat(parseList[forwardMostMove - 1], "+");
+ break;
+ }
case MT_CHECKMATE:
case MT_STAINMATE:
strcat(parseList[forwardMostMove - 1], "#");
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, ',');
}
-void
-ResendOptions (ChessProgramState *cps)
+char *
+ResendOptions (ChessProgramState *cps, int toEngine)
{ // send the stored value of the options
int i;
- char buf[MSG_SIZ];
+ static char buf2[MSG_SIZ*10];
+ char buf[MSG_SIZ], *p = buf2;
Option *opt = cps->option;
+ *p = NULLCHAR;
for(i=0; i<cps->nrOptions; i++, opt++) {
+ *buf = NULLCHAR;
switch(opt->type) {
case Spin:
case Slider:
case CheckBox:
- snprintf(buf, MSG_SIZ, "option %s=%d\n", opt->name, opt->value);
+ if(opt->value != *(int*) (opt->name + MSG_SIZ - 104))
+ snprintf(buf, MSG_SIZ, "%s=%d", opt->name, opt->value);
break;
case ComboBox:
- snprintf(buf, MSG_SIZ, "option %s=%s\n", opt->name, opt->choice[opt->value]);
+ if(opt->value != *(int*) (opt->name + MSG_SIZ - 104))
+ snprintf(buf, MSG_SIZ, "%s=%s", opt->name, opt->choice[opt->value]);
break;
default:
- snprintf(buf, MSG_SIZ, "option %s=%s\n", opt->name, opt->textValue);
+ if(strcmp(opt->textValue, opt->name + MSG_SIZ - 100))
+ snprintf(buf, MSG_SIZ, "%s=%s", opt->name, opt->textValue);
break;
case Button:
case SaveButton:
continue;
}
- SendToProgram(buf, cps);
+ if(*buf) {
+ if(toEngine) {
+ snprintf(buf2, MSG_SIZ, "option %s\n", buf);
+ SendToProgram(buf2, cps);
+ } else {
+ if(p != buf2) *p++ = ',';
+ strncpy(p, buf, 10*MSG_SIZ-1 - (p - buf2));
+ while(*p) p++;
+ }
+ }
}
+ return buf2;
}
void
cps->comboCnt = 0; // and values of combo boxes
}
SendToProgram(buf, cps);
- if(cps->reload) ResendOptions(cps);
+ if(cps->reload) ResendOptions(cps, TRUE);
} else {
SendToProgram("xboard\n", cps);
}
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);
}
int
NamesToList (char *names, char **engineList, char **engineMnemonic, char *group)
{
- char buf[MSG_SIZ], *p, *q;
+ char buf[2*MSG_SIZ], *p, *q;
int i=1, header, skip, all = !strcmp(group, "all"), depth = 0;
insert = names; // afterwards, this global will point just after last retrieved engine line or group end in the 'names'
skip = !all && group[0]; // if group requested, we start in skip mode
return i;
}
+void
+SaveEngineSettings (int n)
+{
+ int len; char *p, *q, *s, buf[MSG_SIZ], *optionSettings;
+ if(!currentEngine[n] || !currentEngine[n][0]) { DisplayMessage("saving failed: engine not from list", ""); return; } // no engine from list is loaded
+ p = strstr(firstChessProgramNames, currentEngine[n]);
+ if(!p) { DisplayMessage("saving failed: engine not found in list", ""); return; } // sanity check; engine could be deleted from list after loading
+ optionSettings = ResendOptions(n ? &second : &first, FALSE);
+ len = strlen(currentEngine[n]);
+ q = p + len; *p = 0; // cut list into head and tail piece
+ s = strstr(currentEngine[n], "firstOptions");
+ if(s && (s[-1] == '-' || s[-1] == '/') && (s[12] == ' ' || s[12] == '=') && (s[13] == '"' || s[13] == '\'')) {
+ char *r = s + 14;
+ while(*r && *r != s[13]) r++;
+ s[14] = 0; // cut currentEngine into head and tail part, removing old settings
+ snprintf(buf, MSG_SIZ, "%s%s%s", currentEngine[n], optionSettings, *r ? r : "\""); // synthesize new engine line
+ } else if(*optionSettings) {
+ snprintf(buf, MSG_SIZ, "%s -firstOptions \"%s\"", currentEngine[n], optionSettings);
+ }
+ ASSIGN(currentEngine[n], buf); // updated engine line
+ len = p - firstChessProgramNames + strlen(q) + strlen(currentEngine[n]) + 1;
+ s = malloc(len);
+ snprintf(s, len, "%s%s%s", firstChessProgramNames, currentEngine[n], q);
+ FREE(firstChessProgramNames); firstChessProgramNames = s; // new list
+}
+
// following implemented as macro to avoid type limitations
#define SWAP(item, temp) temp = appData.item[0]; appData.item[0] = appData.item[n]; appData.item[n] = temp;
if(n == 1) SwapEngines(n);
ParseArgsFromString(buf);
if(n == 1) SwapEngines(n);
+ if(n < 2) { ASSIGN(currentEngine[n], command[i]); }
if(n == 0 && *appData.secondChessProgram == NULLCHAR) {
SwapEngines(1); // set second same as first if not yet set (to suppress WB startup dialog)
ParseArgsFromString(buf);
if(mnemonic[n]) { // if somehow the engine with the selected nickname is no longer found in the list, we skip
ReplaceEngine(&first, 0);
FloatToFront(&appData.recentEngineList, command[n]);
+ ASSIGN(currentEngine[0], command[n]);
}
}
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)
&& result != GameIsDrawn)
{ int i, j, k=0, oppoKings = 0, color = (result==WhiteWins ? (int)WhitePawn : (int)BlackPawn);
for(j=BOARD_LEFT; j<BOARD_RGHT; j++) for(i=0; i<BOARD_HEIGHT; i++) {
- int p = (signed char)boards[forwardMostMove][i][j] - color;
+ int p = (int)boards[forwardMostMove][i][j] - color;
if(p >= 0 && p <= (int)WhiteKing) k++;
oppoKings += (p + color == WhiteKing + BlackPawn - color);
}
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,
redraw, init, gameMode);
}
pieceDefs = FALSE; // [HGM] gen: reset engine-defined piece moves
+ deadRanks = 0; // assume entire board is used
+ handSize = 0;
for(i=0; i<EmptySquare; i++) { FREE(pieceDesc[i]); pieceDesc[i] = NULL; }
CleanupTail(); // [HGM] vari: delete any stored variations
CommentPopDown(); // [HGM] make sure no comments to the previous game keep hanging on
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);
snprintf(buf, MSG_SIZ, " %d:%02d%c", seconds/60, seconds%60, 0);
}
+ if(appData.cumulativeTimePGN) {
+ snprintf(buf, MSG_SIZ, " %+ld", timeRemaining[i & 1][i+1]/1000);
+ }
+
snprintf( move_buffer, sizeof(move_buffer)/sizeof(move_buffer[0]),"{%s%.2f/%d%s}",
pvInfoList[i].score >= 0 ? "+" : "",
pvInfoList[i].score / 100.0,
}
void
-ToggleSecond ()
+StartSecond ()
{
- if(second.analyzing) {
- SendToProgram("exit\n", &second);
- second.analyzing = FALSE;
- } else {
- if (second.pr == NoProc) StartChessProgram(&second);
+ if(WaitForEngine(&second, StartSecond)) return;
InitChessProgram(&second, FALSE);
FeedMovesToProgram(&second, currentMove);
SendToProgram("analyze\n", &second);
second.analyzing = TRUE;
+ ThawUI();
+}
+
+void
+ToggleSecond ()
+{
+ if(second.analyzing) {
+ SendToProgram("exit\n", &second);
+ second.analyzing = FALSE;
+ } else {
+ StartSecond();
}
}
safeStrCpy(bookMove, "move ", sizeof(bookMove)/sizeof(bookMove[0]));
strcat(bookMove, bookHit);
- HandleMachineMove(bookMove, &first);
+ savedMessage = bookMove; // args for deferred call
+ savedState = &first;
+ ScheduleDelayedEvent(DeferredBookMove, 1);
}
}
safeStrCpy(bookMove, "move ", sizeof(bookMove)/sizeof(bookMove[0]));
strcat(bookMove, bookHit);
- HandleMachineMove(bookMove, &first);
+ savedMessage = bookMove; // args for deferred call
+ savedState = &first;
+ ScheduleDelayedEvent(DeferredBookMove, 1);
}
}
void
TwoMachinesEvent P((void))
{
- int i;
+ int i, move = forwardMostMove;
char buf[MSG_SIZ];
ChessProgramState *onmove;
char *bookHit = NULL;
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) {
+ if (!first.sendTime || !second.sendTime || move == 0) { // [HGM] first engine changed sides from Reset, so recalc time odds
+ ResetClocks();
timeRemaining[0][forwardMostMove] = whiteTimeRemaining;
timeRemaining[1][forwardMostMove] = blackTimeRemaining;
}
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();
startedFromSetupPosition = TRUE;
InitChessProgram(&first, FALSE);
if(fakeRights) { // [HGM] suppress this if we just pasted a FEN.
+ int r, f;
boards[0][EP_STATUS] = EP_NONE;
- boards[0][CASTLING][2] = boards[0][CASTLING][5] = BOARD_WIDTH>>1;
- if(boards[0][0][BOARD_WIDTH>>1] == king) {
- boards[0][CASTLING][1] = boards[0][0][BOARD_LEFT] == WhiteRook ? BOARD_LEFT : NoRights;
- boards[0][CASTLING][0] = boards[0][0][BOARD_RGHT-1] == WhiteRook ? BOARD_RGHT-1 : NoRights;
- } else boards[0][CASTLING][2] = NoRights;
- if(boards[0][BOARD_HEIGHT-1][BOARD_WIDTH>>1] == WHITE_TO_BLACK king) {
- boards[0][CASTLING][4] = boards[0][BOARD_HEIGHT-1][BOARD_LEFT] == BlackRook ? BOARD_LEFT : NoRights;
- boards[0][CASTLING][3] = boards[0][BOARD_HEIGHT-1][BOARD_RGHT-1] == BlackRook ? BOARD_RGHT-1 : NoRights;
- } else boards[0][CASTLING][5] = NoRights;
- if(gameInfo.variant == VariantSChess) {
- int i;
- for(i=BOARD_LEFT; i<BOARD_RGHT; i++) { // pieces in their original position are assumed virgin
- boards[0][VIRGIN][i] = 0;
- if(boards[0][0][i] == FIDEArray[0][i-BOARD_LEFT]) boards[0][VIRGIN][i] |= VIRGIN_W;
- if(boards[0][BOARD_HEIGHT-1][i] == FIDEArray[1][i-BOARD_LEFT]) boards[0][VIRGIN][i] |= VIRGIN_B;
+ for(f=0; f<=nrCastlingRights; f++) boards[0][CASTLING][f] = NoRights;
+ for(r=BOARD_HEIGHT-1; r>=0; r--) for(f=BOARD_RGHT-1; f>=BOARD_LEFT; f--) { // first pass: Kings & e.p.
+ if(rightsBoard[r][f]) {
+ ChessSquare p = boards[0][r][f];
+ if(p == (blackPlaysFirst ? WhitePawn : BlackPawn)) boards[0][EP_STATUS] = f;
+ else if(p == king) boards[0][CASTLING][2] = f;
+ else if(p == WHITE_TO_BLACK king) boards[0][CASTLING][5] = f;
+ else rightsBoard[r][f] = 2; // mark for second pass
+ }
+ }
+ for(r=BOARD_HEIGHT-1; r>=0; r--) for(f=BOARD_RGHT-1; f>=BOARD_LEFT; f--) { // second pass: Rooks
+ if(rightsBoard[r][f] == 2) {
+ ChessSquare p = boards[0][r][f];
+ if(p == WhiteRook) boards[0][CASTLING][(f < boards[0][CASTLING][2])] = f; else
+ if(p == BlackRook) boards[0][CASTLING][(f < boards[0][CASTLING][5])+3] = f;
}
}
}
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);
}
}
}
+ CopyBoard(rightsBoard, nullBoard);
if(gameMode != IcsExamining) { // [HGM] editpos: cycle trough boards
- int r;
+ int r, i;
for(r = 0; r < BOARD_HEIGHT; r++) {
for(x = BOARD_LEFT; x < BOARD_RGHT; x++) { // create 'menu board' by removing duplicates
ChessSquare p = menuBoard[r][x];
for(y = x + 1; y < BOARD_RGHT; y++) if(menuBoard[r][y] == p) menuBoard[r][y] = EmptySquare;
}
}
+ menuBoard[CASTLING][0] = menuBoard[CASTLING][3] = NoRights; // h-side Rook was deleted
DisplayMessage("Clicking clock again restores position", "");
if(gameInfo.variant != lastVariant) lastVariant = gameInfo.variant, CopyBoard(erasedBoard, boards[0]);
if(!nonEmpty) { // asked to clear an empty board
} else
CopyBoard(erasedBoard, currentBoard);
+ for(i=0; i<nrCastlingRights; i++) if(boards[0][CASTLING][i] != NoRights)
+ rightsBoard[castlingRank[i]][boards[0][CASTLING][i]] = 1; // copy remaining rights
}
}
if (gameMode == EditPosition) {
} else {
if(x < BOARD_LEFT || x >= BOARD_RGHT) {
if(x == BOARD_LEFT-2) {
- if(y < BOARD_HEIGHT-1-gameInfo.holdingsSize) break;
+ if(y < handSize-1-gameInfo.holdingsSize) break;
boards[0][y][1] = 0;
} else
if(x == BOARD_RGHT+1) {
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;
+ if(y == baseRank && (x == BOARD_WIDTH>>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) {
n = PieceToNumber(selection - BlackPawn);
if(n >= gameInfo.holdingsSize) { n = 0; selection = BlackPawn; }
- boards[0][BOARD_HEIGHT-1-n][0] = selection;
- boards[0][BOARD_HEIGHT-1-n][1]++;
+ boards[0][handSize-1-n][0] = selection;
+ boards[0][handSize-1-n][1]++;
} else
if(x == BOARD_RGHT+1 && selection < BlackPawn) {
n = PieceToNumber(selection);
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 (strncmp((*p), name, len) == 0
&& (*p)[len] == '=' && (*p)[len+1] == '\"') {
(*p) += len + 2;
- ASSIGN(*loc, *p); // kludge alert: assign rest of line just to be sure allocation is large enough so that sscanf below always fits
- sscanf(*p, "%[^\"]", *loc);
+ len = strlen(*p) + 1; if(len < MSG_SIZ && !strcmp(name, "option")) len = MSG_SIZ; // make sure string options have enough space to change their value
+ FREE(*loc); *loc = malloc(len);
+ strncpy(*loc, *p, len);
+ sscanf(*p, "%[^\"]", *loc); // should always fit, because we allocated at least strlen(*p)
while (**p && **p != '\"') (*p)++;
if (**p == '\"') (*p)++;
snprintf(buf, MSG_SIZ, "accepted %s\n", name);
opt->type = SaveButton;
} else return FALSE;
*p = 0; // terminate option name
+ *(int*) (opt->name + MSG_SIZ - 104) = opt->value; // hide default values somewhere
+ if(opt->target == &opt->textValue) strncpy(opt->name + MSG_SIZ - 100, opt->textValue, 99);
// now look if the command-line options define a setting for this engine option.
if(cps->optionSettings && cps->optionSettings[0])
p = strstr(cps->optionSettings, opt->name); else p = NULL;
if(!strcmp(((char**)opt->textValue)[n], q+1)) opt->value = n;
break;
case TextBox:
+ case FileName:
+ case PathName:
safeStrCpy(opt->textValue, q+1, MSG_SIZ - (opt->textValue - opt->name));
break;
case Spin:
DelayedEventCallback cb = GetDelayedEvent();
if ((cb == InitBackEnd3 && cps == &first) ||
(cb == SettingsMenuIfReady && cps == &second) ||
- (cb == LoadEngine) ||
+ (cb == LoadEngine) || (cb == StartSecond) ||
(cb == TwoMachinesEventIfReady)) {
CancelDelayedEvent();
ScheduleDelayedEvent(cb, val ? 1 : 3600000);
if (StringFeature(&p, "egt", &cps->egtFormats, cps)) continue;
if (StringFeature(&p, "option", &q, cps)) { // read to freshly allocated temp buffer first
if(cps->reload) { FREE(q); q = NULL; continue; } // we are reloading because of xreuse
+ if(cps->nrOptions == 0) { ASSIGN(cps->option[0].name, _("Make Persistent -save")); ParseOption(&(cps->option[cps->nrOptions++]), cps); }
FREE(cps->option[cps->nrOptions].name);
cps->option[cps->nrOptions].name = q; q = NULL;
if(!ParseOption(&(cps->option[cps->nrOptions++]), cps)) { // [HGM] options: add option feature
#define FUDGE 25 /* 25ms = 1/40 sec; should be plenty even for 50 Hz clocks */
+static int timeSuffix; // [HGM] This should realy be a passed parameter, but it has to pass through too many levels for my laziness...
+
/* Decrement running clock by amount of time that has passed */
void
DecrementClocks ()
{
- long timeRemaining;
+ long tRemaining;
long lastTickLength, fudge;
TimeMark now;
if (WhiteOnMove(forwardMostMove)) {
if(whiteNPS >= 0) lastTickLength = 0;
- timeRemaining = whiteTimeRemaining -= lastTickLength;
- if(timeRemaining < 0 && !appData.icsActive) {
+ tRemaining = whiteTimeRemaining -= lastTickLength;
+ if( tRemaining < 0 && !appData.icsActive) {
GetTimeQuota((forwardMostMove-whiteStartMove-1)/2, 0, whiteTC); // sets suddenDeath & nextSession;
if(suddenDeath) { // [HGM] if we run out of a non-last incremental session, go to the next
whiteStartMove = forwardMostMove; whiteTC = nextSession;
- lastWhite= timeRemaining = whiteTimeRemaining += GetTimeQuota(-1, 0, whiteTC);
+ lastWhite= tRemaining = whiteTimeRemaining += GetTimeQuota(-1, 0, whiteTC);
}
}
+ if(forwardMostMove && appData.moveTime) timeSuffix = timeRemaining[0][forwardMostMove-1] - tRemaining;
DisplayWhiteClock(whiteTimeRemaining - fudge,
WhiteOnMove(currentMove < forwardMostMove ? currentMove : forwardMostMove));
+ timeSuffix = 0;
} else {
if(blackNPS >= 0) lastTickLength = 0;
- timeRemaining = blackTimeRemaining -= lastTickLength;
- if(timeRemaining < 0 && !appData.icsActive) { // [HGM] if we run out of a non-last incremental session, go to the next
+ tRemaining = blackTimeRemaining -= lastTickLength;
+ if( tRemaining < 0 && !appData.icsActive) { // [HGM] if we run out of a non-last incremental session, go to the next
GetTimeQuota((forwardMostMove-blackStartMove-1)/2, 0, blackTC);
if(suddenDeath) {
blackStartMove = forwardMostMove;
- lastBlack = timeRemaining = blackTimeRemaining += GetTimeQuota(-1, 0, blackTC=nextSession);
+ lastBlack = tRemaining = blackTimeRemaining += GetTimeQuota(-1, 0, blackTC=nextSession);
}
}
+ if(forwardMostMove && appData.moveTime) timeSuffix = timeRemaining[1][forwardMostMove-1] - tRemaining;
DisplayBlackClock(blackTimeRemaining - fudge,
!WhiteOnMove(currentMove < forwardMostMove ? currentMove : forwardMostMove));
+ timeSuffix = 0;
}
if (CheckFlags()) return;
}
tickStartTM = now;
- intendedTickLength = NextTickLength(timeRemaining - fudge) + fudge;
+ intendedTickLength = NextTickLength( tRemaining - fudge) + fudge;
StartClockTimer(intendedTickLength);
/* if the time remaining has fallen below the alarm threshold, sound the
((gameMode == IcsPlayingBlack) && !WhiteOnMove(currentMove))
)) return;
- if (alarmSounded && (timeRemaining > appData.icsAlarmTime)) {
+ if (alarmSounded && ( tRemaining > appData.icsAlarmTime)) {
alarmSounded = FALSE;
- } else if (!alarmSounded && (timeRemaining <= appData.icsAlarmTime)) {
+ } else if (!alarmSounded && ( tRemaining <= appData.icsAlarmTime)) {
PlayAlarmSound();
alarmSounded = TRUE;
}
{
long second, minute, hour, day;
char *sign = "";
- static char buf[32];
+ static char buf[40], moveTime[8];
if (ms > 0 && ms <= 9900) {
/* convert milliseconds to tenths, rounding up */
minute = second / 60;
second = second % 60;
+ if(timeSuffix) snprintf(moveTime, 8, " (%d)", timeSuffix/1000); // [HGM] kludge alert; fraction contains move time
+ else *moveTime = NULLCHAR;
+
if (day > 0)
- snprintf(buf, sizeof(buf)/sizeof(buf[0]), " %s%ld:%02ld:%02ld:%02ld ",
- sign, day, hour, minute, second);
+ snprintf(buf, sizeof(buf)/sizeof(buf[0]), " %s%ld:%02ld:%02ld:%02ld%s ",
+ sign, day, hour, minute, second, moveTime);
else if (hour > 0)
- snprintf(buf, sizeof(buf)/sizeof(buf[0]), " %s%ld:%02ld:%02ld ", sign, hour, minute, second);
+ snprintf(buf, sizeof(buf)/sizeof(buf[0]), " %s%ld:%02ld:%02ld%s ", sign, hour, minute, second, moveTime);
else
- snprintf(buf, sizeof(buf)/sizeof(buf[0]), " %s%2ld:%02ld ", sign, minute, second);
+ snprintf(buf, sizeof(buf)/sizeof(buf[0]), " %s%2ld:%02ld%s ", sign, minute, second, moveTime);
return buf;
}
p = buf;
/* Piece placement data */
- for (i = BOARD_HEIGHT - 1; i >= 0; i--) {
+ for (i = BOARD_HEIGHT - 1 - deadRanks; i >= 0; i--) {
if(MSG_SIZ - (p - buf) < BOARD_RGHT - BOARD_LEFT + 20) { *p = 0; return StrSave(buf); }
emptycount = 0;
for (j = BOARD_LEFT; j < BOARD_RGHT; j++) {
*p++ = PieceToChar(piece);
}
for(i=0; i<gameInfo.holdingsSize; i++) { /* black holdings */
- piece = boards[move][BOARD_HEIGHT-i-1][0];
+ piece = boards[move][handSize-i-1][0];
if( piece != EmptySquare )
- for(j=0; j<(int) boards[move][BOARD_HEIGHT-i-1][1]; j++)
+ for(j=0; j<(int) boards[move][handSize-i-1][1]; j++)
*p++ = PieceToChar(piece);
}
}
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;
fromY = moveList[move - 1][1] - ONE;
toX = moveList[move - 1][2] - AAA;
toY = moveList[move - 1][3] - ONE;
- if (fromY == (whiteToPlay ? BOARD_HEIGHT-2 : 1) &&
- toY == (whiteToPlay ? BOARD_HEIGHT-4 : 3) &&
- boards[move][toY][toX] == (whiteToPlay ? BlackPawn : WhitePawn) &&
- fromX == toX) {
+ if ((whiteToPlay ? toY < fromY - 1 : toY > fromY + 1) &&
+ boards[move][toY][toX] == (whiteToPlay ? BlackPawn : WhitePawn) ) {
/* 2-square pawn move just happened */
- *p++ = toX + AAA;
- *p++ = whiteToPlay ? '6'+BOARD_HEIGHT-8 : '3';
+ *p++ = (3*toX + 5*fromX + 4)/8 + AAA;
+ *p++ = (3*toY + 5*fromY + 4)/8 + ONE;
+ if(gameInfo.variant == VariantBerolina) {
+ *p++ = toX + AAA;
+ *p++ = toY + ONE;
+ }
} else {
*p++ = '-';
}
}
}
+ i = boards[move][CHECK_COUNT];
+ if(i) {
+ sprintf(p, "%d+%d ", i&255, i>>8);
+ while(*p) p++;
+ }
+
if(moveCounts)
{ int i = 0, j=move;
p = fen;
+ for(i=1; i<=deadRanks; i++) for(j=BOARD_LEFT; j<BOARD_RGHT; j++) board[BOARD_HEIGHT-i][j] = DarkSquare;
+
/* Piece placement data */
- for (i = BOARD_HEIGHT - 1; i >= 0; i--) {
+ for (i = BOARD_HEIGHT - 1 - deadRanks; i >= 0; i--) {
j = 0;
for (;;) {
if (*p == '/' || *p == ' ' || *p == '[' ) {
/* [HGM] by default clear Crazyhouse holdings, if present */
if(gameInfo.holdingsWidth) {
- for(i=0; i<BOARD_HEIGHT; i++) {
+ for(i=0; i<handSize; i++) {
board[i][0] = EmptySquare; /* black holdings */
board[i][BOARD_WIDTH-1] = EmptySquare; /* white holdings */
board[i][1] = (ChessSquare) 0; /* black counts */
i = (int)piece - (int)BlackPawn;
i = PieceToNumber((ChessSquare)i);
if( i >= gameInfo.holdingsSize ) return FALSE;
- board[BOARD_HEIGHT-1-i][0] = piece; /* black holdings */
- board[BOARD_HEIGHT-1-i][1]++; /* black counts */
+ board[handSize-1-i][0] = piece; /* black holdings */
+ board[handSize-1-i][1]++; /* black counts */
bcnt++;
} else {
i = (int)piece - (int)WhitePawn;
if(board[BOARD_HEIGHT-1][j] == ClearBoard) {
if(!bcnt) return FALSE;
if(n >= bcnt) n = rand() % bcnt; // use same randomization for black and white if possible
- for(k=0, m=n; k<gameInfo.holdingsSize; k++) if((n -= board[BOARD_HEIGHT-1-k][1]) < 0) {
- board[BOARD_HEIGHT-1][j] = board[BOARD_HEIGHT-1-k][0]; bcnt--;
- if(--board[BOARD_HEIGHT-1-k][1] == 0) board[BOARD_HEIGHT-1-k][0] = EmptySquare;
+ for(k=0, m=n; k<gameInfo.holdingsSize; k++) if((n -= board[handSize-1-k][1]) < 0) {
+ board[BOARD_HEIGHT-1][j] = board[handSize-1-k][0]; bcnt--;
+ if(--board[handSize-1-k][1] == 0) board[handSize-1-k][0] = EmptySquare;
break;
}
}
if(*p=='-') {
p++; board[EP_STATUS] = EP_NONE;
} else {
- char c = *p++ - AAA;
-
- if(c < BOARD_LEFT || c >= BOARD_RGHT) return TRUE;
- if(*p >= '0' && *p <='9') p++;
- board[EP_STATUS] = c;
+ int d, r, c = *p - AAA;
+
+ if(c >= BOARD_LEFT && c < BOARD_RGHT) {
+ p++;
+ board[EP_STATUS] = board[EP_FILE] = c; r = 0;
+ if(*p >= '0' && *p <='9') r = board[EP_RANK] = *p++ - ONE;
+ d = (r < BOARD_HEIGHT << 1 ? 1 : -1); // assume double-push (P next to e.p. square nearer center)
+ if(board[r+d][c] == EmptySquare) d *= 2; // but if no Pawn there, triple push
+ board[LAST_TO] = 256*(r + d) + c;
+ c = *p++ - AAA;
+ if(c >= BOARD_LEFT && c < BOARD_RGHT) { // mover explicitly mentioned
+ if(*p >= '0' && *p <='9') r = board[EP_RANK] = *p++ - ONE;
+ board[LAST_TO] = 256*r + c;
+ if(!(board[EP_RANK]-r & 1)) board[EP_RANK] |= 128;
+ }
+ }
}
}
+ while(*p == ' ') p++;
+
+ board[CHECK_COUNT] = 0; // [HGM] 3check: check-count field
+ if(sscanf(p, "%d+%d", &i, &j) == 2) {
+ board[CHECK_COUNT] = i + 256*j;
+ while(*p && *p != ' ') p++;
+ }
- if(sscanf(p, "%d", &i) == 1) {
+ c = sscanf(p, "%d%*d +%d+%d", &i, &j, &k);
+ if(c > 0) {
FENrulePlies = i; /* 50-move ply counter */
/* (The move number is still ignored) */
+ if(c == 3 && !board[CHECK_COUNT]) board[CHECK_COUNT] = (3 - j) + 256*(3 - k); // SCIDB-style check count
}
return TRUE;
ToNrEvent(currentMove+1);
}
+int transparency[2];
+
void
LoadTheme ()
{
- char *p, *q, buf[MSG_SIZ];
+#define BUF_SIZ (2*MSG_SIZ)
+ char *p, *q, buf[BUF_SIZ];
if(engineLine && engineLine[0]) { // a theme was selected from the listbox
- snprintf(buf, MSG_SIZ, "-theme %s", engineLine);
+ snprintf(buf, BUF_SIZ, "-theme %s", engineLine);
ParseArgsFromString(buf);
ActivateTheme(TRUE); // also redo colors
return;
{
int len;
q = appData.themeNames;
- snprintf(buf, MSG_SIZ, "\"%s\"", nickName);
+ snprintf(buf, BUF_SIZ, "\"%s\"", nickName);
if(appData.useBitmaps) {
- snprintf(buf+strlen(buf), MSG_SIZ-strlen(buf), " -ubt true -lbtf \"%s\" -dbtf \"%s\" -lbtm %d -dbtm %d",
- appData.liteBackTextureFile, appData.darkBackTextureFile,
+ snprintf(buf+strlen(buf), BUF_SIZ-strlen(buf), " -ubt true -lbtf \"%s\"",
+ Shorten(appData.liteBackTextureFile));
+ snprintf(buf+strlen(buf), BUF_SIZ-strlen(buf), " -dbtf \"%s\" -lbtm %d -dbtm %d",
+ Shorten(appData.darkBackTextureFile),
appData.liteBackTextureMode,
appData.darkBackTextureMode );
} else {
- snprintf(buf+strlen(buf), MSG_SIZ-strlen(buf), " -ubt false -lsc %s -dsc %s",
- Col2Text(2), // lightSquareColor
- Col2Text(3) ); // darkSquareColor
+ snprintf(buf+strlen(buf), BUF_SIZ-strlen(buf), " -ubt false");
+ }
+ if(!appData.useBitmaps || transparency[0]) {
+ snprintf(buf+strlen(buf), BUF_SIZ-strlen(buf), " -lsc %s", Col2Text(2) ); // lightSquareColor
+ }
+ if(!appData.useBitmaps || transparency[1]) {
+ snprintf(buf+strlen(buf), BUF_SIZ-strlen(buf), " -dsc %s", Col2Text(3) ); // darkSquareColor
}
if(appData.useBorder) {
- snprintf(buf+strlen(buf), MSG_SIZ-strlen(buf), " -ub true -border \"%s\"",
+ snprintf(buf+strlen(buf), BUF_SIZ-strlen(buf), " -ub true -border \"%s\"",
appData.border);
} else {
- snprintf(buf+strlen(buf), MSG_SIZ-strlen(buf), " -ub false");
+ snprintf(buf+strlen(buf), BUF_SIZ-strlen(buf), " -ub false");
}
if(appData.useFont) {
- snprintf(buf+strlen(buf), MSG_SIZ-strlen(buf), " -upf true -pf \"%s\" -fptc \"%s\" -fpfcw %s -fpbcb %s",
+ snprintf(buf+strlen(buf), BUF_SIZ-strlen(buf), " -upf true -pf \"%s\" -fptc \"%s\" -fpfcw %s -fpbcb %s",
appData.renderPiecesWithFont,
appData.fontToPieceTable,
Col2Text(9), // appData.fontBackColorWhite
Col2Text(10) ); // appData.fontForeColorBlack
} else {
- snprintf(buf+strlen(buf), MSG_SIZ-strlen(buf), " -upf false -pid \"%s\"",
- appData.pieceDirectory);
- if(!appData.pieceDirectory[0])
- snprintf(buf+strlen(buf), MSG_SIZ-strlen(buf), " -wpc %s -bpc %s",
+ snprintf(buf+strlen(buf), BUF_SIZ-strlen(buf), " -upf false");
+ if(appData.pieceDirectory[0]) {
+ snprintf(buf+strlen(buf), BUF_SIZ-strlen(buf), " -pid \"%s\"", Shorten(appData.pieceDirectory));
+ if(appData.trueColors != 2) // 2 is a kludge to suppress this in WinBoard
+ snprintf(buf+strlen(buf), BUF_SIZ-strlen(buf), " -trueColors %s", appData.trueColors ? "true" : "false");
+ }
+ if(!appData.pieceDirectory[0] || !appData.trueColors)
+ snprintf(buf+strlen(buf), BUF_SIZ-strlen(buf), " -wpc %s -bpc %s",
Col2Text(0), // whitePieceColor
Col2Text(1) ); // blackPieceColor
}
- snprintf(buf+strlen(buf), MSG_SIZ-strlen(buf), " -hsc %s -phc %s\n",
+ snprintf(buf+strlen(buf), BUF_SIZ-strlen(buf), " -hsc %s -phc %s\n",
Col2Text(4), // highlightSquareColor
Col2Text(5) ); // premoveHighlightColor
appData.themeNames = malloc(len = strlen(q) + strlen(buf) + 1);