X-Git-Url: http://winboard.nl/cgi-bin?a=blobdiff_plain;f=backend.c;h=3f6229da29fab8839781c7a70822004c2e7b732d;hb=02de46755f727ffb565f7c855f37c344eee925ff;hp=e93176433bedaa6341849223dfdee0954a410535;hpb=a68d4f3896442273c5134c7d8d2876bb8c4d2e83;p=xboard.git diff --git a/backend.c b/backend.c index e931764..3f6229d 100644 --- a/backend.c +++ b/backend.c @@ -222,6 +222,7 @@ int NextTourneyGame P((int nr, int *swap)); int Pairing P((int nr, int nPlayers, int *w, int *b, int *sync)); FILE *WriteTourneyFile P((char *results, FILE *f)); void DisplayTwoMachinesTitle P(()); +static void ExcludeClick P((int index)); #ifdef WIN32 extern void ConsoleCreate(); @@ -392,7 +393,7 @@ PosFlags (index) return flags; } -FILE *gameFileFP, *debugFP; +FILE *gameFileFP, *debugFP, *serverFP; char *currentDebugFile; // [HGM] debug split: to remember name /* @@ -734,8 +735,12 @@ ClearOptions (ChessProgramState *cps) } char *engineNames[] = { -"first", -"second" + /* TRANSLATORS: "first" is the first of possible two chess engines. It is inserted into strings + such as "%s engine" / "%s chess program" / "%s machine" - all meaning the same thing */ +N_("first"), + /* TRANSLATORS: "second" is the second of possible two chess engines. It is inserted into strings + such as "%s engine" / "%s chess program" / "%s machine" - all meaning the same thing */ +N_("second") }; void @@ -871,7 +876,8 @@ extern Boolean isUCI, hasBook, storeVariant, v1, addToList, useNick; static char resetOptions[] = "-reuse -firstIsUCI false -firstHasOwnBookUCI true -firstTimeOdds 1 " "-firstInitString \"" INIT_STRING "\" -firstComputerString \"" COMPUTER_STRING "\" " - "-firstOptions \"\" -firstNPS -1 -fn \"\""; + "-firstFeatures \"\" -firstLogo \"\" -firstAccumulateTC 1 " + "-firstOptions \"\" -firstNPS -1 -fn \"\" -firstScoreAbs false"; void FloatToFront(char **list, char *engineLine) @@ -892,6 +898,8 @@ FloatToFront(char **list, char *engineLine) ASSIGN(*list, tidy+1); } +char *insert, *wbOptions; // point in ChessProgramNames were we should insert new engine + void Load (ChessProgramState *cps, int i) { @@ -899,7 +907,8 @@ Load (ChessProgramState *cps, int i) if(engineLine && 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(resetOptions); appData.fenOverride[0] = NULL; appData.pvSAN[0] = FALSE; + ParseArgsFromString(resetOptions); appData.pvSAN[0] = FALSE; + FREE(appData.fenOverride[0]); appData.fenOverride[0] = NULL; appData.firstProtocolVersion = PROTOVER; ParseArgsFromString(buf); SwapEngines(i); @@ -910,19 +919,20 @@ Load (ChessProgramState *cps, int i) p = engineName; while(q = strchr(p, SLASH)) p = q+1; 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 + if(engineDir[0] != NULLCHAR) { + ASSIGN(appData.directory[i], engineDir); + } else if(p != engineName) { // derive directory from engine path, when not given p[-1] = 0; - appData.directory[i] = strdup(engineName); + ASSIGN(appData.directory[i], engineName); p[-1] = SLASH; - } else appData.directory[i] = "."; + if(SLASH == '/' && p - engineName > 1) *(p -= 2) = '.'; // for XBoard use ./exeName as command after split! + } else { ASSIGN(appData.directory[i], "."); } if(params[0]) { if(strchr(p, ' ') && !strchr(p, '"')) snprintf(buf2, MSG_SIZ, "\"%s\"", p), p = buf2; // quote if it contains spaces snprintf(command, MSG_SIZ, "%s %s", p, params); p = command; } - appData.chessProgram[i] = strdup(p); + ASSIGN(appData.chessProgram[i], p); appData.isUCI[i] = isUCI; appData.protocolVersion[i] = v1 ? 1 : PROTOVER; appData.hasOwnBookUCI[i] = hasBook; @@ -944,8 +954,10 @@ Load (ChessProgramState *cps, int i) 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); - snprintf(firstChessProgramNames, len, "%s%s", q, buf); + if(insert != q) insert[-1] = NULLCHAR; + snprintf(firstChessProgramNames, len, "%s\n%s%s", q, buf, insert); if(q) free(q); FloatToFront(&appData.recentEngineList, buf); } @@ -1481,6 +1493,8 @@ MatchEvent (int mode) NextMatchGame(); } +char *comboLine = NULL; // [HGM] recent: WinBoard's first-engine combobox line + void InitBackEnd3 P((void)) { @@ -1494,7 +1508,7 @@ InitBackEnd3 P((void)) free(programVersion); programVersion = (char*) malloc(8 + strlen(PACKAGE_STRING) + strlen(first.tidy)); sprintf(programVersion, "%s + %s", PACKAGE_STRING, first.tidy); - FloatToFront(&appData.recentEngineList, appData.firstChessProgram); + FloatToFront(&appData.recentEngineList, comboLine ? comboLine : appData.firstChessProgram); } if (appData.icsActive) { @@ -2610,6 +2624,13 @@ SeekGraphClick (ClickType click, int x, int y, int moving) { static int lastDown = 0, displayed = 0, lastSecond; if(y < 0) return FALSE; + if(!(appData.seekGraph && appData.icsActive && loggedOn && + (gameMode == BeginningOfGame || gameMode == IcsIdle))) { + if(!seekGraphUp) return FALSE; + seekGraphUp = FALSE; // seek graph is up when it shouldn't be: take it down + DrawPosition(TRUE, NULL); + return TRUE; + } if(!seekGraphUp) { // initiate cration of seek graph by requesting seek-ad list if(click == Release || moving) return FALSE; nrOfSeekAds = 0; @@ -3556,7 +3577,7 @@ read_from_ics (InputSourceRef isr, VOIDSTAR closure, char *data, int count, int if (looking_at(buf, &i, "% ") || ((started == STARTED_MOVES || started == STARTED_MOVES_NOHIDE) && looking_at(buf, &i, "}*"))) { char *bookHit = NULL; // [HGM] book - if(soughtPending) { // [HGM] seekgraph: on ICC sought-list has no termination line + if(soughtPending && nrOfSeekAds) { // [HGM] seekgraph: on ICC sought-list has no termination line soughtPending = FALSE; seekGraphUp = TRUE; DrawSeekGraph(); @@ -4204,6 +4225,7 @@ ParseBoard12 (char *string) newGameMode = ((relation == RELATION_PLAYING_MYMOVE) == (to_play == 'W')) ? IcsPlayingWhite : IcsPlayingBlack; + soughtPending =FALSE; // [HGM] seekgraph: solve race condition break; case RELATION_EXAMINING: newGameMode = IcsExamining; @@ -4503,7 +4525,10 @@ ParseBoard12 (char *string) r = boards[moveNum][CASTLING][5] = initialRights[5]; } /* [HGM] e.p. rights. Assume that ICS sends file number here? */ - boards[moveNum][EP_STATUS] = double_push == -1 ? EP_NONE : double_push + BOARD_LEFT; + 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 (ics_getting_history == H_GOT_REQ_HEADER || @@ -5329,9 +5354,6 @@ ParsePV (char *pv, Boolean storeComments, Boolean atEnd) if(nr == 0 && !storeComments && *pv == '(') pv++; // first (ponder) move can be in parentheses lastParseAttempt = pv; valid = ParseOneMove(pv, endPV, &moveType, &fromX, &fromY, &toX, &toY, &promoChar); -if(appData.debugMode){ -fprintf(debugFP,"parsePV: %d %c%c%c%c yy='%s'\nPV = '%s'\n", valid, fromX+AAA, fromY+ONE, toX+AAA, toY+ONE, yy_textstr, pv); -} if(!valid && nr == 0 && ParseOneMove(pv, endPV-1, &moveType, &fromX, &fromY, &toX, &toY, &promoChar)){ nr++; moveType = Comment; // First move has been played; kludge to make sure we continue @@ -5411,6 +5433,9 @@ LoadMultiPV (int x, int y, char *buf, int index, int *start, int *end) first.option[multi].value = n; *start = *end = 0; return FALSE; + } else if(strstr(buf+lineStart, "exclude:") == buf+lineStart) { // exclude moves clicked + ExcludeClick(origIndex - lineStart); + return FALSE; } ParsePV(buf+startPV, FALSE, gameMode != AnalyzeMode); *start = startPV; *end = index-1; @@ -5450,6 +5475,7 @@ UnLoadPV () { int oldFMM = forwardMostMove; // N.B.: this was currentMove before PV was loaded! if(endPV < 0) return; + if(appData.autoCopyPV) CopyFENToClipboard(); endPV = -1; if(gameMode == AnalyzeMode && currentMove > forwardMostMove) { Boolean saveAnimate = appData.animate; @@ -6088,6 +6114,115 @@ SendBoard (ChessProgramState *cps, int moveNum) setboardSpoiledMachineBlack = 0; /* [HGM] assume WB 4.2.7 already solves this after sending setboard */ } +char exclusionHeader[MSG_SIZ]; +int exCnt, excludePtr; +typedef struct { int ff, fr, tf, tr, pc, mark; } Exclusion; +static Exclusion excluTab[200]; +static char excludeMap[(BOARD_RANKS*BOARD_FILES*BOARD_RANKS*BOARD_FILES+7)/8]; // [HGM] exclude: bitmap for excluced moves + +static void +WriteMap (int s) +{ + int j; + for(j=0; j<(BOARD_RANKS*BOARD_FILES*BOARD_RANKS*BOARD_FILES+7)/8; j++) excludeMap[j] = s; + exclusionHeader[19] = s ? '-' : '+'; // update tail state +} + +static void +ClearMap () +{ + safeStrCpy(exclusionHeader, "exclude: none best +tail \n", MSG_SIZ); + excludePtr = 24; exCnt = 0; + WriteMap(0); +} + +static void +UpdateExcludeHeader (int fromY, int fromX, int toY, int toX, char promoChar, char state) +{ // search given move in table of header moves, to know where it is listed (and add if not there), and update state + char buf[2*MOVE_LEN], *p; + Exclusion *e = excluTab; + int i; + for(i=0; i>= 3; + if(state == '*') state = (excludeMap[k] & 1< 18) { // tail + if(exclusionHeader[19] == '-') { // tail was excluded + SendToProgram("include all\n", &first); + WriteMap(0); // clear map completely + // now re-exclude selected moves + for(i=0; i index) { + char *p=exclusionHeader + excluTab[i].mark; // do trust header more than map (promotions!) + ExcludeOneMove(e[i].fr, e[i].ff, e[i].tr, e[i].tf, e[i].pc, *p == '+' ? '-' : '+'); + break; + } + } +} + ChessSquare DefaultPromoChoice (int white) { @@ -6389,12 +6524,15 @@ int lastLoadGameNumber = 0, lastLoadPositionNumber = 0; int lastLoadGameUseList = FALSE; char lastLoadGameTitle[MSG_SIZ], lastLoadPositionTitle[MSG_SIZ]; ChessMove lastLoadGameStart = EndOfFile; +int doubleClick; void UserMoveEvent(int fromX, int fromY, int toX, int toY, int promoChar) { ChessMove moveType; ChessSquare pdown, pup; + int ff=fromX, rf=fromY, ft=toX, rt=toY; + /* Check if the user is playing in turn. This is complicated because we let the user "pick up" a piece before it is his turn. So the piece he @@ -6561,6 +6699,13 @@ UserMoveEvent(int fromX, int fromY, int toX, int toY, int promoChar) } } + if(doubleClick) { // [HGM] exclude: move entered with double-click on from square is for exclusion, not playing + if(ExcludeOneMove(fromY, fromX, toY, toX, promoChar, '*')) // toggle + ClearPremoveHighlights(); // was included + else ClearHighlights(), SetPremoveHighlights(ff, rf, ft, rt); // exclusion indicated by premove highlights + return; + } + FinishMove(moveType, fromX, fromY, toX, toY, promoChar); } @@ -6632,6 +6777,8 @@ FinishMove (ChessMove moveType, int fromX, int fromY, int toX, int toY, int prom else forwardMostMove = currentMove; } + ClearMap(); + /* If we need the chess program but it's dead, restart it */ ResurrectChessProgram(); @@ -6821,12 +6968,11 @@ LeftClick (ClickType clickType, int xPix, int yPix) static int second = 0, promotionChoice = 0, clearFlag = 0; char promoChoice = NULLCHAR; ChessSquare piece; + static TimeMark lastClickTime, prevClickTime; - if(appData.seekGraph && appData.icsActive && loggedOn && - (gameMode == BeginningOfGame || gameMode == IcsIdle)) { - SeekGraphClick(clickType, xPix, yPix, 0); - return; - } + if(SeekGraphClick(clickType, xPix, yPix, 0)) return; + + prevClickTime = lastClickTime; GetTimeMark(&lastClickTime); if (clickType == Press) ErrorPopDown(); @@ -6875,7 +7021,13 @@ LeftClick (ClickType clickType, int xPix, int yPix) || x == BOARD_RGHT+1 && y >= gameInfo.holdingsSize) ) return; - if(clickType == Press && fromX == x && fromY == y && promoDefaultAltered) + if(gotPremove && x == premoveFromX && y == premoveFromY && clickType == Release) { + // could be static click on premove from-square: abort premove + gotPremove = 0; + ClearPremoveHighlights(); + } + + if(clickType == Press && fromX == x && fromY == y && promoDefaultAltered && SubtractTimeMarks(&lastClickTime, &prevClickTime) >= 200) 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 @@ -6896,6 +7048,7 @@ LeftClick (ClickType clickType, int xPix, int yPix) } return; } + doubleClick = FALSE; fromX = x; fromY = y; toX = toY = -1; if(!appData.oneClick || !OnlyMove(&x, &y, FALSE) || // even if only move, we treat as normal when this would trigger a promotion popup, to allow sweep selection @@ -6942,6 +7095,10 @@ LeftClick (ClickType clickType, int xPix, int yPix) !(fromP == BlackKing && toP == BlackRook && frc))) { /* Clicked again on same color piece -- changed his mind */ second = (x == fromX && y == fromY); + 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 || appData.oneClick && !OnlyMove(&x, &y, TRUE)) { @@ -6955,7 +7112,7 @@ LeftClick (ClickType clickType, int xPix, int yPix) (fromX == BOARD_LEFT-2 || fromX == BOARD_RGHT+1) && y == (toP < BlackPawn ? 0 : BOARD_HEIGHT-1)) gatingPiece = boards[currentMove][fromY][fromX]; - else gatingPiece = EmptySquare; + else gatingPiece = doubleClick ? fromP : EmptySquare; fromX = x; fromY = y; dragging = 1; MarkTargetSquares(0); @@ -7077,7 +7234,7 @@ LeftClick (ClickType clickType, int xPix, int yPix) // off-board moves should not be highlighted if(x < 0 || y < 0) ClearHighlights(); - if(gatingPiece != EmptySquare) promoChoice = ToLower(PieceToChar(gatingPiece)); + if(gatingPiece != EmptySquare && gameInfo.variant == VariantSChess) promoChoice = ToLower(PieceToChar(gatingPiece)); if (HasPromotionChoice(fromX, fromY, toX, toY, &promoChoice, appData.sweepSelect)) { SetHighlights(fromX, fromY, toX, toY); @@ -8346,7 +8503,7 @@ if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats. /* * If chess program startup fails, exit with an error message. - * Attempts to recover here are futile. + * Attempts to recover here are futile. [HGM] Well, we try anyway */ if ((StrStr(message, "unknown host") != NULL) || (StrStr(message, "No remote directory") != NULL) @@ -8360,7 +8517,17 @@ if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats. _(cps->which), cps->program, cps->host, message); RemoveInputSource(cps->isr); if(appData.icsActive) DisplayFatalError(buf1, 0, 1); else { - if(cps == &first) appData.noChessProgram = TRUE; + cps->isr = NULL; + DestroyChildProcess(cps->pr, 9 ); // just to be sure + cps->pr = NoProc; + if(cps == &first) { + appData.noChessProgram = TRUE; + gameMode = MachinePlaysBlack; ModeHighlight(); // kludge to unmark Machine Black menu + gameMode = BeginningOfGame; ModeHighlight(); + SetNCPMode(); + } + if(GetDelayedEvent()) CancelDelayedEvent(), ThawUI(); // [HGM] cancel remaining loading effort scheduled after feature timeout + DisplayMessage("", ""); // erase waiting message DisplayError(buf1, 0); } return; @@ -9320,10 +9487,13 @@ ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board) 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) */ + /* [HGM] Shogi-style promotions, to piece implied by original (Might overwrite ordinary Pawn promotion) */ board[toY][toX] = (ChessSquare) (PROMOTED piece); } else if(!appData.testLegality && promoChar != NULLCHAR && promoChar != '=') { // without legality testing, unconditionally believe promoChar - board[toY][toX] = CharToPiece(piece < BlackPawn ? ToUpper(promoChar) : ToLower(promoChar)); + ChessSquare newPiece = CharToPiece(piece < BlackPawn ? ToUpper(promoChar) : ToLower(promoChar)); + if((newPiece <= WhiteMan || newPiece >= BlackPawn && newPiece <= BlackMan) // unpromoted piece specified + && pieceToChar[PROMOTED newPiece] == '~') newPiece = PROMOTED newPiece; // but promoted version available + board[toY][toX] = newPiece; } if((gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat || gameInfo.variant == VariantGrand) && promoChar != NULLCHAR && gameInfo.holdingsSize) { @@ -9860,6 +10030,7 @@ NamesToList (char *names, char **engineList, char **engineMnemonic, char *group) { char buf[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 for(;*names && depth >= 0 && i < MAXENGINES-1; names = p) { p = names; q = buf; header = 0; @@ -9867,7 +10038,7 @@ NamesToList (char *names, char **engineList, char **engineMnemonic, char *group) *q = 0; if(*p == '\n') p++; if(buf[0] == '#') { - if(strstr(buf, "# end") == buf) { depth--; continue; } // leave group, and suppress printing label + if(strstr(buf, "# end") == buf) { if(!--depth) insert = p; continue; } // leave group, and suppress printing label depth++; // we must be entering a new group if(all) continue; // suppress printing group headers when complete list requested header = 1; @@ -9876,7 +10047,7 @@ NamesToList (char *names, char **engineList, char **engineMnemonic, char *group) if(depth != header && !all || skip) continue; // skip contents of group (but print first-level header) if(engineList[i]) free(engineList[i]); engineList[i] = strdup(buf); - if(buf[0] != '#') TidyProgramName(engineList[i], "localhost", buf); // group headers not tidied + if(buf[0] != '#') insert = p, TidyProgramName(engineList[i], "localhost", buf); // group headers not tidied if(engineMnemonic[i]) free(engineMnemonic[i]); if((q = strstr(engineList[i]+2, "variant")) && q[-2]== ' ' && (q[-1]=='/' || q[-1]=='-') && (q[7]==' ' || q[7]=='=')) { strcat(buf, " ("); @@ -9911,6 +10082,13 @@ SwapEngines (int n) SWAP(pgnName, p) SWAP(pvSAN, h) SWAP(engOptions, p) + SWAP(engInitString, p) + SWAP(computerString, p) + SWAP(features, p) + SWAP(fenOverride, p) + SWAP(NPS, h) + SWAP(accumulateTC, h) + SWAP(host, p) } int @@ -10047,11 +10225,11 @@ NextTourneyGame (int nr, int *swapColors) matchGame = 1; roundNr = nr / syncInterval + 1; } - if(first.pr != NoProc && second.pr != NoProc) return 1; // engines already loaded + if(first.pr != NoProc && second.pr != NoProc || nr<0) return 1; // engines already loaded // redefine engines, engine dir, etc. NamesToList(firstChessProgramNames, command, mnemonic, "all"); // get mnemonics of installed engines - if(first.pr == NoProc || nr < 0) { + if(first.pr == NoProc) { SetPlayer(whitePlayer, appData.participants); // find white player amongst it, and parse its engine line InitEngine(&first, 0); // initialize ChessProgramStates based on new settings. } @@ -10071,6 +10249,24 @@ NextMatchGame () { // performs game initialization that does not invoke engines, and then tries to start the game int res, firstWhite, swapColors = 0; if(!NextTourneyGame(nextGame, &swapColors)) return; // this sets matchGame, -fcp / -scp and other options for next game, if needed + if(matchMode && appData.debugMode) { // [HGM] debug split: game is part of a match; we might have to create a debug file just for this game + char buf[MSG_SIZ]; + snprintf(buf, MSG_SIZ, appData.nameOfDebugFile, nextGame+1); // expand name of debug file with %d in it + if(strcmp(buf, currentDebugFile)) { // name has changed + FILE *f = fopen(buf, "w"); + if(f) { // if opening the new file failed, just keep using the old one + ASSIGN(currentDebugFile, buf); + fclose(debugFP); + debugFP = f; + } + if(appData.serverFileName) { + if(serverFP) fclose(serverFP); + serverFP = fopen(appData.serverFileName, "w"); + if(serverFP && first.pr != NoProc) fprintf(serverFP, "StartChildProcess (dir=\".\") .\\%s\n", first.tidy); + if(serverFP && second.pr != NoProc) fprintf(serverFP, "StartChildProcess (dir=\".\") .\\%s\n", second.tidy); + } + } + } firstWhite = appData.firstPlaysBlack ^ (matchGame & 1 | appData.sameColorGames > 1); // non-incremental default firstWhite ^= swapColors; // reverses if NextTourneyGame says we are in an odd round first.twoMachinesColor = firstWhite ? "white\n" : "black\n"; // perform actual color assignement @@ -10668,6 +10864,7 @@ Reset (int redraw, int init) DisplayMessage("", ""); HistorySet(parseList, backwardMostMove, forwardMostMove, currentMove-1); lastSavedGame = 0; // [HGM] save: make sure next game counts as unsaved + ClearMap(); // [HGM] exclude: invalidate map } void @@ -13334,7 +13531,7 @@ WaitForEngine (ChessProgramState *cps, DelayedEventCallback retry) } else { /* kludge: allow timeout for initial "feature" command */ FreezeUI(); - snprintf(buf, MSG_SIZ, _("Starting %s chess program"), cps->which); + snprintf(buf, MSG_SIZ, _("Starting %s chess program"), _(cps->which)); DisplayMessage("", buf); ScheduleDelayedEvent(retry, FEATURE_TIMEOUT); } @@ -13384,17 +13581,6 @@ TwoMachinesEvent P((void)) break; } - if(matchMode && appData.debugMode) { // [HGM] debug split: game is part of a match; we might have to create a debug file just for this game - snprintf(buf, MSG_SIZ, appData.nameOfDebugFile, nextGame+1); // expand name of debug file with %d in it - if(strcmp(buf, currentDebugFile)) { // name has changed - FILE *f = fopen(buf, "w"); - if(f) { // if opening the new file failed, just keep using the old one - ASSIGN(currentDebugFile, buf); - fclose(debugFP); - debugFP = f; - } - } - } // forwardMostMove = currentMove; TruncateGame(); // [HGM] vari: MachineWhite and MachineBlack do this... @@ -13662,6 +13848,7 @@ EditPositionEvent () currentMove = forwardMostMove = backwardMostMove = 0; HistorySet(parseList, backwardMostMove, forwardMostMove, currentMove-1); DisplayMove(-1); + if(!appData.pieceMenu) DisplayMessage(_("Click clock to clear board"), ""); } void @@ -13908,6 +14095,8 @@ EditPositionMenuEvent (ChessSquare selection, int x, int y) } else boards[0][y][x] = selection; DrawPosition(TRUE, boards[0]); + ClearHighlights(); + fromX = fromY = -1; } break; } @@ -14204,7 +14393,7 @@ StopExaminingEvent () void ForwardInner (int target) { - int limit; + int limit; int oldSeekGraphUp = seekGraphUp; if (appData.debugMode) fprintf(debugFP, "ForwardInner(%d), current %d, forward %d\n", @@ -14261,11 +14450,12 @@ ForwardInner (int target) } DisplayBothClocks(); DisplayMove(currentMove - 1); - DrawPosition(FALSE, boards[currentMove]); + DrawPosition(oldSeekGraphUp, boards[currentMove]); HistorySet(parseList,backwardMostMove,forwardMostMove,currentMove-1); if ( !matchMode && gameMode != Training) { // [HGM] PV info: routine tests if empty DisplayComment(currentMove - 1, commentList[currentMove]); } + ClearMap(); // [HGM] exclude: invalidate map } @@ -14379,6 +14569,7 @@ BackwardInner (int target) HistorySet(parseList,backwardMostMove,forwardMostMove,currentMove-1); // [HGM] PV info: routine tests if comment empty DisplayComment(currentMove - 1, commentList[currentMove]); + ClearMap(); // [HGM] exclude: invalidate map } void @@ -15018,6 +15209,10 @@ SendToProgram (char *message, ChessProgramState *cps) fprintf(debugFP, "%ld >%-6s: %s", SubtractTimeMarks(&now, &programStartTime), cps->which, message); + if(serverFP) + fprintf(serverFP, "%ld >%-6s: %s", + SubtractTimeMarks(&now, &programStartTime), + cps->which, message), fflush(serverFP); } count = strlen(message); @@ -15119,6 +15314,11 @@ ReceiveFromProgram (InputSourceRef isr, VOIDSTAR closure, char *message, int cou SubtractTimeMarks(&now, &programStartTime), cps->which, quote, message); + if(serverFP) + fprintf(serverFP, "%ld <%-6s: %s%s\n", + SubtractTimeMarks(&now, &programStartTime), cps->which, + quote, + message), fflush(serverFP); } } @@ -15334,8 +15534,8 @@ ParseOption (Option *opt, ChessProgramState *cps) if(sscanf(p, " -check %d", &def) < 1) return FALSE; opt->value = (def != 0); opt->type = CheckBox; - } else if(p = strstr(opt->name, " -combo ")) { - opt->textValue = (char*) (&cps->comboList[cps->comboCnt]); // cheat with pointer type + } else if(p = strstr(opt->name, " -combo ")) { + opt->textValue = (char*) (opt->choice = &cps->comboList[cps->comboCnt]); // cheat with pointer type cps->comboList[cps->comboCnt++] = q = p+8; // holds possible choices if(*q == '*') cps->comboList[cps->comboCnt-1]++; opt->value = n = 0; @@ -15436,6 +15636,7 @@ ParseFeatures (char *args, ChessProgramState *cps) if (BoolFeature(&p, "playother", &cps->usePlayother, cps)) continue; if (BoolFeature(&p, "colors", &cps->useColors, cps)) continue; if (BoolFeature(&p, "usermove", &cps->useUsermove, cps)) continue; + if (BoolFeature(&p, "exclude", &cps->excludeMoves, cps)) continue; if (BoolFeature(&p, "ics", &cps->sendICS, cps)) continue; if (BoolFeature(&p, "name", &cps->sendName, cps)) continue; if (BoolFeature(&p, "pause", &val, cps)) continue; /* unused at present */ @@ -15455,7 +15656,10 @@ ParseFeatures (char *args, ChessProgramState *cps) if (BoolFeature(&p, "memory", &cps->memSize, cps)) continue; if (BoolFeature(&p, "smp", &cps->maxCores, cps)) continue; if (StringFeature(&p, "egt", cps->egtFormats, cps)) continue; - if (StringFeature(&p, "option", cps->option[cps->nrOptions].name, cps)) { + if (StringFeature(&p, "option", buf, cps)) { + FREE(cps->option[cps->nrOptions].name); + cps->option[cps->nrOptions].name = malloc(MSG_SIZ); + safeStrCpy(cps->option[cps->nrOptions].name, buf, MSG_SIZ); if(!ParseOption(&(cps->option[cps->nrOptions++]), cps)) { // [HGM] options: add option feature snprintf(buf, MSG_SIZ, "rejected option %s\n", cps->option[--cps->nrOptions].name); SendToProgram(buf, cps);