* Massachusetts.
*
* Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
- * 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
+ * 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
*
* Enhancements Copyright 2005 Alessandro Scotti
*
extern int chatCount;
int chattingPartner;
char marker[BOARD_RANKS][BOARD_FILES]; /* [HGM] marks for target squares */
+ChessSquare pieceSweep = EmptySquare;
+ChessSquare promoSweep = EmptySquare, defaultPromoChoice;
+int promoDefaultAltered;
/* States for ics_getting_history */
#define H_FALSE 0
char*
safeStrCpy( char *dst, const char *src, size_t count )
-{
- /* see for example: https://buildsecurityin.us-cert.gov/bsi-rules/home/g1/854-BSI.html
- *
- * usage: safeStrCpy( stringA, stringB, sizeof(stringA)/sizeof(stringA[0]);
- */
-
+{ // [HGM] made safe
+ int i;
assert( dst != NULL );
assert( src != NULL );
assert( count > 0 );
- strncpy( dst, src, count );
- if( dst[ count-1 ] != '\0' )
+ for(i=0; i<count; i++) if((dst[i] = src[i]) == NULLCHAR) break;
+ if( i == count && dst[count-1] != NULLCHAR)
{
+ dst[ count-1 ] = '\0'; // make sure incomplete copy still null-terminated
if(appData.debugMode)
- printf("safeStrCpy: copying %s into %s didn't work, not enough space %d\n",src,dst,count);
+ fprintf(debugFP, "safeStrCpy: copying %s into %s didn't work, not enough space %d\n",src,dst,count);
}
- dst[ count-1 ] = '\0';
return dst;
}
int shiftKey; // [HGM] set by mouse handler
int have_sent_ICS_logon = 0;
-int sending_ICS_login = 0;
-int sending_ICS_password = 0;
-
int movesPerSession;
int suddenDeath, whiteStartMove, blackStartMove; /* [HGM] for implementation of 'any per time' sessions, as in first part of byoyomi TC */
long whiteTimeRemaining, blackTimeRemaining, timeControl, timeIncrement, lastWhite, lastBlack;
BlackUnicorn, BlackBishop, BlackMan, BlackRook }
};
+ChessSquare SpartanArray[2][BOARD_FILES] = {
+ { WhiteRook, WhiteKnight, WhiteBishop, WhiteQueen,
+ WhiteKing, WhiteBishop, WhiteKnight, WhiteRook },
+ { BlackAlfil, BlackMarshall, BlackKing, BlackDragon,
+ BlackDragon, BlackKing, BlackAngel, BlackAlfil }
+};
+
ChessSquare fairyArray[2][BOARD_FILES] = { /* [HGM] Queen side differs from King side */
{ WhiteRook, WhiteKnight, WhiteBishop, WhiteQueen,
WhiteKing, WhiteBishop, WhiteKnight, WhiteRook },
- { BlackLance, BlackAlfil, BlackMarshall, BlackAngel,
- BlackKing, BlackMarshall, BlackAlfil, BlackLance }
+ { BlackCardinal, BlackAlfil, BlackMarshall, BlackAngel,
+ BlackKing, BlackMarshall, BlackAlfil, BlackCardinal }
};
ChessSquare ShatranjArray[2][BOARD_FILES] = { /* [HGM] (movGen knows about Shatranj Q and P) */
}
void
+CommonEngineInit()
+{ // [HGM] moved some code here from InitBackend1 that has to be done after both engines have contributed their settings
+ if (appData.firstPlaysBlack) {
+ first.twoMachinesColor = "black\n";
+ second.twoMachinesColor = "white\n";
+ } else {
+ first.twoMachinesColor = "white\n";
+ second.twoMachinesColor = "black\n";
+ }
+
+ first.other = &second;
+ second.other = &first;
+
+ { float norm = 1;
+ if(appData.timeOddsMode) {
+ norm = appData.timeOdds[0];
+ if(norm > appData.timeOdds[1]) norm = appData.timeOdds[1];
+ }
+ first.timeOdds = appData.timeOdds[0]/norm;
+ second.timeOdds = appData.timeOdds[1]/norm;
+ }
+
+ if(programVersion) free(programVersion);
+ if (appData.noChessProgram) {
+ programVersion = (char*) malloc(5 + strlen(PACKAGE_STRING));
+ sprintf(programVersion, "%s", PACKAGE_STRING);
+ } else {
+ /* [HGM] tidy: use tidy name, in stead of full pathname (which was probably a bug due to / vs \ ) */
+ programVersion = (char*) malloc(8 + strlen(PACKAGE_STRING) + strlen(first.tidy));
+ sprintf(programVersion, "%s + %s", PACKAGE_STRING, first.tidy);
+ }
+}
+
+void
+UnloadEngine(ChessProgramState *cps)
+{
+ /* Kill off first chess program */
+ if (cps->isr != NULL)
+ RemoveInputSource(cps->isr);
+ cps->isr = NULL;
+
+ if (cps->pr != NoProc) {
+ ExitAnalyzeMode();
+ DoSleep( appData.delayBeforeQuit );
+ SendToProgram("quit\n", cps);
+ DoSleep( appData.delayAfterQuit );
+ DestroyChildProcess(cps->pr, cps->useSigterm);
+ }
+ cps->pr = NoProc;
+}
+
+void
+ClearOptions(ChessProgramState *cps)
+{
+ int i;
+ cps->nrOptions = cps->comboCnt = 0;
+ for(i=0; i<MAX_OPTIONS; i++) {
+ cps->option[i].min = cps->option[i].max = cps->option[i].value = 0;
+ cps->option[i].textValue = 0;
+ }
+}
+
+char *engineNames[] = {
+"first",
+"second"
+};
+
+InitEngine(ChessProgramState *cps, int n)
+{ // [HGM] all engine initialiation put in a function that does one engine
+
+ ClearOptions(cps);
+
+ cps->which = engineNames[n];
+ cps->maybeThinking = FALSE;
+ cps->pr = NoProc;
+ cps->isr = NULL;
+ cps->sendTime = 2;
+ cps->sendDrawOffers = 1;
+
+ cps->program = appData.chessProgram[n];
+ cps->host = appData.host[n];
+ cps->dir = appData.directory[n];
+ cps->initString = appData.engInitString[n];
+ cps->computerString = appData.computerString[n];
+ cps->useSigint = TRUE;
+ cps->useSigterm = TRUE;
+ cps->reuse = appData.reuse[n];
+ cps->nps = appData.NPS[n]; // [HGM] nps: copy nodes per second
+ cps->useSetboard = FALSE;
+ cps->useSAN = FALSE;
+ cps->usePing = FALSE;
+ cps->lastPing = 0;
+ cps->lastPong = 0;
+ cps->usePlayother = FALSE;
+ cps->useColors = TRUE;
+ cps->useUsermove = FALSE;
+ cps->sendICS = FALSE;
+ cps->sendName = appData.icsActive;
+ cps->sdKludge = FALSE;
+ cps->stKludge = FALSE;
+ TidyProgramName(cps->program, cps->host, cps->tidy);
+ cps->matchWins = 0;
+ safeStrCpy(cps->variants, appData.variant, MSG_SIZ);
+ cps->analysisSupport = 2; /* detect */
+ cps->analyzing = FALSE;
+ cps->initDone = FALSE;
+
+ /* New features added by Tord: */
+ cps->useFEN960 = FALSE;
+ cps->useOOCastle = TRUE;
+ /* End of new features added by Tord. */
+ cps->fenOverride = appData.fenOverride[n];
+
+ /* [HGM] time odds: set factor for each machine */
+ cps->timeOdds = appData.timeOdds[n];
+
+ /* [HGM] secondary TC: how to handle sessions that do not fit in 'level'*/
+ cps->accumulateTC = appData.accumulateTC[n];
+ cps->maxNrOfSessions = 1;
+
+ /* [HGM] debug */
+ cps->debug = FALSE;
+ cps->supportsNPS = UNKNOWN;
+
+ /* [HGM] options */
+ cps->optionSettings = appData.engOptions[n];
+
+ cps->scoreIsAbsolute = appData.scoreIsAbsolute[n]; /* [AS] */
+ cps->isUCI = appData.isUCI[n]; /* [AS] */
+ cps->hasOwnBookUCI = appData.hasOwnBookUCI[n]; /* [AS] */
+
+ if (appData.protocolVersion[n] > PROTOVER
+ || appData.protocolVersion[n] < 1)
+ {
+ char buf[MSG_SIZ];
+ int len;
+
+ len = snprintf(buf, MSG_SIZ, _("protocol version %d not supported"),
+ appData.protocolVersion[n]);
+ if( (len > MSG_SIZ) && appData.debugMode )
+ fprintf(debugFP, "InitBackEnd1: buffer truncated.\n");
+
+ DisplayFatalError(buf, 0, 2);
+ }
+ else
+ {
+ cps->protocolVersion = appData.protocolVersion[n];
+ }
+
+ InitEngineUCI( installDir, cps ); // [HGM] moved here from winboard.c, to make available in xboard
+}
+
+ChessProgramState *savCps;
+
+void
+LoadEngine()
+{
+ int i;
+ if(WaitForEngine(savCps, LoadEngine)) return;
+ CommonEngineInit(); // recalculate time odds
+ if(gameInfo.variant != StringToVariant(appData.variant)) {
+ // we changed variant when loading the engine; this forces us to reset
+ Reset(TRUE, savCps != &first);
+ EditGameEvent(); // for consistency with other path, as Reset changes mode
+ }
+ InitChessProgram(savCps, FALSE);
+ SendToProgram("force\n", savCps);
+ DisplayMessage("", "");
+ if (startedFromSetupPosition) SendBoard(savCps, backwardMostMove);
+ for (i = backwardMostMove; i < forwardMostMove; i++) SendMoveToProgram(i, savCps);
+ ThawUI();
+ SetGNUMode();
+}
+
+void
+ReplaceEngine(ChessProgramState *cps, int n)
+{
+ EditGameEvent();
+ UnloadEngine(cps);
+ appData.noChessProgram = False;
+ appData.clockMode = True;
+ InitEngine(cps, n);
+ savCps = cps; // parameter to LoadEngine passed as globals, to allow scheduled calling :-(
+ LoadEngine();
+}
+
+void
InitBackEnd1()
{
int matched, min, sec;
/* [AS] Adjudication threshold */
adjudicateLossThreshold = appData.adjudicateLossThreshold;
- first.which = _("first");
- second.which = _("second");
- first.maybeThinking = second.maybeThinking = FALSE;
- first.pr = second.pr = NoProc;
- first.isr = second.isr = NULL;
- first.sendTime = second.sendTime = 2;
- first.sendDrawOffers = 1;
- if (appData.firstPlaysBlack) {
- first.twoMachinesColor = "black\n";
- second.twoMachinesColor = "white\n";
- } else {
- first.twoMachinesColor = "white\n";
- second.twoMachinesColor = "black\n";
- }
- first.program = appData.firstChessProgram;
- second.program = appData.secondChessProgram;
- first.host = appData.firstHost;
- second.host = appData.secondHost;
- first.dir = appData.firstDirectory;
- second.dir = appData.secondDirectory;
- first.other = &second;
- second.other = &first;
- first.initString = appData.initString;
- second.initString = appData.secondInitString;
- first.computerString = appData.firstComputerString;
- second.computerString = appData.secondComputerString;
- first.useSigint = second.useSigint = TRUE;
- first.useSigterm = second.useSigterm = TRUE;
- first.reuse = appData.reuseFirst;
- second.reuse = appData.reuseSecond;
- first.nps = appData.firstNPS; // [HGM] nps: copy nodes per second
- second.nps = appData.secondNPS;
- first.useSetboard = second.useSetboard = FALSE;
- first.useSAN = second.useSAN = FALSE;
- first.usePing = second.usePing = FALSE;
- first.lastPing = second.lastPing = 0;
- first.lastPong = second.lastPong = 0;
- first.usePlayother = second.usePlayother = FALSE;
- first.useColors = second.useColors = TRUE;
- first.useUsermove = second.useUsermove = FALSE;
- first.sendICS = second.sendICS = FALSE;
- first.sendName = second.sendName = appData.icsActive;
- first.sdKludge = second.sdKludge = FALSE;
- first.stKludge = second.stKludge = FALSE;
- TidyProgramName(first.program, first.host, first.tidy);
- TidyProgramName(second.program, second.host, second.tidy);
- first.matchWins = second.matchWins = 0;
- safeStrCpy(first.variants, appData.variant, sizeof(first.variants)/sizeof(first.variants[0]));
- safeStrCpy(second.variants, appData.variant,sizeof(second.variants)/sizeof(second.variants[0]));
- first.analysisSupport = second.analysisSupport = 2; /* detect */
- first.analyzing = second.analyzing = FALSE;
- first.initDone = second.initDone = FALSE;
-
- /* New features added by Tord: */
- first.useFEN960 = FALSE; second.useFEN960 = FALSE;
- first.useOOCastle = TRUE; second.useOOCastle = TRUE;
- /* End of new features added by Tord. */
- first.fenOverride = appData.fenOverride1;
- second.fenOverride = appData.fenOverride2;
-
- /* [HGM] time odds: set factor for each machine */
- first.timeOdds = appData.firstTimeOdds;
- second.timeOdds = appData.secondTimeOdds;
- { float norm = 1;
- if(appData.timeOddsMode) {
- norm = first.timeOdds;
- if(norm > second.timeOdds) norm = second.timeOdds;
- }
- first.timeOdds /= norm;
- second.timeOdds /= norm;
- }
-
- /* [HGM] secondary TC: how to handle sessions that do not fit in 'level'*/
- first.accumulateTC = appData.firstAccumulateTC;
- second.accumulateTC = appData.secondAccumulateTC;
- first.maxNrOfSessions = second.maxNrOfSessions = 1;
-
- /* [HGM] debug */
- first.debug = second.debug = FALSE;
- first.supportsNPS = second.supportsNPS = UNKNOWN;
-
- /* [HGM] options */
- first.optionSettings = appData.firstOptions;
- second.optionSettings = appData.secondOptions;
-
- first.scoreIsAbsolute = appData.firstScoreIsAbsolute; /* [AS] */
- second.scoreIsAbsolute = appData.secondScoreIsAbsolute; /* [AS] */
- first.isUCI = appData.firstIsUCI; /* [AS] */
- second.isUCI = appData.secondIsUCI; /* [AS] */
- first.hasOwnBookUCI = appData.firstHasOwnBookUCI; /* [AS] */
- second.hasOwnBookUCI = appData.secondHasOwnBookUCI; /* [AS] */
-
- if (appData.firstProtocolVersion > PROTOVER
- || appData.firstProtocolVersion < 1)
- {
- char buf[MSG_SIZ];
- int len;
-
- len = snprintf(buf, MSG_SIZ, _("protocol version %d not supported"),
- appData.firstProtocolVersion);
- if( (len > MSG_SIZ) && appData.debugMode )
- fprintf(debugFP, "InitBackEnd1: buffer truncated.\n");
-
- DisplayFatalError(buf, 0, 2);
- }
- else
- {
- first.protocolVersion = appData.firstProtocolVersion;
- }
-
- if (appData.secondProtocolVersion > PROTOVER
- || appData.secondProtocolVersion < 1)
- {
- char buf[MSG_SIZ];
- int len;
-
- len = snprintf(buf, MSG_SIZ, _("protocol version %d not supported"),
- appData.secondProtocolVersion);
- if( (len > MSG_SIZ) && appData.debugMode )
- fprintf(debugFP, "InitBackEnd1: buffer truncated.\n");
-
- DisplayFatalError(buf, 0, 2);
- }
- else
- {
- second.protocolVersion = appData.secondProtocolVersion;
- }
+ InitEngine(&first, 0);
+ InitEngine(&second, 1);
+ CommonEngineInit();
if (appData.icsActive) {
appData.clockMode = TRUE; /* changes dynamically in ICS mode */
-// } else if (*appData.searchTime != NULLCHAR || appData.noChessProgram) {
} else if (appData.noChessProgram) { // [HGM] st: searchTime mode now also is clockMode
appData.clockMode = FALSE;
first.sendTime = second.sendTime = 0;
}
#endif
- if (appData.noChessProgram) {
- programVersion = (char*) malloc(5 + strlen(PACKAGE_STRING));
- sprintf(programVersion, "%s", PACKAGE_STRING);
- } else {
- /* [HGM] tidy: use tidy name, in stead of full pathname (which was probably a bug due to / vs \ ) */
- programVersion = (char*) malloc(8 + strlen(PACKAGE_STRING) + strlen(first.tidy));
- sprintf(programVersion, "%s + %s", PACKAGE_STRING, first.tidy);
- }
-
if (!appData.icsActive) {
char buf[MSG_SIZ];
int len;
case VariantJanus: /* should work */
case VariantSuper: /* experimental */
case VariantGreat: /* experimental, requires legality testing to be off */
+ case VariantSChess: /* S-Chess, should work */
+ case VariantSpartan: /* should work */
break;
}
}
- InitEngineUCI( installDir, &first ); // [HGM] moved here from winboard.c, to make available in xboard
- InitEngineUCI( installDir, &second );
}
int NextIntegerFromString( char ** str, long * value )
if(mps)
snprintf(buf, MSG_SIZ, ":%d/%s+%g", mps, mytc, ti);
- else
+ else
snprintf(buf, MSG_SIZ, ":%s+%g", mytc, ti);
} else {
if(mps)
snprintf(buf, MSG_SIZ, ":%d/%s", mps, mytc);
- else
+ else
snprintf(buf, MSG_SIZ, ":%s", mytc);
}
fullTimeControlString = StrSave(buf); // this should now be in PGN format
-
+
if( NextTimeControlFromString( &tc, &tc1 ) != 0 ) {
return FALSE;
}
}
void
+MatchEvent(int mode)
+{ // [HGM] moved out of InitBackend3, to make it callable when match starts through menu
+ /* Set up machine vs. machine match */
+ if (appData.noChessProgram) {
+ DisplayFatalError(_("Can't have a match with no chess programs"),
+ 0, 2);
+ return;
+ }
+ matchMode = mode;
+ matchGame = 1;
+ if (*appData.loadGameFile != NULLCHAR) {
+ int index = appData.loadGameIndex; // [HGM] autoinc
+ if(index<0) lastIndex = index = 1;
+ if (!LoadGameFromFile(appData.loadGameFile,
+ index,
+ appData.loadGameFile, FALSE)) {
+ DisplayFatalError(_("Bad game file"), 0, 1);
+ return;
+ }
+ } else if (*appData.loadPositionFile != NULLCHAR) {
+ int index = appData.loadPositionIndex; // [HGM] autoinc
+ if(index<0) lastIndex = index = 1;
+ if (!LoadPositionFromFile(appData.loadPositionFile,
+ index,
+ appData.loadPositionFile)) {
+ DisplayFatalError(_("Bad position file"), 0, 1);
+ return;
+ }
+ }
+ first.matchWins = second.matchWins = 0; // [HGM] match: needed in later matches\r
+ TwoMachinesEvent();
+}
+
+void
InitBackEnd3 P((void))
{
GameMode initialMode;
}
if (appData.matchMode) {
- /* Set up machine vs. machine match */
- if (appData.noChessProgram) {
- DisplayFatalError(_("Can't have a match with no chess programs"),
- 0, 2);
- return;
- }
- matchMode = TRUE;
- matchGame = 1;
- if (*appData.loadGameFile != NULLCHAR) {
- int index = appData.loadGameIndex; // [HGM] autoinc
- if(index<0) lastIndex = index = 1;
- if (!LoadGameFromFile(appData.loadGameFile,
- index,
- appData.loadGameFile, FALSE)) {
- DisplayFatalError(_("Bad game file"), 0, 1);
- return;
- }
- } else if (*appData.loadPositionFile != NULLCHAR) {
- int index = appData.loadPositionIndex; // [HGM] autoinc
- if(index<0) lastIndex = index = 1;
- if (!LoadPositionFromFile(appData.loadPositionFile,
- index,
- appData.loadPositionFile)) {
- DisplayFatalError(_("Bad position file"), 0, 1);
- return;
- }
- }
- TwoMachinesEvent();
+ MatchEvent(TRUE);
} else if (*appData.cmailGameName != NULLCHAR) {
/* Set up cmail mode */
ReloadCmailMsgEvent(TRUE);
if(channel >= 0) // channel broadcast; look if there is a chatbox for this channel
for(p=0; p<MAX_CHAT; p++) {
- if(channel == atoi(chatPartner[p])) {
+ if(chatPartner[p][0] >= '0' && chatPartner[p][0] <= '9' && channel == atoi(chatPartner[p])) {
talker[0] = '['; strcat(talker, "] ");
Colorize(channel == 1 ? ColorChannel1 : ColorChannel, FALSE);
chattingPartner = p; break;
if (!have_sent_ICS_logon && looking_at(buf, &i, "login:")) {
ICSInitScript();
have_sent_ICS_logon = 1;
- /* if we don't send the login/password via icsLogon, use special readline
- code for it */
- if (strlen(appData.icsLogon)==0)
- {
- sending_ICS_password = 0; // in case we come back to login
- sending_ICS_login = 1;
- };
continue;
}
- /* need to shadow the password */
- if (!sending_ICS_password && looking_at(buf, &i, "password:")) {
- /* if we don't send the login/password via icsLogon, use special readline
- code for it */
- if (strlen(appData.icsLogon)==0)
- sending_ICS_password = 1;
- continue;
- }
if (ics_getting_history != H_GETTING_MOVES /*smpos kludge*/ &&
(looking_at(buf, &i, "\n<12> ") ||
int j, k, n, moveNum, white_stren, black_stren, white_time, black_time, takeback;
int double_push, castle_ws, castle_wl, castle_bs, castle_bl, irrev_count;
char to_play, board_chars[200];
- char move_str[500], str[500], elapsed_time[500];
+ char move_str[MSG_SIZ], str[MSG_SIZ], elapsed_time[MSG_SIZ];
char black[32], white[32];
Board board;
int prevMove = currentMove;
&ticking);
if (n < 21) {
- snprintf(str, sizeof(str), _("Failed to parse board string:\n\"%s\""), string);
+ snprintf(str, MSG_SIZ, _("Failed to parse board string:\n\"%s\""), string);
DisplayError(str, 0);
return;
}
if(!appData.testLegality && move_str[1] != '@') { // drops never ambiguous (parser chokes on long form!)
if(appData.debugMode)
fprintf(debugFP, "replaced ICS move '%s' by '%s'\n", move_str, buf);
- safeStrCpy(move_str, buf, sizeof(move_str)/sizeof(move_str[0]));
+ safeStrCpy(move_str, buf, MSG_SIZ);
}
valid = ParseOneMove(move_str, moveNum - 1, &moveType,
&fromX, &fromY, &toX, &toY, &promoChar)
strcat(parseList[moveNum - 1], " ");
strcat(parseList[moveNum - 1], elapsed_time);
/* currentMoveString is set as a side-effect of ParseOneMove */
- if(gameInfo.variant == VariantShogi && currentMoveString[4]) currentMoveString[4] = '+';
+ if(gameInfo.variant == VariantShogi && currentMoveString[4]) currentMoveString[4] = '^';
safeStrCpy(moveList[moveNum - 1], currentMoveString, sizeof(moveList[moveNum - 1])/sizeof(moveList[moveNum - 1][0]));
strcat(moveList[moveNum - 1], "\n");
- if(gameInfo.holdingsWidth && !appData.disguise) // inherit info that ICS does not give from previous board
+ if(gameInfo.holdingsWidth && !appData.disguise && gameInfo.variant != VariantSuper
+ && gameInfo.variant != VariantGreat) // 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
}
+static int lastX, lastY, selectFlag, dragging;
+
+void
+Sweep(int step)
+{
+ ChessSquare king = WhiteKing, pawn = WhitePawn, last = promoSweep;
+ if(gameInfo.variant == VariantKnightmate) king = WhiteUnicorn;
+ if(gameInfo.variant == VariantSuicide || gameInfo.variant == VariantGiveaway) king = EmptySquare;
+ if(promoSweep >= BlackPawn) king = WHITE_TO_BLACK king, pawn = WHITE_TO_BLACK pawn;
+ if(gameInfo.variant == VariantSpartan && pawn == BlackPawn) pawn = BlackLance, king = EmptySquare;
+ if(fromY != BOARD_HEIGHT-2 && fromY != 1) pawn = EmptySquare;
+ do {
+ promoSweep -= step;
+ if(promoSweep == EmptySquare) promoSweep = BlackPawn; // wrap
+ else if((int)promoSweep == -1) promoSweep = WhiteKing;
+ else if(promoSweep == BlackPawn && step < 0) promoSweep = WhitePawn;
+ else if(promoSweep == WhiteKing && step > 0) promoSweep = BlackKing;
+ if(!step) step = 1;
+ } while(PieceToChar(promoSweep) == '.' || PieceToChar(promoSweep) == '~' || promoSweep == pawn ||
+ appData.testLegality && (promoSweep == king ||
+ gameInfo.variant == VariantShogi && promoSweep != PROMOTED last && last != PROMOTED promoSweep && last != promoSweep));
+ ChangeDragPiece(promoSweep);
+}
+
+int PromoScroll(int x, int y)
+{
+ int step = 0;
+
+ if(promoSweep == EmptySquare || !appData.sweepSelect) return FALSE;
+ if(abs(x - lastX) < 15 && abs(y - lastY) < 15) return FALSE;
+ if( y > lastY + 2 ) step = -1; else if(y < lastY - 2) step = 1;
+ if(!step) return FALSE;
+ lastX = x; lastY = y;
+ if((promoSweep < BlackPawn) == flipView) step = -step;
+ if(step > 0) selectFlag = 1;
+ if(!selectFlag) Sweep(step);
+ return FALSE;
+}
+
+void
+NextPiece(int step)
+{
+ ChessSquare piece = boards[currentMove][toY][toX];
+ do {
+ pieceSweep -= step;
+ if(pieceSweep == EmptySquare) pieceSweep = WhitePawn; // wrap
+ if((int)pieceSweep == -1) pieceSweep = BlackKing;
+ if(!step) step = -1;
+ } while(PieceToChar(pieceSweep) == '.');
+ boards[currentMove][toY][toX] = pieceSweep;
+ DrawPosition(FALSE, boards[currentMove]);
+ boards[currentMove][toY][toX] = piece;
+}
/* [HGM] Shogi move preprocessor: swap digits for letters, vice versa */
void
AlphaRank(char *move, int n)
int *fromX, *fromY, *toX, *toY;
char *promoChar;
{
- if (appData.debugMode) {
- fprintf(debugFP, "move to parse: %s\n", move);
- }
*moveType = yylexstr(moveNum, move, yy_textstr, sizeof yy_textstr);
switch (*moveType) {
moveList[endPV-1][1] = fromY + ONE;
moveList[endPV-1][2] = toX + AAA;
moveList[endPV-1][3] = toY + ONE;
+ moveList[endPV-1][4] = promoChar;
+ moveList[endPV-1][5] = NULLCHAR;
+ strncat(moveList[endPV-1], "\n", MOVE_LEN);
if(storeComments)
CoordsToAlgebraic(boards[endPV - 1],
PosFlags(endPV - 1),
DrawPosition(TRUE, boards[currentMove]);
}
-static int lastX, lastY;
-
Boolean
LoadMultiPV(int x, int y, char *buf, int index, int *start, int *end)
{
{ // step through PV based on mouse coordinates (called on mouse move)
int margin = h>>3, step = 0;
- if(endPV < 0) return;
// we must somehow check if right button is still down (might be released off board!)
- if(y < margin && (abs(x - lastX) > 6 || abs(y - lastY) > 6)) step = 1; else
- if(y > h - margin && (abs(x - lastX) > 6 || abs(y - lastY) > 6)) step = -1; else
- if( y > lastY + 6 ) step = -1; else if(y < lastY - 6) step = 1;
+ if(endPV < 0 && pieceSweep == EmptySquare) return; // needed in XBoard because lastX/Y is shared :-(
+ if(abs(x - lastX) < 7 && abs(y - lastY) < 7) return;
+ if( y > lastY + 2 ) step = -1; else if(y < lastY - 2) step = 1;
if(!step) return;
lastX = x; lastY = y;
+
+ if(pieceSweep != EmptySquare) { NextPiece(step); return; }
+ if(endPV < 0) return;
+ if(y < margin) step = 1; else
+ if(y > h - margin) step = -1;
if(currentMove + step > endPV || currentMove + step < forwardMostMove) step = 0;
currentMove += step;
if(currentMove == forwardMostMove) ClearPremoveHighlights(); else
int i, j, pawnRow, overrule,
oldx = gameInfo.boardWidth,
oldy = gameInfo.boardHeight,
- oldh = gameInfo.holdingsWidth,
- oldv = gameInfo.variant;
+ oldh = gameInfo.holdingsWidth;
+ static int oldv;
if(appData.icsActive) shuffleOpenings = FALSE; // [HGM] shuffle: in ICS mode, only shuffle on ICS request
if(startVariant == gameInfo.variant) // [HGM] nicks: enable nicknames in original variant
SetCharTable(pieceNickName, appData.pieceNickNames);
else SetCharTable(pieceNickName, "............");
+ pieces = FIDEArray;
switch (gameInfo.variant) {
case VariantFischeRandom:
shuffleOpenings = TRUE;
default:
- pieces = FIDEArray;
break;
case VariantShatranj:
pieces = ShatranjArray;
gameInfo.boardWidth = 10;
SetCharTable(pieceToChar, "PNBRQ..ACKpnbrq..ack");
break;
+ case VariantSChess:
+ SetCharTable(pieceToChar, "PNBRQ..HEKpnbrq..hek");
+ gameInfo.holdingsSize = 7;
+ break;
case VariantJanus:
pieces = JanusArray;
gameInfo.boardWidth = 10;
pieces = KnightmateArray;
SetCharTable(pieceToChar, "P.BRQ.....M.........K.p.brq.....m.........k.");
break;
+ case VariantSpartan:
+ pieces = SpartanArray;
+ SetCharTable(pieceToChar, "PNBRQ................K......lwg.....c...h..k");
+ break;
case VariantFairy:
pieces = fairyArray;
SetCharTable(pieceToChar, "PNBRQFEACWMOHIJGDVLSUKpnbrqfeacwmohijgdvlsuk");
if(j < BOARD_LEFT || j >= BOARD_RGHT || overrule) continue;
initialPosition[0][j] = pieces[0][j-gameInfo.holdingsWidth];
initialPosition[pawnRow][j] = WhitePawn;
- initialPosition[BOARD_HEIGHT-pawnRow-1][j] = BlackPawn;
+ initialPosition[BOARD_HEIGHT-pawnRow-1][j] = gameInfo.variant == VariantSpartan ? BlackLance : BlackPawn;
if(gameInfo.variant == VariantXiangqi) {
if(j&1) {
initialPosition[pawnRow][j] =
initialPosition[BOARD_HEIGHT-1-PieceToNumber(WhiteMan)][0] = BlackMan;
initialPosition[BOARD_HEIGHT-1-PieceToNumber(WhiteMan)][1] = 9;
}
+ if( gameInfo.variant == VariantSChess ) {
+ initialPosition[1][0] = BlackMarshall;
+ initialPosition[2][0] = BlackAngel;
+ initialPosition[6][BOARD_WIDTH-1] = WhiteMarshall;
+ initialPosition[5][BOARD_WIDTH-1] = WhiteAngel;
+ initialPosition[1][1] = initialPosition[2][1] =
+ initialPosition[6][BOARD_WIDTH-2] = initialPosition[5][BOARD_WIDTH-2] = 1;
+ }
if (appData.debugMode) {
fprintf(debugFP, "shuffleOpenings = %d\n", shuffleOpenings);
}
if(oldx != gameInfo.boardWidth ||
oldy != gameInfo.boardHeight ||
+ oldv != gameInfo.variant ||
oldh != gameInfo.holdingsWidth
-#ifdef GOTHIC
- || oldv == VariantGothic || // For licensing popups
- gameInfo.variant == VariantGothic
-#endif
-#ifdef FALCON
- || oldv == VariantFalcon ||
- gameInfo.variant == VariantFalcon
-#endif
)
InitDrawingSizes(-2 ,0);
+ oldv = gameInfo.variant;
if (redraw)
DrawPosition(TRUE, boards[currentMove]);
}
/* Kludge to set black to move, avoiding the troublesome and now
* deprecated "black" command.
*/
- if (!WhiteOnMove(moveNum)) SendToProgram("a2a3\n", cps);
+ 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("edit\n", cps);
SendToProgram("#\n", cps);
setboardSpoiledMachineBlack = 0; /* [HGM] assume WB 4.2.7 already solves this after sending setboard */
}
+ChessSquare
+DefaultPromoChoice(int white)
+{
+ ChessSquare result;
+ if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier || gameInfo.variant == VariantMakruk)
+ result = WhiteFerz; // no choice
+ else if(gameInfo.variant == VariantSuicide || gameInfo.variant == VariantGiveaway)
+ result= WhiteKing; // in Suicide Q is the last thing we want
+ else if(gameInfo.variant == VariantSpartan)
+ result = white ? WhiteQueen : WhiteAngel;
+ else result = WhiteQueen;
+ if(!white) result = WHITE_TO_BLACK result;
+ return result;
+}
+
static int autoQueen; // [HGM] oneclick
int
promotionZoneSize = 3;
}
+ // Treat Lance as Pawn when it is not representing Amazon
+ if(gameInfo.variant != VariantSuper) {
+ if(piece == WhiteLance) piece = WhitePawn; else
+ if(piece == BlackLance) piece = BlackPawn;
+ }
+
// next weed out all moves that do not touch the promotion zone at all
if((int)piece >= BlackPawn) {
if(toY >= promotionZoneSize && fromY >= promotionZoneSize)
*promoChoice = PieceToChar(BlackQueen); // Queen as good as any
return FALSE;
}
- if(autoQueen) { // predetermined
- if(gameInfo.variant == VariantSuicide || gameInfo.variant == VariantLosers)
- *promoChoice = PieceToChar(BlackKing); // in Suicide Q is the last thing we want
- else *promoChoice = PieceToChar(BlackQueen);
- return FALSE;
- }
+ // give caller the default choice even if we will not make it
+ *promoChoice = ToLower(PieceToChar(defaultPromoChoice));
+ if(gameInfo.variant == VariantShogi) *promoChoice = '+';
+ if(appData.sweepSelect && gameInfo.variant != VariantGreat
+ && gameInfo.variant != VariantShogi
+ && gameInfo.variant != VariantSuper) return FALSE;
+ if(autoQueen) return FALSE; // predetermined
// suppress promotion popup on illegal moves that are not premoves
premove = gameMode == IcsPlayingWhite && !WhiteOnMove(currentMove) ||
gameMode == IcsPlayingBlack && WhiteOnMove(currentMove);
if(appData.testLegality && !premove) {
moveType = LegalityTest(boards[currentMove], PosFlags(currentMove),
- fromY, fromX, toY, toX, NULLCHAR);
+ fromY, fromX, toY, toX, gameInfo.variant == VariantShogi ? '+' : NULLCHAR);
if(moveType != WhitePromotion && moveType != BlackPromotion)
return FALSE;
}
case BeginningOfGame:
case AnalyzeMode:
case Training:
+ if(fromY == DROP_RANK) break; // [HGM] drop moves (entered through move type-in) are automatically assigned to side-to-move
if ((int) boards[currentMove][fromY][fromX] >= (int) BlackPawn &&
(int) boards[currentMove][fromY][fromX] < (int) EmptySquare) {
/* User is moving for Black */
pup = boards[currentMove][toY][toX];
/* [HGM] If move started in holdings, it means a drop. Convert to standard form */
- if( fromX == BOARD_LEFT-2 || fromX == BOARD_RGHT+1) {
+ if( (fromX == BOARD_LEFT-2 || fromX == BOARD_RGHT+1) && fromY != DROP_RANK ) {
if( pup != EmptySquare ) return;
moveType = WhiteOnMove(currentMove) ? WhiteDrop : BlackDrop;
- if(appData.debugMode) fprintf(debugFP, "Drop move %d, curr=%d, x=%d,y=%d, p=%d\n",
+ 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
fromX = fromX ? WhitePawn : BlackPawn; // first piece type in selected holdings
- while(PieceToChar(fromX) == '.' || PieceToNumber(fromX) != fromY && fromX != (int) EmptySquare) fromX++;
+ while(PieceToChar(fromX) == '.' || PieceToNumber(fromX) != fromY && fromX != (int) EmptySquare) fromX++;
fromY = DROP_RANK;
}
MakeMove(fromX, fromY, toX, toY, promoChar); /*updates forwardMostMove*/
- if(Adjudicate(NULL)) return 1; // [HGM] adjudicate: take care of automtic game end
+ if(Adjudicate(NULL)) { // [HGM] adjudicate: take care of automatic game end
+ ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/
+ return 1;
+ }
if (gameMode == BeginningOfGame) {
if (appData.noChessProgram) {
switch (gameMode) {
case EditGame:
+ if(appData.testLegality)
switch (MateTest(boards[currentMove], PosFlags(currentMove)) ) {
case MT_NONE:
case MT_CHECK:
}
userOfferedDraw = FALSE; // [HGM] drawclaim: after move made, and tested for claimable draw
+ promoDefaultAltered = FALSE; // [HGM] fall back on default choice
if(bookHit) { // [HGM] book: simulate book reply
static char bookMove[MSG_SIZ]; // a bit generous?
return FALSE;
}
+ChessSquare gatingPiece = EmptySquare; // exported to front-end, for dragging
+
+int CanPromote(ChessSquare piece, int y)
+{
+ if(gameMode == EditPosition) return FALSE; // no promotions when editing position
+ // some variants have fixed promotion piece, no promotion at all, or another selection mechanism
+ if(gameInfo.variant == VariantShogi || gameInfo.variant == VariantXiangqi ||
+ gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat ||
+ gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier ||
+ gameInfo.variant == VariantMakruk) return FALSE;
+ return (piece == BlackPawn && y == 1 ||
+ piece == WhitePawn && y == BOARD_HEIGHT-2 ||
+ piece == BlackLance && y == 1 ||
+ piece == WhiteLance && y == BOARD_HEIGHT-2 );
+}
+
void LeftClick(ClickType clickType, int xPix, int yPix)
{
int x, y;
Boolean saveAnimate;
- static int second = 0, promotionChoice = 0, dragging = 0;
+ static int second = 0, promotionChoice = 0, clearFlag = 0;
char promoChoice = NULLCHAR;
+ ChessSquare piece;
if(appData.seekGraph && appData.icsActive && loggedOn &&
(gameMode == BeginningOfGame || gameMode == IcsIdle)) {
x = BOARD_WIDTH - 1 - x;
}
+ if(promoSweep != EmptySquare) { // up-click during sweep-select of promo-piece
+ defaultPromoChoice = promoSweep;
+ promoSweep = EmptySquare; // terminate sweep
+ promoDefaultAltered = TRUE;
+ if(!selectFlag) x = fromX, y = fromY; // and fake up-click on same square if we were still selecting
+ }
+
if(promotionChoice) { // we are waiting for a click to indicate promotion piece
if(clickType == Release) return; // ignore upclick of click-click destination
promotionChoice = FALSE; // only one chance: if click not OK it is interpreted as cancel
|| x == BOARD_RGHT+1 && y >= gameInfo.holdingsSize) )
return;
+ if(clickType == Press && fromX == x && fromY == y && promoDefaultAltered)
+ fromX = fromY = -1; // second click on piece after altering default promo piece treated as first click
+
+ if(!promoDefaultAltered) { // determine default promotion piece, based on the side the user is moving for
+ int side = (gameMode == IcsPlayingWhite || gameMode == MachinePlaysBlack ||
+ gameMode != MachinePlaysWhite && gameMode != IcsPlayingBlack && WhiteOnMove(currentMove));
+ defaultPromoChoice = DefaultPromoChoice(side);
+ }
+
autoQueen = appData.alwaysPromoteToQueen;
if (fromX == -1) {
- if(!appData.oneClick || !OnlyMove(&x, &y, FALSE)) {
- if (clickType == Press) {
+ int originalY = y;
+ gatingPiece = EmptySquare;
+ if (clickType != Press) {
+ if(dragging) { // [HGM] from-square must have been reset due to game end since last press
+ DragPieceEnd(xPix, yPix); dragging = 0;
+ DrawPosition(FALSE, NULL);
+ }
+ return;
+ }
+ fromX = x; fromY = y;
+ 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) {
/* First square */
- if (OKToStartUserMove(x, y)) {
- fromX = x;
- fromY = y;
+ if (OKToStartUserMove(fromX, fromY)) {
second = 0;
MarkTargetSquares(0);
DragPieceBegin(xPix, yPix); dragging = 1;
+ if(appData.sweepSelect && CanPromote(piece = boards[currentMove][fromY][fromX], fromY)) {
+ promoSweep = defaultPromoChoice;
+ selectFlag = 0; lastX = xPix; lastY = yPix;
+ Sweep(0); // Pawn that is going to promote: preview promotion piece
+ DisplayMessage("", _("Pull pawn backwards to under-promote"));
+ }
if (appData.highlightDragging) {
- SetHighlights(x, y, -1, -1);
+ SetHighlights(fromX, fromY, -1, -1);
}
- }
- } else if(dragging) { // [HGM] from-square must have been reset due to game end since last press
- DragPieceEnd(xPix, yPix); dragging = 0;
- DrawPosition(FALSE, NULL);
+ } else fromX = fromY = -1;
+ return;
}
- return;
- }
}
/* fromX != -1 */
/* Check if clicking again on the same color piece */
fromP = boards[currentMove][fromY][fromX];
toP = boards[currentMove][y][x];
- frc = gameInfo.variant == VariantFischeRandom || gameInfo.variant == VariantCapaRandom;
+ frc = gameInfo.variant == VariantFischeRandom || gameInfo.variant == VariantCapaRandom || gameInfo.variant == VariantSChess;
if ((WhitePawn <= fromP && fromP <= WhiteKing &&
WhitePawn <= toP && toP <= WhiteKing &&
!(fromP == WhiteKing && toP == WhiteRook && frc) &&
!(fromP == BlackKing && toP == BlackRook && frc))) {
/* Clicked again on same color piece -- changed his mind */
second = (x == fromX && y == fromY);
+ promoDefaultAltered = FALSE;
if(!second || appData.oneClick && !OnlyMove(&x, &y, TRUE)) {
if (appData.highlightDragging) {
SetHighlights(x, y, -1, -1);
ClearHighlights();
}
if (OKToStartUserMove(x, y)) {
+ if(gameInfo.variant == VariantSChess && // S-Chess: back-rank piece selected after holdings means gating
+ (fromX == BOARD_LEFT-2 || fromX == BOARD_RGHT+1) &&
+ y == (toP < BlackPawn ? 0 : BOARD_HEIGHT-1))
+ gatingPiece = boards[currentMove][fromY][fromX];
+ else gatingPiece = EmptySquare;
fromX = x;
fromY = y; dragging = 1;
MarkTargetSquares(0);
DragPieceBegin(xPix, yPix);
+ if(appData.sweepSelect && CanPromote(piece = boards[currentMove][y][x], y)) {
+ promoSweep = defaultPromoChoice;
+ selectFlag = 0; lastX = xPix; lastY = yPix;
+ Sweep(0); // Pawn that is going to promote: preview promotion piece
+ }
}
- return;
}
+ if(x == fromX && y == fromY) return; // if OnlyMove altered (x,y) we go on
+ second = FALSE;
}
// ignore clicks on holdings
if(x < BOARD_LEFT || x >= BOARD_RGHT) return;
if (clickType == Release && x == fromX && y == fromY) {
DragPieceEnd(xPix, yPix); dragging = 0;
+ if(clearFlag) {
+ // a deferred attempt to click-click move an empty square on top of a piece
+ boards[currentMove][y][x] = EmptySquare;
+ ClearHighlights();
+ DrawPosition(FALSE, boards[currentMove]);
+ fromX = fromY = -1; clearFlag = 0;
+ return;
+ }
if (appData.animateDragging) {
/* Undo animation damage if any */
DrawPosition(FALSE, NULL);
/* Second up/down in same square; just abort move */
second = 0;
fromX = fromY = -1;
+ gatingPiece = EmptySquare;
ClearHighlights();
gotPremove = 0;
ClearPremoveHighlights();
return;
}
+ clearFlag = 0;
+
/* we now have a different from- and (possibly off-board) to-square */
/* Completed move */
toX = x;
toY = y;
saveAnimate = appData.animate;
if (clickType == Press) {
+ if(gameMode == EditPosition && boards[currentMove][fromY][fromX] == EmptySquare) {
+ // must be Edit Position mode with empty-square selected
+ fromX = x; fromY = y; DragPieceBegin(xPix, yPix); dragging = 1; // consider this a new attempt to drag
+ if(x >= BOARD_LEFT && x < BOARD_RGHT) clearFlag = 1; // and defer click-click move of empty-square to up-click
+ return;
+ }
/* Finish clickclick move */
if (appData.animate || appData.highlightLastMove) {
SetHighlights(fromX, fromY, toX, toY);
}
// off-board moves should not be highlighted
- if(x < 0 || x < 0) ClearHighlights();
+ if(x < 0 || y < 0) ClearHighlights();
+
+ if(gatingPiece != EmptySquare) promoChoice = ToLower(PieceToChar(gatingPiece));
if (HasPromotionChoice(fromX, fromY, toX, toY, &promoChoice)) {
SetHighlights(fromX, fromY, toX, toY);
xSqr = EventToSquare(x, BOARD_WIDTH);
ySqr = EventToSquare(y, BOARD_HEIGHT);
- if (action == Release) UnLoadPV(); // [HGM] pv
+ if (action == Release) {
+ if(pieceSweep != EmptySquare) {
+ EditPositionMenuEvent(pieceSweep, toX, toY);
+ pieceSweep = EmptySquare;
+ } else UnLoadPV(); // [HGM] pv
+ }
if (action != Press) return -2; // return code to be ignored
switch (gameMode) {
case IcsExamining:
if(xSqr < BOARD_LEFT || xSqr >= BOARD_RGHT) return -1;\r
case EditPosition:
if (xSqr == BOARD_LEFT-1 || xSqr == BOARD_RGHT) return -1;\r
- if (xSqr < 0 || ySqr < 0) return -1;\r
- whichMenu = 0; // edit-position menu
- break;
+ if (xSqr < 0 || ySqr < 0) return -1;
+ if(appData.pieceMenu) { whichMenu = 0; break; } // edit-position menu
+ 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;
+ NextPiece(0);
+ return -2;\r
case IcsObserving:
if(!appData.icsEngineAnalyze) return -1;
case IcsPlayingWhite:
if(canAdjudicate && appData.checkMates) {
if(engineOpponent)
SendMoveToProgram(forwardMostMove-1, engineOpponent); // make sure opponent gets move
- ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/
GameEnds( WhiteOnMove(forwardMostMove) ? BlackWins : WhiteWins,
"Xboard adjudication: King destroyed", GE_XBOARD );
return 1;
if(canAdjudicate && appData.checkMates) {
if(engineOpponent)
SendMoveToProgram(forwardMostMove-1, engineOpponent); // make sure opponent gets to see move
- ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/
GameEnds( WhiteOnMove(forwardMostMove) ? WhiteWins : BlackWins,
"Xboard adjudication: Bare king", GE_XBOARD );
return 1;
/* but only adjudicate if adjudication enabled */
if(engineOpponent)
SendMoveToProgram(forwardMostMove-1, engineOpponent); // make sure opponent gets move
- ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/
GameEnds( nrW > 1 ? WhiteWins : nrB > 1 ? BlackWins : GameIsDrawn,
"Xboard adjudication: Bare king", GE_XBOARD );
return 1;
if(canAdjudicate && appData.checkMates && result) { // [HGM] mates: adjudicate finished games if requested
if(engineOpponent)
SendMoveToProgram(forwardMostMove-1, engineOpponent); /* make sure opponent gets to see move */
- ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/
GameEnds( result, reason, GE_XBOARD );
return 1;
}
SendToProgram("force\n", engineOpponent); // suppress reply
SendMoveToProgram(forwardMostMove-1, engineOpponent); /* make sure opponent gets to see last move */
}
- ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/
GameEnds( GameIsDrawn, "Xboard adjudication: Insufficient mating material", GE_XBOARD );
return 1;
}
SendToProgram("force\n", engineOpponent); // suppress reply
SendMoveToProgram(forwardMostMove-1, engineOpponent); /* make sure opponent gets to see move */
}
- ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/
GameEnds( GameIsDrawn, "Xboard adjudication: Trivial draw", GE_XBOARD );
return 1;
}
SendToProgram("force\n", engineOpponent); // suppress reply
SendMoveToProgram(forwardMostMove-1, engineOpponent); /* make sure opponent gets to see move */
}
- ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/
GameEnds( result, details, GE_XBOARD );
return 1;
}
SendToProgram("force\n", engineOpponent); // suppress reply
SendMoveToProgram(forwardMostMove-1, engineOpponent); /* make sure opponent gets to see move */
}
- ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/
GameEnds( GameIsDrawn, "Xboard adjudication: 50-move rule", GE_XBOARD );
return 1;
}
SendToProgram("force\n", engineOpponent); // suppress reply
SendMoveToProgram(forwardMostMove-1, engineOpponent); /* make sure opponent gets to see move */
}
- ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/
GameEnds( GameIsDrawn, p, GE_XBOARD );
return 1;
}
SendToProgram("force\n", engineOpponent); // suppress reply
SendMoveToProgram(forwardMostMove-1, engineOpponent); /* make sure opponent gets to see move */
}
- ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/
GameEnds( GameIsDrawn, "Xboard adjudication: long game", GE_XBOARD );
return 1;
}
&fromX, &fromY, &toX, &toY, &promoChar)) {
/* Machine move could not be parsed; ignore it. */
snprintf(buf1, MSG_SIZ*10, _("Illegal move \"%s\" from %s machine"),
- machineMove, cps->which);
+ machineMove, _(cps->which));
DisplayError(buf1, 0);
snprintf(buf1, MSG_SIZ*10, "Xboard: Forfeit due to invalid move: %s (%c%c%c%c) res=%d",
machineMove, fromX+AAA, fromY+ONE, toX+AAA, toY+ONE, moveType);
if (cps->sendTime == 2) cps->sendTime = 1;
if (cps->offeredDraw) cps->offeredDraw--;
- /* currentMoveString is set as a side-effect of ParseOneMove */
- safeStrCpy(machineMove, currentMoveString, sizeof(machineMove)/sizeof(machineMove[0]));
- strcat(machineMove, "\n");
- safeStrCpy(moveList[forwardMostMove], machineMove, sizeof(moveList[forwardMostMove])/sizeof(moveList[forwardMostMove][0]));
-
/* [AS] Save move info*/
pvInfoList[ forwardMostMove ].score = programStats.score;
pvInfoList[ forwardMostMove ].depth = programStats.depth;
}
}
- if(Adjudicate(cps)) return; // [HGM] adjudicate: for all automatic game ends
+ if(Adjudicate(cps)) {
+ ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/
+ return; // [HGM] adjudicate: for all automatic game ends
+ }
#if ZIPPY
if ((gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack) &&
return; // [HGM] This return was missing, causing option features to be recognized as non-compliant commands!
}
+ if (!appData.testLegality && !strncmp(message, "setup ", 6)) { // [HGM] allow first engine to define opening position
+ int dummy, s=6; char buf[MSG_SIZ];
+ if(appData.icsActive || forwardMostMove != 0 || cps != &first || startedFromSetupPosition) return;
+ if(sscanf(message, "setup (%s", buf) == 1) s = 8 + strlen(buf), buf[s-9] = NULLCHAR, SetCharTable(pieceToChar, buf);
+ ParseFEN(boards[0], &dummy, message+s);
+ DrawPosition(TRUE, boards[0]);
+ startedFromSetupPosition = TRUE;
+ return;
+ }
/* [HGM] Allow engine to set up a position. Don't ask me why one would
* want this, I was asked to put it in, and obliged.
*/
* Look for communication commands
*/
if (!strncmp(message, "telluser ", 9)) {
- EscapeExpand(message+9, message+9); // [HGM] esc: allow escape sequences in popup box
+ if(message[9] == '\\' && message[10] == '\\')
+ EscapeExpand(message+9, message+11); // [HGM] esc: allow escape sequences in popup box
DisplayNote(message + 9);
return;
}
if (!strncmp(message, "tellusererror ", 14)) {
cps->userError = 1;
- EscapeExpand(message+14, message+14); // [HGM] esc: allow escape sequences in popup box
+ if(message[14] == '\\' && message[15] == '\\')
+ EscapeExpand(message+14, message+16); // [HGM] esc: allow escape sequences in popup box
DisplayError(message + 14, 0);
return;
}
gameMode = EditGame;
ModeHighlight();
}
+ /* [HGM] illegal-move claim should forfeit game when Xboard */
+ /* only passes fully legal moves */
+ if( appData.testLegality && gameMode == TwoMachinesPlay ) {
+ GameEnds( cps->twoMachinesColor[0] == 'w' ? BlackWins : WhiteWins,
+ "False illegal-move claim", GE_XBOARD );
+ return; // do not take back move we tested as valid
+ }
currentMove = forwardMostMove-1;
DisplayMove(currentMove-1); /* before DisplayMoveError */
SwitchClocks(forwardMostMove-1); // [HGM] race
DisplayBothClocks();
snprintf(buf1, 10*MSG_SIZ, _("Illegal move \"%s\" (rejected by %s chess program)"),
- parseList[currentMove], cps->which);
+ parseList[currentMove], _(cps->which));
DisplayMoveError(buf1);
DrawPosition(FALSE, boards[currentMove]);
-
- /* [HGM] illegal-move claim should forfeit game when Xboard */
- /* only passes fully legal moves */
- if( appData.testLegality && gameMode == TwoMachinesPlay ) {
- GameEnds( cps->twoMachinesColor[0] == 'w' ? BlackWins : WhiteWins,
- "False illegal-move claim", GE_XBOARD );
- }
return;
}
if (strncmp(message, "time", 4) == 0 && StrStr(message, "Illegal")) {
cps->maybeThinking = FALSE;
snprintf(buf1, sizeof(buf1), _("Failed to start %s chess program %s on %s: %s\n"),
- cps->which, cps->program, cps->host, message);
+ _(cps->which), cps->program, cps->host, message);
RemoveInputSource(cps->isr);
DisplayFatalError(buf1, 0, 1);
return;
/* Hint move could not be parsed!? */
snprintf(buf2, sizeof(buf2),
_("Illegal hint move \"%s\"\nfrom %s chess program"),
- buf1, cps->which);
+ buf1, _(cps->which));
DisplayError(buf2, 0);
}
} else {
yynewstr(game);
for (;;) {
yyboardindex = boardIndex;
- moveType = (ChessMove) yylex();
+ moveType = (ChessMove) Myylex();
switch (moveType) {
case IllegalMove: /* maybe suicide chess, etc. */
if (appData.debugMode) {
if (q != NULL) *q = NULLCHAR;
p++;
}
+ while(q = strchr(p, '\n')) *q = ' '; // [HGM] crush linefeeds in result message
gameInfo.resultDetails = StrSave(p);
continue;
}
if( board[toY][toX] != EmptySquare )
board[EP_STATUS] = EP_CAPTURE;
- /* [HGM] In Shatranj and Courier all promotions are to Ferz */
- if((gameInfo.variant==VariantShatranj || gameInfo.variant==VariantCourier || gameInfo.variant == VariantMakruk)
- && promoChar != 0) promoChar = PieceToChar(WhiteFerz);
-
if (fromY == DROP_RANK) {
/* must be first */
piece = board[toY][toX] = (ChessSquare) fromX;
} else {
int i;
+ if( board[fromY][fromX] == WhiteLance || board[fromY][fromX] == BlackLance ) {
+ if( gameInfo.variant != VariantSuper && gameInfo.variant != VariantShogi )
+ board[EP_STATUS] = EP_PAWN_MOVE; // Lance is Pawn-like in most variants
+ } else
if( board[fromY][fromX] == WhitePawn ) {
if(fromY != toY) // [HGM] Xiangqi sideway Pawn moves should not count as 50-move breakers
board[EP_STATUS] = EP_PAWN_MOVE;
king += (int) WhiteUnicorn - (int) WhiteKing;
/* Code added by Tord: */
- /* FRC castling assumed when king captures friendly rook. */
- if (board[fromY][fromX] == WhiteKing &&
- board[toY][toX] == WhiteRook) {
+ /* FRC castling assumed when king captures friendly rook. [HGM] or RxK for S-Chess */
+ if (board[fromY][fromX] == WhiteKing && board[toY][toX] == WhiteRook ||
+ board[fromY][fromX] == WhiteRook && board[toY][toX] == WhiteKing) {
board[fromY][fromX] = EmptySquare;
board[toY][toX] = EmptySquare;
- if(toX > fromX) {
+ if((toX > fromX) != (piece == WhiteRook)) {
board[0][BOARD_RGHT-2] = WhiteKing; board[0][BOARD_RGHT-3] = WhiteRook;
} else {
board[0][BOARD_LEFT+2] = WhiteKing; board[0][BOARD_LEFT+3] = WhiteRook;
}
- } else if (board[fromY][fromX] == BlackKing &&
- board[toY][toX] == BlackRook) {
+ } else if (board[fromY][fromX] == BlackKing && board[toY][toX] == BlackRook ||
+ board[fromY][fromX] == BlackRook && board[toY][toX] == BlackKing) {
board[fromY][fromX] = EmptySquare;
board[toY][toX] = EmptySquare;
- if(toX > fromX) {
+ if((toX > fromX) != (piece == BlackRook)) {
board[BOARD_HEIGHT-1][BOARD_RGHT-2] = BlackKing; board[BOARD_HEIGHT-1][BOARD_RGHT-3] = BlackRook;
} else {
board[BOARD_HEIGHT-1][BOARD_LEFT+2] = BlackKing; board[BOARD_HEIGHT-1][BOARD_LEFT+3] = BlackRook;
board[toY][toX] = king;
board[toY][toX+1] = board[fromY][BOARD_LEFT];
board[fromY][BOARD_LEFT] = EmptySquare;
- } else if (board[fromY][fromX] == WhitePawn
+ } else if ((board[fromY][fromX] == WhitePawn && gameInfo.variant != VariantXiangqi ||
+ board[fromY][fromX] == WhiteLance && gameInfo.variant != VariantSuper && gameInfo.variant != VariantShogi)
&& toY >= BOARD_HEIGHT-promoRank
- && gameInfo.variant != VariantXiangqi
) {
/* white pawn promotion */
board[toY][toX] = CharToPiece(ToUpper(promoChar));
board[toY][toX] = BlackKing;
board[fromY][0] = EmptySquare;
board[toY][2] = BlackRook;
- } else if (board[fromY][fromX] == BlackPawn
+ } else if ((board[fromY][fromX] == BlackPawn && gameInfo.variant != VariantXiangqi ||
+ board[fromY][fromX] == BlackLance && gameInfo.variant != VariantSuper && gameInfo.variant != VariantShogi)
&& toY < promoRank
- && gameInfo.variant != VariantXiangqi
) {
/* black pawn promotion */
board[toY][toX] = CharToPiece(ToLower(promoChar));
/* [HGM] OK, so I have written it. Holdings are stored in the */
/* penultimate board files, so they are automaticlly stored */
/* in the game history. */
- if (fromY == DROP_RANK) {
+ if (fromY == DROP_RANK || gameInfo.variant == VariantSChess
+ && promoChar && piece != WhitePawn && piece != BlackPawn) {
/* Delete from holdings, by decreasing count */
/* and erasing image if necessary */
- p = (int) fromX;
+ p = fromY == DROP_RANK ? (int) fromX : CharToPiece(piece > BlackPawn ? ToLower(promoChar) : ToUpper(promoChar));
if(p < (int) BlackPawn) { /* white drop */
p -= (int)WhitePawn;
p = PieceToNumber((ChessSquare)p);
}
}
if (captured != EmptySquare && gameInfo.holdingsSize > 0
- && gameInfo.variant != VariantBughouse ) {
+ && gameInfo.variant != VariantBughouse && gameInfo.variant != VariantSChess ) {
/* [HGM] holdings: Add to holdings, if holdings exist */
if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat) {
// [HGM] superchess: suppress flipping color of captured pieces by reverse pre-flip
board[toY][toX] = EmptySquare;
}
}
+ if(gameInfo.variant == VariantSChess && promoChar != NULLCHAR && promoChar != '=' && piece != WhitePawn && piece != BlackPawn) {
+ board[fromY][fromX] = CharToPiece(piece < BlackPawn ? ToUpper(promoChar) : ToLower(promoChar)); // S-Chess gating
+ } else
if(promoChar == '+') {
/* [HGM] Shogi-style promotions, to piece implied by original (Might overwrite orinary Pawn promotion) */
board[toY][toX] = (ChessSquare) (PROMOTED piece);
- } else if(!appData.testLegality) { // without legality testing, unconditionally believe promoChar
- board[toY][toX] = CharToPiece(promoChar);
+ } else if(!appData.testLegality && promoChar != NULLCHAR && promoChar != '=') { // without legality testing, unconditionally believe promoChar
+ board[toY][toX] = CharToPiece(piece < BlackPawn ? ToUpper(promoChar) : ToLower(promoChar));
}
- if((gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)
- && promoChar != NULLCHAR && gameInfo.holdingsSize) {
+ if((gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)
+ && promoChar != NULLCHAR && gameInfo.holdingsSize) {
// [HGM] superchess: take promotion piece out of holdings
int k = PieceToNumber(CharToPiece(ToUpper(promoChar)));
if((int)piece < (int)BlackPawn) { // determine stm from piece color
if( gameInfo.variant == VariantBughouse || gameInfo.variant == VariantCrazyhouse )
overruled = gameInfo.boardWidth != 8 || gameInfo.boardHeight != 8 || gameInfo.holdingsSize != 5;
if( gameInfo.variant == VariantCapablanca || gameInfo.variant == VariantCapaRandom ||
- gameInfo.variant == VariantGothic || gameInfo.variant == VariantFalcon )
+ gameInfo.variant == VariantGothic || gameInfo.variant == VariantFalcon || gameInfo.variant == VariantJanus )
overruled = gameInfo.boardWidth != 10 || gameInfo.boardHeight != 8 || gameInfo.holdingsSize != 0;
if( gameInfo.variant == VariantCourier )
overruled = gameInfo.boardWidth != 12 || gameInfo.boardHeight != 8 || gameInfo.holdingsSize != 0;
overruled = gameInfo.boardWidth != 8 || gameInfo.boardHeight != 8 || gameInfo.holdingsSize != 8;
if( gameInfo.variant == VariantGreat )
overruled = gameInfo.boardWidth != 10 || gameInfo.boardHeight != 8 || gameInfo.holdingsSize != 8;
+ if( gameInfo.variant == VariantSChess )
+ overruled = gameInfo.boardWidth != 8 || gameInfo.boardHeight != 8 || gameInfo.holdingsSize != 7;
if(overruled) {
snprintf(b, MSG_SIZ, "%dx%d+%d_%s", gameInfo.boardWidth, gameInfo.boardHeight,
first.matchWins, second.matchWins,
appData.matchGames - (first.matchWins + second.matchWins));
popupRequested++; // [HGM] crash: postpone to after resetting endingGame
+ if (appData.firstPlaysBlack) { // [HGM] match: back to original for next match
+ first.twoMachinesColor = "black\n";
+ second.twoMachinesColor = "white\n";
+ } else {
+ first.twoMachinesColor = "white\n";
+ second.twoMachinesColor = "black\n";
+ }
}
}
if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile) &&
return;
if (matchMode || appData.timeDelay == 0)
continue;
- if (appData.timeDelay < 0 || gameMode == AnalyzeFile)
+ if (appData.timeDelay < 0)
return;
StartLoadGameTimer((long)(1000.0 * appData.timeDelay));
break;
fprintf(debugFP, "AutoPlayOneMove(): current %d\n", currentMove);
}
- if (gameMode != PlayFromGameFile)
+ if (gameMode != PlayFromGameFile && gameMode != AnalyzeFile)
return FALSE;
+ if (gameMode == AnalyzeFile && currentMove > backwardMostMove) {
+ pvInfoList[currentMove].depth = programStats.depth;
+ pvInfoList[currentMove].score = programStats.score;
+ pvInfoList[currentMove].time = 0;
+ if(currentMove < forwardMostMove) AppendComment(currentMove+1, lastPV[0], 2);
+ }
+
if (currentMove >= forwardMostMove) {
+ if(gameMode == AnalyzeFile) { ExitAnalyzeMode(); SendToProgram("force\n", &first); }
gameMode = EditGame;
ModeHighlight();
} else {
if (gameFileFP == NULL)
return FALSE;
- moveType = (ChessMove) yylex();
+ moveType = (ChessMove) Myylex();
}
done = FALSE;
if (q != NULL) *q = NULLCHAR;
p++;
}
+ while(q = strchr(p, '\n')) *q = ' '; // [HGM] crush linefeeds in result message
GameEnds(moveType, p, GE_FILE);
done = TRUE;
if (cmailMsgLoaded) {
return FALSE;
} else {
/* currentMoveString is set as a side-effect of yylex */
- strcat(currentMoveString, "\n");
- safeStrCpy(moveList[forwardMostMove], currentMoveString, sizeof(moveList[forwardMostMove])/sizeof(moveList[forwardMostMove][0]));
thinkOutput[0] = NULLCHAR;
MakeMove(fromX, fromY, toX, toY, promoChar);
cm = lastLoadGameStart = EndOfFile;
while (gn > 0) {
yyboardindex = forwardMostMove;
- cm = (ChessMove) yylex();
+ cm = (ChessMove) Myylex();
switch (cm) {
case EndOfFile:
if (cmailMsgLoaded) {
if (gn > 0) {
do {
yyboardindex = forwardMostMove;
- cm = (ChessMove) yylex();
+ cm = (ChessMove) Myylex();
} while (cm == PGNTag || cm == Comment);
}
break;
/* Skip any header junk before position diagram and/or move 1 */
for (;;) {
yyboardindex = forwardMostMove;
- cm = (ChessMove) yylex();
+ cm = (ChessMove) Myylex();
if (cm == EndOfFile ||
cm == GNUChessGame || cm == XBoardGame) {
}
yyboardindex = forwardMostMove;
- cm = (ChessMove) yylex();
+ cm = (ChessMove) Myylex();
/* Handle comments interspersed among the tags */
while (cm == Comment) {
p = yy_text;
AppendComment(currentMove, p, FALSE);
yyboardindex = forwardMostMove;
- cm = (ChessMove) yylex();
+ cm = (ChessMove) Myylex();
}
}
for (i = BOARD_HEIGHT - 1; i >= 0; i--)
for (j = BOARD_LEFT; j < BOARD_RGHT; p++)
switch (*p) {
+ case '{':
case '[':
case '-':
case ' ':
}
}
yyboardindex = forwardMostMove;
- cm = (ChessMove) yylex();
+ cm = (ChessMove) Myylex();
}
if (first.pr == NoProc) {
p = yy_text;
AppendComment(currentMove, p, FALSE);
yyboardindex = forwardMostMove;
- cm = (ChessMove) yylex();
+ cm = (ChessMove) Myylex();
}
if ((cm == EndOfFile && lastLoadGameStart != EndOfFile ) ||
EditTagsEvent()
{
char *tags = PGNTags(&gameInfo);
- EditTagsPopUp(tags);
+ EditTagsPopUp(tags, NULL);
free(tags);
}
}
void
+SettingsMenuIfReady()
+{
+ if (second.lastPing != second.lastPong) {
+ DisplayMessage("", _("Waiting for second chess program"));
+ ScheduleDelayedEvent(SettingsMenuIfReady, 10); // [HGM] fast: lowered from 1000
+ return;
+ }
+ ThawUI();
+ DisplayMessage("", "");
+ SettingsPopUp(&second);
+}
+
+int
+WaitForEngine(ChessProgramState *cps, DelayedEventCallback retry)
+{
+ char buf[MSG_SIZ];
+ if (cps->pr == NULL) {
+ StartChessProgram(cps);
+ if (cps->protocolVersion == 1) {
+ retry();
+ } else {
+ /* kludge: allow timeout for initial "feature" command */
+ FreezeUI();
+ snprintf(buf, MSG_SIZ, _("Starting %s chess program"), cps->which);
+ DisplayMessage("", buf);
+ ScheduleDelayedEvent(retry, FEATURE_TIMEOUT);
+ }
+ return 1;
+ }
+ return 0;
+}
+
+void
TwoMachinesEvent P((void))
{
int i;
char buf[MSG_SIZ];
ChessProgramState *onmove;
char *bookHit = NULL;
+ static int stalling = 0;
if (appData.noChessProgram) return;
TruncateGame(); // [HGM] vari: MachineWhite and MachineBlack do this...
ResurrectChessProgram(); /* in case first program isn't running */
- if (second.pr == NULL) {
- StartChessProgram(&second);
- if (second.protocolVersion == 1) {
- TwoMachinesEventIfReady();
- } else {
- /* kludge: allow timeout for initial "feature" command */
- FreezeUI();
- DisplayMessage("", _("Starting second chess program"));
- ScheduleDelayedEvent(TwoMachinesEventIfReady, FEATURE_TIMEOUT);
- }
- return;
+ if(WaitForEngine(&second, TwoMachinesEventIfReady)) return;
+ if(first.lastPing != first.lastPong) { // [HGM] wait till we are sure first engine has set up position
+ DisplayMessage("", _("Waiting for first chess program"));
+ ScheduleDelayedEvent(TwoMachinesEvent, 10);
+ return;
+ }
+ if(!stalling) {
+ InitChessProgram(&second, FALSE);
+ SendToProgram("force\n", &second);
+ }
+ if(second.lastPing != second.lastPong) { // [HGM] second engine might have to reallocate hash
+ if(!stalling) DisplayMessage("", _("Waiting for second chess program"));
+ stalling = 1;
+ ScheduleDelayedEvent(TwoMachinesEvent, 10);
+ return;
}
+ stalling = 0;
DisplayMessage("", "");
- InitChessProgram(&second, FALSE);
- SendToProgram("force\n", &second);
if (startedFromSetupPosition) {
SendBoard(&second, backwardMostMove);
if (appData.debugMode) {
}
void
+ClockClick(int which)
+{ // [HGM] code moved to back-end from winboard.c
+ if(which) { // black clock
+ if (gameMode == EditPosition || gameMode == IcsExamining) {
+ if(!appData.pieceMenu && blackPlaysFirst) EditPositionMenuEvent(ClearBoard, 0, 0);
+ SetBlackToPlayEvent();
+ } else if (gameMode == EditGame || shiftKey) {
+ AdjustClock(which, -1);
+ } else if (gameMode == IcsPlayingWhite ||
+ gameMode == MachinePlaysBlack) {
+ CallFlagEvent();
+ }
+ } else { // white clock
+ if (gameMode == EditPosition || gameMode == IcsExamining) {
+ if(!appData.pieceMenu && !blackPlaysFirst) EditPositionMenuEvent(ClearBoard, 0, 0);
+ SetWhiteToPlayEvent();
+ } else if (gameMode == EditGame || shiftKey) {
+ AdjustClock(which, -1);
+ } else if (gameMode == IcsPlayingBlack ||
+ gameMode == MachinePlaysWhite) {
+ CallFlagEvent();
+ }
+ }
+}
+
+void
DrawEvent()
{
/* Offer draw or accept pending draw offer from opponent */
char *text;
{
int len;
+ char *p;
+ float score;
+ if(index && sscanf(text, "%f/%d", &score, &len) == 2 &&
+ pvInfoList[index-1].depth == len &&
+ fabs(pvInfoList[index-1].score - score*100.) < 0.5 &&
+ (p = strchr(text, '\n'))) text = p; // [HGM] strip off first line with PV info, if any
while (*text == '\n') text++;
len = strlen(text);
while (len > 0 && text[len - 1] == '\n') len--;
while(commentList[index][oldlen-1] == '\n')
commentList[index][--oldlen] = NULLCHAR;
commentList[index] = (char *) malloc(oldlen + len + 6); // might waste 4
- safeStrCpy(commentList[index], old, oldlen);
+ safeStrCpy(commentList[index], old, oldlen + len + 6);
free(old);
// [HGM] braces: join "{A\n}\n" + "{\nB}" as "{A\nB\n}"
- if(commentList[index][oldlen-1] == '}' && (text[0] == '{' || addBraces)) {
- if(addBraces) addBraces = FALSE; else { text++; len--; }
+ if(commentList[index][oldlen-1] == '}' && (text[0] == '{' || addBraces == TRUE)) {
+ if(addBraces == TRUE) addBraces = FALSE; else { text++; len--; }
while (*text == '\n') { text++; len--; }
commentList[index][--oldlen] = NULLCHAR;
}
- if(addBraces) strcat(commentList[index], "\n{\n");
+ if(addBraces) strcat(commentList[index], addBraces == 2 ? "\n(" : "\n{\n");
else strcat(commentList[index], "\n");
strcat(commentList[index], text);
- if(addBraces) strcat(commentList[index], "\n}\n");
+ if(addBraces) strcat(commentList[index], addBraces == 2 ? ")\n" : "\n}\n");
else strcat(commentList[index], "\n");
} else {
commentList[index] = (char *) malloc(len + 6); // perhaps wastes 4...
if(addBraces)
- safeStrCpy(commentList[index], "{\n", sizeof(commentList[index])/sizeof(commentList[index][0]));
+ safeStrCpy(commentList[index], addBraces == 2 ? "(" : "{\n", 3);
else commentList[index][0] = NULLCHAR;
strcat(commentList[index], text);
- strcat(commentList[index], "\n");
- if(addBraces) strcat(commentList[index], "}\n");
+ strcat(commentList[index], addBraces == 2 ? ")\n" : "\n");
+ if(addBraces == TRUE) strcat(commentList[index], "}\n");
}
}
/* [HGM] PV time: and then remove it, to prevent it appearing twice */
char *GetInfoFromComment( int index, char * text )
{
- char * sep = text;
+ char * sep = text, *p;
if( text != NULL && index > 0 ) {
int score = 0;
return text;
}
+ p = text;
+ if(p[1] == '(') { // comment starts with PV
+ p = strchr(p, ')'); // locate end of PV
+ if(p == NULL || sep < p+5) return text;
+ // at this point we have something like "{(.*) +0.23/6 ..."
+ p = text; while(*++p != ')') p[-1] = *p; p[-1] = ')';
+ *p = '\n'; while(*p == ' ' || *p == '\n') p++; *--p = '{';
+ // we now moved the brace to behind the PV: "(.*) {+0.23/6 ..."
+ }
time = -1; sec = -1; deci = -1;
- if( sscanf( text+1, "%d.%d/%d %d:%d", &score, &score_lo, &depth, &time, &sec ) != 5 &&
- sscanf( text+1, "%d.%d/%d %d.%d", &score, &score_lo, &depth, &time, &deci ) != 5 &&
- sscanf( text+1, "%d.%d/%d %d", &score, &score_lo, &depth, &time ) != 4 &&
- sscanf( text+1, "%d.%d/%d", &score, &score_lo, &depth ) != 3 ) {
+ if( sscanf( p+1, "%d.%d/%d %d:%d", &score, &score_lo, &depth, &time, &sec ) != 5 &&
+ sscanf( p+1, "%d.%d/%d %d.%d", &score, &score_lo, &depth, &time, &deci ) != 5 &&
+ sscanf( p+1, "%d.%d/%d %d", &score, &score_lo, &depth, &time ) != 4 &&
+ sscanf( p+1, "%d.%d/%d", &score, &score_lo, &depth ) != 3 ) {
return text;
}
/* [HGM] PV time: now locate end of PV info */
while( *++sep >= '0' && *sep <= '9'); // strip depth
if(time >= 0)
- while( *++sep >= '0' && *sep <= '9'); // strip time
+ while( *++sep >= '0' && *sep <= '9' || *sep == '\n'); // strip time
if(sec >= 0)
while( *++sep >= '0' && *sep <= '9'); // strip seconds
if(deci >= 0)
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
}
return sep;
}
outCount = OutputToProcess(cps->pr, message, count, &error);
if (outCount < count && !exiting
&& !endingGame) { /* [HGM] crash: to not hang GameEnds() writing to deceased engines */
- snprintf(buf, MSG_SIZ, _("Error writing to %s chess program"), cps->which);
+ snprintf(buf, MSG_SIZ, _("Error writing to %s chess program"), _(cps->which));
if(gameInfo.resultDetails==NULL) { /* [HGM] crash: if game in progress, give reason for abort */
if((signed char)boards[forwardMostMove][EP_STATUS] <= EP_DRAWS) {
gameInfo.result = GameIsDrawn; /* [HGM] accept exit as draw claim */
- snprintf(buf, MSG_SIZ, "%s program exits in draw position (%s)", cps->which, cps->program);
+ snprintf(buf, MSG_SIZ, _("%s program exits in draw position (%s)"), _(cps->which), cps->program);
} else {
gameInfo.result = cps->twoMachinesColor[0]=='w' ? BlackWins : WhiteWins;
}
if (count <= 0) {
if (count == 0) {
snprintf(buf, MSG_SIZ, _("Error: %s chess program (%s) exited unexpectedly"),
- cps->which, cps->program);
+ _(cps->which), cps->program);
if(gameInfo.resultDetails==NULL) { /* [HGM] crash: if game in progress, give reason for abort */
if((signed char)boards[forwardMostMove][EP_STATUS] <= EP_DRAWS) {
gameInfo.result = GameIsDrawn; /* [HGM] accept exit as draw claim */
- snprintf(buf, MSG_SIZ, _("%s program exits in draw position (%s)"), cps->which, cps->program);
+ snprintf(buf, MSG_SIZ, _("%s program exits in draw position (%s)"), _(cps->which), cps->program);
} else {
gameInfo.result = cps->twoMachinesColor[0]=='w' ? BlackWins : WhiteWins;
}
if(!cps->userError || !appData.popupExitMessage) DisplayFatalError(buf, 0, 1); else errorExitStatus = 1;
} else {
snprintf(buf, MSG_SIZ, _("Error reading from %s chess program (%s)"),
- cps->which, cps->program);
+ _(cps->which), cps->program);
RemoveInputSource(cps->isr);
/* [AS] Program is misbehaving badly... kill it */
sscanf(message, "error %c", &c)!=1 && sscanf(message, "illegal %c", &c)!=1 &&
sscanf(message, "tell%c", &c)!=1 && sscanf(message, "0-1 %c", &c)!=1 &&
sscanf(message, "1-0 %c", &c)!=1 && sscanf(message, "1/2-1/2 %c", &c)!=1 &&
+ sscanf(message, "setboard %c", &c)!=1 && sscanf(message, "setup %c", &c)!=1 &&
+ sscanf(message, "hint: %c", &c)!=1 &&
sscanf(message, "pong %c", &c)!=1 && start != '#') {
quote = appData.engineComments == 2 ? "# " : "### NON-COMPLIANT! ### ";
print = (appData.engineComments >= 2);
SendToProgram(buf, cps);
}
- if(cps->nps > 0) { /* [HGM] nps */
+ if(cps->nps >= 0) { /* [HGM] nps */
if(cps->supportsNPS == FALSE)
cps->nps = -1; // don't use if engine explicitly says not supported!
else {
} else if((p = strstr(opt->name, " -file "))) {
// for now -file is a synonym for -string, to already provide compatibility with future polyglots
opt->textValue = p+7;
- opt->type = TextBox; // FileName;
+ opt->type = FileName; // FileName;
} else if((p = strstr(opt->name, " -path "))) {
// for now -file is a synonym for -string, to already provide compatibility with future polyglots
opt->textValue = p+7;
- opt->type = TextBox; // PathName;
+ opt->type = PathName; // PathName;
} else if(p = strstr(opt->name, " -check ")) {
if(sscanf(p, " -check %d", &def) < 1) return FALSE;
opt->value = (def != 0);
if(p && (p == cps->optionSettings || p[-1] == ',')) {
snprintf(buf, MSG_SIZ, "option %s", p);
if(p = strstr(buf, ",")) *p = 0;
+ if(q = strchr(buf, '=')) switch(opt->type) {
+ case ComboBox:
+ for(n=0; n<opt->max; n++)
+ if(!strcmp(((char**)opt->textValue)[n], q+1)) opt->value = n;
+ break;
+ case TextBox:
+ safeStrCpy(opt->textValue, q+1, MSG_SIZ - (opt->textValue - opt->name));
+ break;
+ case Spin:
+ case CheckBox:
+ opt->value = atoi(q+1);
+ default:
+ break;
+ }
strcat(buf, "\n");
SendToProgram(buf, cps);
}
{
DelayedEventCallback cb = GetDelayedEvent();
if ((cb == InitBackEnd3 && cps == &first) ||
+ (cb == SettingsMenuIfReady && cps == &second) ||
+ (cb == LoadEngine) ||
(cb == TwoMachinesEventIfReady && cps == &second)) {
CancelDelayedEvent();
ScheduleDelayedEvent(cb, val ? 1 : 3600000);
}
if(cps->nrOptions >= MAX_OPTIONS) {
cps->nrOptions--;
- snprintf(buf, MSG_SIZ, "%s engine has too many options\n", cps->which);
+ snprintf(buf, MSG_SIZ, _("%s engine has too many options\n"), _(cps->which));
DisplayError(buf, 0);
}
continue;
}
void
+TypeInEvent(char firstChar)
+{
+ if ((gameMode == BeginningOfGame && !appData.icsActive) || \r
+ gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||\r
+ gameMode == AnalyzeMode || gameMode == EditGame || \r
+ gameMode == EditPosition || gameMode == IcsExamining ||\r
+ gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
+ isdigit(firstChar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes\r
+ ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||\r
+ gameMode == IcsObserving || gameMode == TwoMachinesPlay ) ||\r
+ gameMode == Training) PopUpMoveDialog(firstChar);
+}
+
+void
+TypeInDoneEvent(char *move)
+{
+ Board board;
+ int n, fromX, fromY, toX, toY;
+ char promoChar;
+ ChessMove moveType;\r
+
+ // [HGM] FENedit\r
+ if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {\r
+ EditPositionPasteFEN(move);\r
+ return;\r
+ }\r
+ // [HGM] movenum: allow move number to be typed in any mode\r
+ if(sscanf(move, "%d", &n) == 1 && n != 0 ) {\r
+ ToNrEvent(2*n-1);\r
+ return;\r
+ }\r
+
+ if (gameMode != EditGame && currentMove != forwardMostMove && \r
+ gameMode != Training) {\r
+ DisplayMoveError(_("Displayed move is not current"));\r
+ } else {\r
+ int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
+ &moveType, &fromX, &fromY, &toX, &toY, &promoChar);\r
+ if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized\r
+ if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
+ &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {\r
+ UserMoveEvent(fromX, fromY, toX, toY, promoChar); \r
+ } else {\r
+ DisplayMoveError(_("Could not parse move"));\r
+ }
+ }\r
+}\r
+
+void
DisplayMove(moveNumber)
int moveNumber;
{
if(blackNPS >= 0) lastTickLength = 0;
blackTimeRemaining -= lastTickLength;
/* [HGM] PGNtime: save time for PGN file if engine did not give it */
-// if(pvInfoList[forwardMostMove-1].time == -1)
- pvInfoList[forwardMostMove-1].time = // use GUI time
+// if(pvInfoList[forwardMostMove].time == -1)
+ pvInfoList[forwardMostMove].time = // use GUI time
(timeRemaining[1][forwardMostMove-1] - blackTimeRemaining)/10;
} else {
if(whiteNPS >= 0) lastTickLength = 0;
whiteTimeRemaining -= lastTickLength;
/* [HGM] PGNtime: save time for PGN file if engine did not give it */
-// if(pvInfoList[forwardMostMove-1].time == -1)
- pvInfoList[forwardMostMove-1].time =
+// if(pvInfoList[forwardMostMove].time == -1)
+ pvInfoList[forwardMostMove].time =
(timeRemaining[0][forwardMostMove-1] - whiteTimeRemaining)/10;
}
flagged = CheckFlags();