void InitDrawingSizes(int x, int y);
void NextMatchGame P((void));
int NextTourneyGame P((int nr, int *swap));
+int Pairing P((int nr, int nPlayers, int *w, int *b, int *sync));
#ifdef WIN32
extern void ConsoleCreate();
void ics_update_width P((int new_width));
extern char installDir[MSG_SIZ];
VariantClass startVariant; /* [HGM] nicks: initial variant */
+Boolean abortMatch;
extern int tinyLayout, smallLayout;
ChessProgramStats programStats;
void PushTail P((int firstMove, int lastMove));
Boolean PopTail P((Boolean annotate));
+void PushInner P((int firstMove, int lastMove));
+void PopInner P((Boolean annotate));
void CleanupTail P((void));
ChessSquare FIDEArray[2][BOARD_FILES] = {
LoadEngine();
}
-extern char *engineName, *engineDir, *engineChoice, *engineLine, *nickName;
-extern Boolean isUCI, hasBook, storeVariant, v1, addToList;
+extern char *engineName, *engineDir, *engineChoice, *engineLine, *nickName, *params;
+extern Boolean isUCI, hasBook, storeVariant, v1, addToList, useNick;
-void Load(ChessProgramState *cps, int i)
+static char resetOptions[] =
+ "-reuse -firstIsUCI false -firstHasOwnBookUCI true -firstTimeOdds 1 "
+ "-firstOptions \"\" -firstNPS -1 -fn \"\"";
+
+void
+Load(ChessProgramState *cps, int i)
{
- char *p, *q, buf[MSG_SIZ];
+ char *p, *q, buf[MSG_SIZ], command[MSG_SIZ];
if(engineLine[0]) { // an engine was selected from the combo box
snprintf(buf, MSG_SIZ, "-fcp %s", engineLine);
SwapEngines(i); // kludge to parse -f* / -first* like it is -s* / -second*
- ParseArgsFromString("-firstIsUCI false -firstHasOwnBookUCI true -firstTimeOdds 1");
+ ParseArgsFromString(resetOptions); appData.fenOverride[0] = NULL;
ParseArgsFromString(buf);
SwapEngines(i);
ReplaceEngine(cps, i);
}
p = engineName;
while(q = strchr(p, SLASH)) p = q+1;
- if(*p== NULLCHAR) return;
- appData.chessProgram[i] = strdup(p);
+ if(*p== NULLCHAR) { DisplayError(_("You did not specify the engine executable"), 0); return; }
if(engineDir[0] != NULLCHAR)
appData.directory[i] = engineDir;
else if(p != engineName) { // derive directory from engine path, when not given
p[-1] = 0;
appData.directory[i] = strdup(engineName);
- p[-1] = '/';
+ p[-1] = SLASH;
} else appData.directory[i] = ".";
+ if(params[0]) {
+ snprintf(command, MSG_SIZ, "%s %s", p, params);
+ p = command;
+ }
+ appData.chessProgram[i] = strdup(p);
appData.isUCI[i] = isUCI;
appData.protocolVersion[i] = v1 ? 1 : PROTOVER;
appData.hasOwnBookUCI[i] = hasBook;
+ if(!nickName[0]) useNick = FALSE;
+ if(useNick) ASSIGN(appData.pgnName[i], nickName);
if(addToList) {
int len;
q = firstChessProgramNames;
if(nickName[0]) snprintf(buf, MSG_SIZ, "\"%s\" -fcp ", nickName); else buf[0] = NULLCHAR;
- snprintf(buf+strlen(buf), MSG_SIZ-strlen(buf), "\"%s\" -fd \"%s\"%s%s%s%s%s\n", p, appData.directory[i],
+ snprintf(buf+strlen(buf), MSG_SIZ-strlen(buf), "\"%s\" -fd \"%s\"%s%s%s%s%s%s%s%s\n", p, appData.directory[i],
+ useNick ? " -fn \"" : "",
+ useNick ? nickName : "",
+ useNick ? "\"" : "",
v1 ? " -firstProtocolVersion 1" : "",
hasBook ? "" : " -fNoOwnBookUCI",
isUCI ? " -fUCI" : "",
storeVariant ? " -variant " : "",
storeVariant ? VariantName(gameInfo.variant) : "");
-fprintf(debugFP, "new line: %s", buf);
firstChessProgramNames = malloc(len = strlen(q) + strlen(buf) + 1);
snprintf(firstChessProgramNames, len, "%s%s", q, buf);
if(q) free(q);
}
void
-InitBackEnd1()
+InitTimeControls()
{
int matched, min, sec;
+ /*
+ * Parse timeControl resource
+ */
+ if (!ParseTimeControl(appData.timeControl, appData.timeIncrement,
+ appData.movesPerSession)) {
+ char buf[MSG_SIZ];
+ snprintf(buf, sizeof(buf), _("bad timeControl option %s"), appData.timeControl);
+ DisplayFatalError(buf, 0, 2);
+ }
+
+ /*
+ * Parse searchTime resource
+ */
+ if (*appData.searchTime != NULLCHAR) {
+ matched = sscanf(appData.searchTime, "%d:%d", &min, &sec);
+ if (matched == 1) {
+ searchTime = min * 60;
+ } else if (matched == 2) {
+ searchTime = min * 60 + sec;
+ } else {
+ char buf[MSG_SIZ];
+ snprintf(buf, sizeof(buf), _("bad searchTime option %s"), appData.searchTime);
+ DisplayFatalError(buf, 0, 2);
+ }
+ }
+}
+
+void
+InitBackEnd1()
+{
ShowThinkingEvent(); // [HGM] thinking: make sure post/nopost state is set according to options
startVariant = StringToVariant(appData.variant); // [HGM] nicks: remember original variant
}
}
- /*
- * Parse timeControl resource
- */
- if (!ParseTimeControl(appData.timeControl, appData.timeIncrement,
- appData.movesPerSession)) {
- char buf[MSG_SIZ];
- snprintf(buf, sizeof(buf), _("bad timeControl option %s"), appData.timeControl);
- DisplayFatalError(buf, 0, 2);
- }
-
- /*
- * Parse searchTime resource
- */
- if (*appData.searchTime != NULLCHAR) {
- matched = sscanf(appData.searchTime, "%d:%d", &min, &sec);
- if (matched == 1) {
- searchTime = min * 60;
- } else if (matched == 2) {
- searchTime = min * 60 + sec;
- } else {
- char buf[MSG_SIZ];
- snprintf(buf, sizeof(buf), _("bad searchTime option %s"), appData.searchTime);
- DisplayFatalError(buf, 0, 2);
- }
- }
+ InitTimeControls();
/* [AS] Adjudication threshold */
adjudicateLossThreshold = appData.adjudicateLossThreshold;
MatchEvent(int mode)
{ // [HGM] moved out of InitBackend3, to make it callable when match starts through menu
int dummy;
+ if(matchMode) { // already in match mode: switch it off
+ abortMatch = TRUE;
+ appData.matchGames = appData.tourneyFile[0] ? nextGame: matchGame; // kludge to let match terminate after next game.
+ ModeHighlight(); // kludgey way to remove checkmark...
+ return;
+ }
+// if(gameMode != BeginningOfGame) {
+// DisplayError(_("You can only start a match from the initial position."), 0);
+// return;
+// }
+ abortMatch = FALSE;
+ appData.matchGames = appData.defaultMatchGames;
/* Set up machine vs. machine match */
nextGame = 0;
NextTourneyGame(0, &dummy); // sets appData.matchGames if this is tourney, to make sure ReserveGame knows it
}
}
+Boolean pushed = FALSE;
void
-ParsePV(char *pv, Boolean storeComments)
+ParsePV(char *pv, Boolean storeComments, Boolean atEnd)
{ // Parse a string of PV moves, and append to current game, behind forwardMostMove
int fromX, fromY, toX, toY; char promoChar;
ChessMove moveType;
Boolean valid;
int nr = 0;
+ if (gameMode == AnalyzeMode && currentMove < forwardMostMove) {
+ PushInner(currentMove, forwardMostMove); // [HGM] engine might not be thinking on forwardMost position!
+ pushed = TRUE;
+ }
endPV = forwardMostMove;
do {
while(*pv == ' ' || *pv == '\n' || *pv == '\t') pv++; // must still read away whitespace
moveList[endPV-1][3] = toY + ONE;
moveList[endPV-1][4] = promoChar;
moveList[endPV-1][5] = NULLCHAR;
+ CoordsToComputerAlgebraic(fromY, fromX, toY, toX, promoChar, moveList[endPV - 1]);
strncat(moveList[endPV-1], "\n", MOVE_LEN);
- if(storeComments)
- CoordsToAlgebraic(boards[endPV - 1],
+ CoordsToAlgebraic(boards[endPV - 1],
PosFlags(endPV - 1),
fromY, fromX, toY, toX, promoChar,
parseList[endPV - 1]);
- else
- parseList[endPV-1][0] = NULLCHAR;
} while(valid);
- currentMove = endPV;
+ currentMove = (atEnd || endPV == forwardMostMove) ? endPV : forwardMostMove + 1;
if(currentMove == forwardMostMove) ClearPremoveHighlights(); else
SetPremoveHighlights(moveList[currentMove-1][0]-AAA, moveList[currentMove-1][1]-ONE,
moveList[currentMove-1][2]-AAA, moveList[currentMove-1][3]-ONE);
DrawPosition(TRUE, boards[currentMove]);
}
+int
+MultiPV(ChessProgramState *cps)
+{ // check if engine supports MultiPV, and if so, return the nmber of the option that sets it
+ int i;
+ for(i=0; i<cps->nrOptions; i++)
+ if(!strcmp(cps->option[i].name, "MultiPV") && cps->option[i].type == Spin)
+ return i;
+ return -1;
+}
+
Boolean
LoadMultiPV(int x, int y, char *buf, int index, int *start, int *end)
{
- int startPV;
- char *p;
+ int startPV, multi, lineStart, origIndex = index;
+ char *p, buf2[MSG_SIZ];
if(index < 0 || index >= strlen(buf)) return FALSE; // sanity
lastX = x; lastY = y;
while(index > 0 && buf[index-1] != '\n') index--; // beginning of line
- startPV = index;
+ lineStart = startPV = index;
while(buf[index] != '\n') if(buf[index++] == '\t') startPV = index;
if(index == startPV && (p = StrCaseStr(buf+index, "PV="))) startPV = p - buf + 3;
index = startPV;
do{ while(buf[index] && buf[index] != '\n') index++;
} while(buf[index] == '\n' && buf[index+1] == '\\' && buf[index+2] == ' ' && index++); // join kibitzed PV continuation line
buf[index] = 0;
- ParsePV(buf+startPV, FALSE);
+ if(lineStart == 0 && gameMode == AnalyzeMode && (multi = MultiPV(&first)) >= 0) {
+ int n = first.option[multi].value;
+ if(origIndex < 10) { if(n>1) n--; } else if(origIndex > index - 6) n++;
+ snprintf(buf2, MSG_SIZ, "option MultiPV=%d\n", n);
+ if(first.option[multi].value != n) SendToProgram(buf2, &first);
+ first.option[multi].value = n;
+ *start = *end = 0;
+ return TRUE;
+ }
+ ParsePV(buf+startPV, FALSE, !shiftKey);
*start = startPV; *end = index-1;
return TRUE;
}
{ // called on right mouse click to load PV
int which = gameMode == TwoMachinesPlay && (WhiteOnMove(forwardMostMove) == (second.twoMachinesColor[0] == 'w'));
lastX = x; lastY = y;
- ParsePV(lastPV[which], FALSE); // load the PV of the thinking engine in the boards array.
+ ParsePV(lastPV[which], FALSE, TRUE); // load the PV of the thinking engine in the boards array.
return TRUE;
}
void
UnLoadPV()
{
+ int oldFMM = forwardMostMove; // N.B.: this was currentMove before PV was loaded!
if(endPV < 0) return;
endPV = -1;
+ if(shiftKey && gameMode == AnalyzeMode) {
+ if(pushed) storedGames--; // abandon shelved tail of original game
+ pushed = FALSE;
+ forwardMostMove = currentMove;
+ currentMove = oldFMM;
+ ToNrEvent(forwardMostMove);
+ }
currentMove = forwardMostMove;
+ if(pushed) { PopInner(0); pushed = FALSE; } // restore shelved game contnuation
ClearPremoveHighlights();
DrawPosition(TRUE, boards[currentMove]);
}
SetProgramStats( &stats );
}
+#define MAXPLAYERS 500
+
+char *
+TourneyStandings(int display)
+{
+ int i, w, b, color, wScore, bScore, dummy, nr=0, nPlayers=0;
+ int score[MAXPLAYERS], ranking[MAXPLAYERS], points[MAXPLAYERS], games[MAXPLAYERS];
+ char result, *p, *names[MAXPLAYERS];
+
+ names[0] = p = strdup(appData.participants);
+ while(p = strchr(p, '\n')) *p++ = NULLCHAR, names[++nPlayers] = p; // count participants
+
+ for(i=0; i<nPlayers; i++) score[i] = games[i] = 0;
+
+ while(result = appData.results[nr]) {
+ color = Pairing(nr, nPlayers, &w, &b, &dummy);
+ if(!(color ^ matchGame & 1)) { dummy = w; w = b; b = dummy; }
+ wScore = bScore = 0;
+ switch(result) {
+ case '+': wScore = 2; break;
+ case '-': bScore = 2; break;
+ case '=': wScore = bScore = 1; break;
+ case ' ':
+ case '*': return strdup("busy"); // tourney not finished
+ }
+ score[w] += wScore;
+ score[b] += bScore;
+ games[w]++;
+ games[b]++;
+ nr++;
+ }
+ if(appData.tourneyType > 0) nPlayers = appData.tourneyType; // in gauntlet, list only gauntlet engine(s)
+ for(w=0; w<nPlayers; w++) {
+ bScore = -1;
+ for(i=0; i<nPlayers; i++) if(score[i] > bScore) bScore = score[i], b = i;
+ ranking[w] = b; points[w] = bScore; score[b] = -2;
+ }
+ p = malloc(nPlayers*34+1);
+ for(w=0; w<nPlayers && w<display; w++)
+ sprintf(p+34*w, "%2d. %5.1f/%-3d %-19.19s\n", w+1, points[w]/2., games[ranking[w]], names[ranking[w]]);
+ free(names[0]);
+ return p;
+}
+
void
Count(Board board, int pCnt[], int *nW, int *nB, int *wStale, int *bStale, int *bishopColor)
{ // count all piece types
DrawPosition(FALSE, boards[currentMove]);
DisplayBothClocks();
HistorySet(parseList,backwardMostMove,forwardMostMove,currentMove-1);
+ DisplayBook(currentMove);
}
void SendEgtPath(ChessProgramState *cps)
fprintf(f, "-loadPositionFile \"%s\"\n", appData.loadPositionFile);
fprintf(f, "-loadPositionIndex %d\n", appData.loadPositionIndex);
fprintf(f, "-rewindIndex %d\n", appData.rewindIndex);
+ if(searchTime > 0)
+ fprintf(f, "-searchTime \"%s\"\n", appData.searchTime);
+ else {
+ fprintf(f, "-mps %d\n", appData.movesPerSession);
+ fprintf(f, "-tc %s\n", appData.timeControl);
+ fprintf(f, "-inc %.2f\n", appData.timeIncrement);
+ }
fprintf(f, "-results \"\"\n");
}
}
SWAP(scoreIsAbsolute, h)
SWAP(timeOdds, h)
SWAP(logo, p)
+ SWAP(pgnName, p)
}
void
{ // [HGM] find the engine line of the partcipant given by number, and parse its options.
int i;
char buf[MSG_SIZ], *engineName, *p = appData.participants;
- static char resetOptions[] = "-reuse -firstIsUCI false -firstHasOwnBookUCI true -firstTimeOdds 1 -firstOptions \"\" "
- "-firstNeedsNoncompliantFEN false -firstNPS -1";
for(i=0; i<player; i++) p = strchr(p, '\n') + 1;
engineName = strdup(p); if(p = strchr(engineName, '\n')) *p = NULLCHAR;
for(i=1; command[i]; i++) if(!strcmp(mnemonic[i], engineName)) break;
if(mnemonic[i]) {
snprintf(buf, MSG_SIZ, "-fcp %s", command[i]);
- ParseArgsFromString(resetOptions);
+ ParseArgsFromString(resetOptions); appData.fenOverride[0] = NULL;
ParseArgsFromString(buf);
}
free(engineName);
tf = fopen(appData.tourneyFile, "r");
if(tf == NULL) { DisplayFatalError(_("Bad tournament file"), 0, 1); return 0; }
ParseArgsFromFile(tf); fclose(tf);
+ InitTimeControls(); // TC might be altered from tourney file
p = appData.participants;
while(p = strchr(p, '\n')) p++, nPlayers++; // count participants
{
GameMode nextGameMode;
int isIcsGame;
- char buf[MSG_SIZ], popupRequested = 0, forceUnload;
+ char buf[MSG_SIZ], popupRequested = 0, *ranking = NULL;
if(endingGame) return; /* [HGM] crash: forbid recursion */
endingGame = 1;
}
if(waitingForGame) resChar = ' '; // quit while waiting for round sync: unreserve already reserved game
- if(appData.tourneyFile[0]){ // [HGM] we are in a tourney; update tourney file with game result
+ if(appData.tourneyFile[0] && !abortMatch){ // [HGM] we are in a tourney; update tourney file with game result
ReserveGame(nextGame, resChar); // sets nextGame
- if(nextGame > appData.matchGames) appData.tourneyFile[0] = 0; // tourney is done
+ if(nextGame > appData.matchGames) appData.tourneyFile[0] = 0, ranking = TourneyStandings(3); // tourney is done
} else roundNr = nextGame = matchGame + 1; // normal match, just increment; round equals matchGame
- if (nextGame <= appData.matchGames) {
+ if (nextGame <= appData.matchGames && !abortMatch) {
gameMode = nextGameMode;
matchGame = nextGame; // this will be overruled in tourney mode!
GetTimeMark(&pauseStart); // [HGM] matchpause: stipulate a pause
ModeHighlight();
endingGame = 0; /* [HGM] crash */
if(popupRequested) { // [HGM] crash: this calls GameEnds recursively through ExitEvent! Make it a harmless tail recursion.
- if(matchMode == TRUE) DisplayFatalError(buf, 0, 0); else {
- matchMode = FALSE; appData.matchGames = matchGame = roundNr = 0;
- DisplayNote(buf);
+ if(matchMode == TRUE) { // match through command line: exit with or without popup
+ if(ranking) {
+ if(strcmp(ranking, "busy")) DisplayFatalError(ranking, 0, 0);
+ else ExitEvent(0);
+ } else DisplayFatalError(buf, 0, 0);
+ } else { // match through menu; just stop, with or without popup
+ matchMode = FALSE; appData.matchGames = matchGame = roundNr = 0;
+ if(ranking){
+ if(strcmp(ranking, "busy")) DisplayNote(ranking);
+ } else DisplayNote(buf);
}
+ if(ranking) free(ranking);
}
}
EditTagsEvent()
{
char *tags = PGNTags(&gameInfo);
+ bookUp = FALSE;
EditTagsPopUp(tags, NULL);
free(tags);
}
if ( !matchMode && gameMode != Training) { // [HGM] PV info: routine tests if empty
DisplayComment(currentMove - 1, commentList[currentMove]);
}
+ DisplayBook(currentMove);
}
HistorySet(parseList,backwardMostMove,forwardMostMove,currentMove-1);
// [HGM] PV info: routine tests if comment empty
DisplayComment(currentMove - 1, commentList[currentMove]);
+ DisplayBook(currentMove);
}
void
gameInfo.round = StrSave("-");
}
if (first.twoMachinesColor[0] == 'w') {
- gameInfo.white = StrSave(first.tidy);
- gameInfo.black = StrSave(second.tidy);
+ gameInfo.white = StrSave(appData.pgnName[0][0] ? appData.pgnName[0] : first.tidy);
+ gameInfo.black = StrSave(appData.pgnName[1][0] ? appData.pgnName[1] : second.tidy);
} else {
- gameInfo.white = StrSave(second.tidy);
- gameInfo.black = StrSave(first.tidy);
+ gameInfo.white = StrSave(appData.pgnName[1][0] ? appData.pgnName[1] : second.tidy);
+ gameInfo.black = StrSave(appData.pgnName[0][0] ? appData.pgnName[0] : first.tidy);
}
gameInfo.timeControl = TimeControlTagValue();
break;
// [HGM] vari: routines for shelving variations
void
-PushTail(int firstMove, int lastMove)
+PushInner(int firstMove, int lastMove)
{
int i, j, nrMoves = lastMove - firstMove;
- if(appData.icsActive) { // only in local mode
- forwardMostMove = currentMove; // mimic old ICS behavior
- return;
- }
- if(storedGames >= MAX_VARIATIONS-1) return;
-
// push current tail of game on stack
savedResult[storedGames] = gameInfo.result;
savedDetails[storedGames] = gameInfo.resultDetails;
storedGames++;
forwardMostMove = firstMove; // truncate game so we can start variation
+}
+
+void
+PushTail(int firstMove, int lastMove)
+{
+ if(appData.icsActive) { // only in local mode
+ forwardMostMove = currentMove; // mimic old ICS behavior
+ return;
+ }
+ if(storedGames >= MAX_VARIATIONS-2) return; // leave one for PV-walk
+
+ PushInner(firstMove, lastMove);
if(storedGames == 1) GreyRevert(FALSE);
}
-Boolean
-PopTail(Boolean annotate)
+void
+PopInner(Boolean annotate)
{
int i, j, nrMoves;
char buf[8000], moveBuf[20];
- if(appData.icsActive) return FALSE; // only in local mode
- if(!storedGames) return FALSE; // sanity
- CommentPopDown(); // make sure no stale variation comments to the destroyed line can remain open
-
storedGames--;
ToNrEvent(savedFirst[storedGames]); // sets currentMove
nrMoves = savedLast[storedGames] - currentMove;
}
gameInfo.resultDetails = savedDetails[storedGames];
forwardMostMove = currentMove + nrMoves;
+}
+
+Boolean
+PopTail(Boolean annotate)
+{
+ if(appData.icsActive) return FALSE; // only in local mode
+ if(!storedGames) return FALSE; // sanity
+ CommentPopDown(); // make sure no stale variation comments to the destroyed line can remain open
+
+ PopInner(annotate);
+
if(storedGames == 0) GreyRevert(TRUE);
return TRUE;
}
PushTail(currentMove, forwardMostMove); // shelve main variation. This truncates game
// kludge: use ParsePV() to append variation to game
move = currentMove;
- ParsePV(start, TRUE);
+ ParsePV(start, TRUE, TRUE);
forwardMostMove = endPV; endPV = -1; currentMove = move; // cleanup what ParsePV did
ClearPremoveHighlights();
CommentPopDown();