X-Git-Url: http://winboard.nl/cgi-bin?a=blobdiff_plain;f=backend.c;h=aa81da2b152c4f243e1bf893520936204f5c7e8b;hb=2987ec348b8b2cbf38ba9a6a0793652a6b238ae8;hp=ab347379d074f3776995f568e8e84355fd1e9807;hpb=1a74e7cd3bed2116e9bcfc4d2b5f270895dff16c;p=xboard.git diff --git a/backend.c b/backend.c index ab34737..aa81da2 100644 --- a/backend.c +++ b/backend.c @@ -148,6 +148,7 @@ extern int gettimeofday(struct timeval *, struct timezone *); #endif #include "backendz.h" #include "evalgraph.h" +#include "engineoutput.h" #include "gettext.h" #ifdef ENABLE_NLS @@ -250,7 +251,6 @@ static int NonStandardBoardSize P((VariantClass v, int w, int h, int s)); #endif ChessProgramState *WhitePlayer(); -void InsertIntoMemo P((int which, char *text)); // [HGM] kibitz: in engineo.c int VerifyDisplayMode P(()); char *GetInfoFromComment( int, char * ); // [HGM] PV time: returns stripped comment @@ -289,6 +289,7 @@ int chattingPartner; char marker[BOARD_RANKS][BOARD_FILES]; /* [HGM] marks for target squares */ char legal[BOARD_RANKS][BOARD_FILES]; /* [HGM] legal target squares */ char lastMsg[MSG_SIZ]; +char lastTalker[MSG_SIZ]; ChessSquare pieceSweep = EmptySquare; ChessSquare promoSweep = EmptySquare, defaultPromoChoice; int promoDefaultAltered; @@ -412,9 +413,15 @@ PosFlags (index) case VariantGrand: flags &= ~F_ALL_CASTLE_OK; break; + case VariantChu: + case VariantChuChess: + case VariantLion: + flags |= F_NULL_MOVE; + break; default: break; } + if(appData.fischerCastling) flags |= F_FRC_TYPE_CASTLING, flags &= ~F_ALL_CASTLE_OK; // [HGM] fischer return flags; } @@ -845,6 +852,7 @@ InitEngine (ChessProgramState *cps, int n) cps->analyzing = FALSE; cps->initDone = FALSE; cps->reload = FALSE; + cps->pseudo = appData.pseudo[n]; /* New features added by Tord: */ cps->useFEN960 = FALSE; @@ -2828,7 +2836,7 @@ read_from_ics (InputSourceRef isr, VOIDSTAR closure, char *data, int count, int int backup; /* [DM] For zippy color lines */ char *p; char talker[MSG_SIZ]; // [HGM] chat - int channel; + int channel, collective=0; connectionAlive = TRUE; // [HGM] alive: I think, therefore I am... @@ -3070,8 +3078,18 @@ read_from_ics (InputSourceRef isr, VOIDSTAR closure, char *data, int count, int char mess[MSG_SIZ]; snprintf(mess, MSG_SIZ, "%s%s", talker, parse); OutputChatMessage(chattingPartner, mess); + if(collective == 1) { // broadcasted talk also goes to private chatbox of talker + int p; + talker[strlen(talker+1)-1] = NULLCHAR; // strip closing delimiter + for(p=0; p= 0) // channel broadcast; look if there is a chatbox for this channel for(p=0; p= '0' && chatPartner[p][0] <= '9' && channel == atoi(chatPartner[p])) { talker[0] = '['; strcat(talker, "] "); - Colorize(channel == 1 ? ColorChannel1 : ColorChannel, FALSE); + Colorize((channel == 1 ? ColorChannel1 : ColorChannel), FALSE); chattingPartner = p; break; } } else if(buf[i-3] == 'e') // kibitz; look if there is a KIBITZ chatbox for(p=0; p') {// shout, c-shout or it; look if there is a 'shouts' chatbox if(buf[i-8] == '-' && buf[i-3] == 't') for(p=0; p') { talker[0] = '<'; strcat(talker, "> "); Colorize(ColorShout, FALSE); } else if(buf[i-8] == '-') { talker[0] = '('; strcat(talker, ") "); Colorize(ColorSShout, FALSE); } @@ -3331,18 +3354,23 @@ read_from_ics (InputSourceRef isr, VOIDSTAR closure, char *data, int count, int } if(chattingPartner<0) // if not, look if there is a chatbox for this indivdual for(p=0; p 0 && buf[oldi-1] == '\n') oldi--; - if (oldi > next_out) SendToPlayer(&buf[next_out], oldi - next_out); started = STARTED_COMMENT; parse_pos = 0; parse[0] = NULLCHAR; savingComment = 3 + chattingPartner; // counts as TRUE - suppressKibitz = TRUE; - continue; + if(collective == 3) i = oldi; else { + suppressKibitz = TRUE; + if(oldi > 0 && buf[oldi-1] == '\n') oldi--; + if (oldi > next_out) SendToPlayer(&buf[next_out], oldi - next_out); + continue; + } } } // [HGM] chat: end of patch @@ -3426,7 +3454,7 @@ read_from_ics (InputSourceRef isr, VOIDSTAR closure, char *data, int count, int parse[parse_pos] = NULLCHAR; started = STARTED_COMMENT; savingComment = TRUE; - } else { + } else if(collective != 3) { started = STARTED_CHATTER; savingComment = FALSE; } @@ -5093,8 +5121,7 @@ SendMoveToProgram (int moveNum, ChessProgramState *cps) /* Added by Tord: Send castle moves in "O-O" in FRC games if required by * the engine. It would be nice to have a better way to identify castle * moves here. */ - if((gameInfo.variant == VariantFischeRandom || gameInfo.variant == VariantCapaRandom) - && cps->useOOCastle) { + if(appData.fischerCastling && cps->useOOCastle) { int fromX = moveList[moveNum][0] - AAA; int fromY = moveList[moveNum][1] - ONE; int toX = moveList[moveNum][2] - AAA; @@ -5647,10 +5674,13 @@ LoadMultiPV (int x, int y, char *buf, int index, int *start, int *end, int pane) } else if(strstr(buf+lineStart, "exclude:") == buf+lineStart) { // exclude moves clicked ExcludeClick(origIndex - lineStart); return FALSE; + } else if(!strncmp(buf+lineStart, "dep\t", 4)) { // column headers clicked + Collapse(origIndex - lineStart); + return FALSE; } ParsePV(buf+startPV, FALSE, gameMode != AnalyzeMode); *start = startPV; *end = index-1; - extendGame = (gameMode == AnalyzeMode && appData.autoExtend); + extendGame = (gameMode == AnalyzeMode && appData.autoExtend && origIndex - startPV < 5); return TRUE; } @@ -5968,7 +5998,7 @@ InitPosition (int redraw) oldh = gameInfo.holdingsWidth; static int oldv; - if(appData.icsActive) shuffleOpenings = FALSE; // [HGM] shuffle: in ICS mode, only shuffle on ICS request + if(appData.icsActive) shuffleOpenings = appData.fischerCastling = FALSE; // [HGM] shuffle: in ICS mode, only shuffle on ICS request /* [AS] Initialize pv info list [HGM] and game status */ { @@ -6009,6 +6039,7 @@ InitPosition (int redraw) switch (gameInfo.variant) { case VariantFischeRandom: shuffleOpenings = TRUE; + appData.fischerCastling = TRUE; default: break; case VariantShatranj: @@ -6039,6 +6070,7 @@ InitPosition (int redraw) break; case VariantCapaRandom: shuffleOpenings = TRUE; + appData.fischerCastling = TRUE; case VariantCapablanca: pieces = CapablancaArray; gameInfo.boardWidth = 10; @@ -6790,6 +6822,7 @@ int lastLoadGameUseList = FALSE; char lastLoadGameTitle[MSG_SIZ], lastLoadPositionTitle[MSG_SIZ]; ChessMove lastLoadGameStart = EndOfFile; int doubleClick; +Boolean addToBookFlag; void UserMoveEvent(int fromX, int fromY, int toX, int toY, int promoChar) @@ -6912,6 +6945,13 @@ UserMoveEvent(int fromX, int fromY, int toX, int toY, int promoChar) DrawPosition(FALSE, boards[currentMove]); return; } else if (toX >= 0 && toY >= 0) { + if(!appData.pieceMenu && toX == fromX && toY == fromY && boards[0][rf][ff] != EmptySquare) { + ChessSquare q, p = boards[0][rf][ff]; + if(p >= BlackPawn) p = BLACK_TO_WHITE p; + if(CHUPROMOTED p < BlackPawn) p = q = CHUPROMOTED boards[0][rf][ff]; + else p = CHUDEMOTED (q = boards[0][rf][ff]); + if(PieceToChar(q) == '+') gatingPiece = p; + } boards[0][toY][toX] = boards[0][fromY][fromX]; if(fromX == BOARD_LEFT-2) { // handle 'moves' out of holdings if(boards[0][fromY][0] != EmptySquare) { @@ -6932,7 +6972,7 @@ UserMoveEvent(int fromX, int fromY, int toX, int toY, int promoChar) return; } - if(toX < 0 || toY < 0) return; + if((toX < 0 || toY < 0) && (fromY != DROP_RANK || fromX != EmptySquare)) return; pup = boards[currentMove][toY][toX]; /* [HGM] If move started in holdings, it means a drop. Convert to standard form */ @@ -6952,7 +6992,7 @@ UserMoveEvent(int fromX, int fromY, int toX, int toY, int promoChar) moveType = LegalityTest(boards[currentMove], PosFlags(currentMove), fromY, fromX, toY, toX, promoChar); - if(fromY == DROP_RANK && fromX == EmptySquare && (gameMode == AnalyzeMode || gameMode == EditGame)) moveType = NormalMove; + if(fromY == DROP_RANK && fromX == EmptySquare && (gameMode == AnalyzeMode || gameMode == EditGame || PosFlags(0) & F_NULL_MOVE)) moveType = NormalMove; /* [HGM] but possibly ignore an IllegalMove result */ if (appData.testLegality) { @@ -6969,6 +7009,16 @@ UserMoveEvent(int fromX, int fromY, int toX, int toY, int promoChar) return; } + if(addToBookFlag) { // adding moves to book + char buf[MSG_SIZ], move[MSG_SIZ]; + CoordsToAlgebraic(boards[currentMove], PosFlags(currentMove), fromY, fromX, toY, toX, promoChar, move); + snprintf(buf, MSG_SIZ, " 0.0%% 1 %s\n", move); + AddBookMove(buf); + addToBookFlag = FALSE; + ClearHighlights(); + return; + } + FinishMove(moveType, fromX, fromY, toX, toY, promoChar); } @@ -7441,7 +7491,7 @@ LeftClick (ClickType clickType, int xPix, int yPix) /* 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 || gameInfo.variant == VariantSChess; + frc = appData.fischerCastling || gameInfo.variant == VariantSChess; if( (killX < 0 || x != fromX || y != fromY) && // [HGM] lion: do not interpret igui as deselect! ((WhitePawn <= fromP && fromP <= WhiteKing && WhitePawn <= toP && toP <= WhiteKing && @@ -8321,10 +8371,10 @@ BitbaseProbe () // probe EGBB if(loaded == 2) return 13; // loading failed before if(loaded == 0) { - loaded = 2; // prepare for failure char *p, *path = strstr(appData.egtFormats, "scorpio:"), buf[MSG_SIZ]; HMODULE lib; PLOAD_EGBB loadBB; + loaded = 2; // prepare for failure if(!path) return 13; // no egbb installed strncpy(buf, path + 8, MSG_SIZ); if(p = strchr(buf, ',')) *p = NULLCHAR; else p = buf + strlen(buf); @@ -8624,7 +8674,7 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h GameEnds(machineWhite ? BlackWins : WhiteWins, buf1, GE_XBOARD); return; - } else if(gameInfo.variant != VariantFischeRandom && gameInfo.variant != VariantCapaRandom) + } else if(!appData.fischerCastling) /* [HGM] Kludge to handle engines that send FRC-style castling when they shouldn't (like TSCP-Gothic) */ switch(moveType) { @@ -8668,7 +8718,7 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h } /* [AS] Adjudicate game if needed (note: remember that forwardMostMove now points past the last move) */ - if( gameMode == TwoMachinesPlay && adjudicateLossThreshold != 0 && forwardMostMove >= adjudicateLossPlies ) { + if( gameMode == TwoMachinesPlay && appData.adjudicateLossThreshold != 0 && forwardMostMove >= adjudicateLossPlies ) { int count = 0; while( count < adjudicateLossPlies ) { @@ -8677,8 +8727,8 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h if( count & 1 ) { score = -score; /* Flip score for winning side */ } - - if( score > adjudicateLossThreshold ) { +printf("score=%d count=%d\n",score,count); + if( score > appData.adjudicateLossThreshold ) { break; } @@ -8722,7 +8772,6 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h (unsigned int)programStats.nodes / (10*abs(programStats.time) + 1.), programStats.movelist); SendToICS(buf); -if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats.nodes, programStats.nodes); } } #endif @@ -8816,7 +8865,10 @@ if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats. int dummy, w, h, hand, s=6; char buf[MSG_SIZ], varName[MSG_SIZ]; if(appData.icsActive || forwardMostMove != 0 || cps != &first) return; *buf = NULLCHAR; - if(sscanf(message, "setup (%s", buf) == 1) s = 8 + strlen(buf), buf[s-9] = NULLCHAR, SetCharTable(pieceToChar, buf); + if(sscanf(message, "setup (%s", buf) == 1) { + s = 8 + strlen(buf), buf[s-9] = NULLCHAR, SetCharTable(pieceToChar, buf); + ASSIGN(appData.pieceToCharTable, buf); + } if(startedFromSetupPosition) return; dummy = sscanf(message+s, "%dx%d+%d_%s", &w, &h, &hand, varName); if(dummy >= 3) { @@ -9097,6 +9149,10 @@ if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats. Don't use it. */ cps->sendTime = 0; } + if (cps->pseudo) { // [HGM] pseudo-engine, granted unusual powers + if (sscanf(message, "wtime %ld\n", &whiteTimeRemaining) == 1 || // adjust clock times + sscanf(message, "btime %ld\n", &blackTimeRemaining) == 1 ) return; + } /* * If chess program startup fails, exit with an error message. @@ -9365,6 +9421,9 @@ if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats. if (sscanf(message, "%d%c %d %d " u64Display " %[^\n]\n", &plylev, &plyext, &curscore, &time, &nodes, buf1) >= 5) { + if(nodes>>32 == u64Const(0xFFFFFFFF)) // [HGM] negative node count read + nodes += u64Const(0x100000000); + if (plyext != ' ' && plyext != '\t') { time *= 100; } @@ -9817,11 +9876,11 @@ ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board) } piece = board[toY][toX] = (ChessSquare) fromX; } else { - ChessSquare victim; +// ChessSquare victim; int i; if( killX >= 0 && killY >= 0 ) // [HGM] lion: Lion trampled over something - victim = board[killY][killX], +// victim = board[killY][killX], board[killY][killX] = EmptySquare, board[EP_STATUS] = EP_CAPTURE; @@ -10388,6 +10447,7 @@ InitChessProgram (ChessProgramState *cps, int setup) SendToProgram(buf, cps); } + setboardSpoiledMachineBlack = FALSE; SendToProgram(cps->initString, cps); if (gameInfo.variant != VariantNormal && gameInfo.variant != VariantLoadable @@ -10801,6 +10861,7 @@ SwapEngines (int n) SWAP(accumulateTC, h) SWAP(drawDepth, h) SWAP(host, p) + SWAP(pseudo, h) } int @@ -11694,11 +11755,20 @@ AutoPlayOneMove () SetHighlights(-1, -1, toX, toY); } } else { + int viaX = moveList[currentMove][5] - AAA; + int viaY = moveList[currentMove][6] - ONE; fromX = moveList[currentMove][0] - AAA; fromY = moveList[currentMove][1] - ONE; HistorySet(parseList, backwardMostMove, forwardMostMove, currentMove); /* [AS] */ + if(moveList[currentMove][4] == ';') { // multi-leg + ChessSquare piece = boards[currentMove][viaY][viaX]; + AnimateMove(boards[currentMove], fromX, fromY, viaX, viaY); + boards[currentMove][viaY][viaX] = boards[currentMove][fromY][fromX]; + AnimateMove(boards[currentMove], fromX=viaX, fromY=viaY, toX, toY); + boards[currentMove][viaY][viaX] = piece; + } else AnimateMove(boards[currentMove], fromX, fromY, toX, toY); if (appData.highlightLastMove) { @@ -12306,12 +12376,26 @@ QuickCompare (Board board, int *minCounts, int *maxCounts) int QuickScan (Board board, Move *move) { // reconstruct game,and compare all positions in it - int cnt=0, stretch=0, total = MakePieceList(board, counts); + int cnt=0, stretch=0, found = -1, total = MakePieceList(board, counts); do { int piece = move->piece; int to = move->to, from = pieceList[piece]; + if(found < 0) { // if already found just scan to game end for final piece count + if(QuickCompare(soughtBoard, minSought, maxSought) || + appData.ignoreColors && QuickCompare(reverseBoard, minReverse, maxReverse) || + flipSearch && (QuickCompare(flipBoard, minSought, maxSought) || + appData.ignoreColors && QuickCompare(rotateBoard, minReverse, maxReverse)) + ) { + static int lastCounts[EmptySquare+1]; + int i; + if(stretch) for(i=0; i= appData.stretch)) found = cnt + 1 - stretch; + if(found >= 0 && !appData.minPieces) return found; + } if(piece <= Q_PROMO) { // special moves encoded by otherwise invalid piece numbers 1-4 - if(!piece) return -1; + if(!piece) return (appData.minPieces && (total < appData.minPieces || total > appData.maxPieces) ? -1 : found); if(piece == Q_PROMO) { // promotion, encoded as (Q_PROMO, to) + (piece, promoType) piece = (++move)->piece; from = pieceList[piece]; @@ -12336,23 +12420,12 @@ QuickScan (Board board, Move *move) } } if(appData.searchMode > 2) counts[pieceType[quickBoard[to]]]--; // account capture - if((total -= (quickBoard[to] != 0)) < soughtTotal) return -1; // piece count dropped below what we search for + if((total -= (quickBoard[to] != 0)) < soughtTotal && found < 0) return -1; // piece count dropped below what we search for quickBoard[from] = 0; aftercastle: quickBoard[to] = piece; pieceList[piece] = to; cnt++; turn ^= 3; - if(QuickCompare(soughtBoard, minSought, maxSought) || - appData.ignoreColors && QuickCompare(reverseBoard, minReverse, maxReverse) || - flipSearch && (QuickCompare(flipBoard, minSought, maxSought) || - appData.ignoreColors && QuickCompare(rotateBoard, minReverse, maxReverse)) - ) { - static int lastCounts[EmptySquare+1]; - int i; - if(stretch) for(i=0; i= appData.stretch)) return cnt + 1 - stretch; move++; } while(1); } @@ -13305,9 +13378,9 @@ GetOutOfBookInfo (char * buf) } } -/* Save game in PGN style and close the file */ -int -SaveGamePGN (FILE *f) +/* Save game in PGN style */ +static void +SaveGamePGN2 (FILE *f) { int i, offset, linelen, newblock; // char *movetext; @@ -13467,7 +13540,13 @@ SaveGamePGN (FILE *f) } else { fprintf(f, "%s\n\n", PGNResult(gameInfo.result)); } +} +/* Save game in PGN style and close the file */ +int +SaveGamePGN (FILE *f) +{ + SaveGamePGN2(f); fclose(f); lastSavedGame = GameCheckSum(); // [HGM] save: remember ID of last saved game to prevent double saving return TRUE; @@ -13946,6 +14025,7 @@ ExitEvent (int status) return; } + if (appData.icsActive) printf("\n"); // [HGM] end on new line after closing XBoard if (appData.icsActive && appData.colorize) Colorize(ColorNone, FALSE); if (telnetISR != NULL) { @@ -14208,7 +14288,10 @@ AnalyzeModeEvent () first.maybeThinking = FALSE; /* avoid killing GNU Chess */ EngineOutputPopUp(); } - if (!appData.icsEngineAnalyze) gameMode = AnalyzeMode; + if (!appData.icsEngineAnalyze) { + gameMode = AnalyzeMode; + ClearEngineOutputPane(0); // [TK] exclude: to print exclusion/multipv header + } pausing = FALSE; ModeHighlight(); SetGameInfo(); @@ -14963,14 +15046,14 @@ EditPositionMenuEvent (ChessSquare selection, int x, int y) boards[0][y][x] = p; } } - menuBoard[1][x] = menuBoard[BOARD_HEIGHT-2][x] = p; } if(gameMode != IcsExamining) { // [HGM] editpos: cycle trough boards - for(x = BOARD_LEFT; x < BOARD_RGHT; x++) { // create 'menu board' by removing duplicates - ChessSquare p = menuBoard[0][x]; - for(y = x + 1; y < BOARD_RGHT; y++) if(menuBoard[0][y] == p) menuBoard[0][y] = EmptySquare; - p = menuBoard[BOARD_HEIGHT-1][x]; - for(y = x + 1; y < BOARD_RGHT; y++) if(menuBoard[BOARD_HEIGHT-1][y] == p) menuBoard[BOARD_HEIGHT-1][y] = EmptySquare; + int r; + for(r = 0; r < BOARD_HEIGHT; r++) { + for(x = BOARD_LEFT; x < BOARD_RGHT; x++) { // create 'menu board' by removing duplicates + ChessSquare p = menuBoard[r][x]; + for(y = x + 1; y < BOARD_RGHT; y++) if(menuBoard[r][y] == p) menuBoard[r][y] = EmptySquare; + } } DisplayMessage("Clicking clock again restores position", ""); if(gameInfo.variant != lastVariant) lastVariant = gameInfo.variant, CopyBoard(erasedBoard, boards[0]); @@ -15232,7 +15315,8 @@ ClockClick (int which) if (gameMode == EditPosition || gameMode == IcsExamining) { if(!appData.pieceMenu && blackPlaysFirst) EditPositionMenuEvent(ClearBoard, 0, 0); SetBlackToPlayEvent(); - } else if ((gameMode == AnalyzeMode || gameMode == EditGame) && !blackFlag && WhiteOnMove(currentMove)) { + } else if ((gameMode == AnalyzeMode || gameMode == EditGame || + gameMode == MachinePlaysBlack && PosFlags(0) & F_NULL_MOVE && !blackFlag && !shiftKey) && WhiteOnMove(currentMove)) { UserMoveEvent((int)EmptySquare, DROP_RANK, 0, 0, 0); // [HGM] multi-move: if not out of time, enters null move } else if (shiftKey) { AdjustClock(which, -1); @@ -15244,7 +15328,8 @@ ClockClick (int which) if (gameMode == EditPosition || gameMode == IcsExamining) { if(!appData.pieceMenu && !blackPlaysFirst) EditPositionMenuEvent(ClearBoard, 0, 0); SetWhiteToPlayEvent(); - } else if ((gameMode == AnalyzeMode || gameMode == EditGame) && !whiteFlag && !WhiteOnMove(currentMove)) { + } else if ((gameMode == AnalyzeMode || gameMode == EditGame || + gameMode == MachinePlaysWhite && PosFlags(0) & F_NULL_MOVE && !whiteFlag && !shiftKey) && !WhiteOnMove(currentMove)) { UserMoveEvent((int)EmptySquare, DROP_RANK, 0, 0, 0); // [HGM] multi-move } else if (shiftKey) { AdjustClock(which, -1); @@ -15408,9 +15493,18 @@ ForwardInner (int target) SetHighlights(-1, -1, toX, toY); } } else { + int viaX = moveList[target - 1][5] - AAA; + int viaY = moveList[target - 1][6] - ONE; fromX = moveList[target - 1][0] - AAA; fromY = moveList[target - 1][1] - ONE; if (target == currentMove + 1) { + if(moveList[target - 1][4] == ';') { // multi-leg + ChessSquare piece = boards[currentMove][viaY][viaX]; + AnimateMove(boards[currentMove], fromX, fromY, viaX, viaY); + boards[currentMove][viaY][viaX] = boards[currentMove][fromY][fromX]; + AnimateMove(boards[currentMove], viaX, viaY, toX, toY); + boards[currentMove][viaY][viaX] = piece; + } else AnimateMove(boards[currentMove], fromX, fromY, toX, toY); } if (appData.highlightLastMove) { @@ -15756,6 +15850,36 @@ HintEvent () hintRequested = TRUE; } +int +SaveSelected (FILE *g, int dummy, char *dummy2) +{ + ListGame * lg = (ListGame *) gameList.head; + int nItem, cnt=0; + FILE *f; + + if( !(f = GameFile()) || ((ListGame *) gameList.tailPred)->number <= 0 ) { + DisplayError(_("Game list not loaded or empty"), 0); + return 0; + } + + creatingBook = TRUE; // suppresses stuff during load game + + /* Get list size */ + for (nItem = 1; nItem <= ((ListGame *) gameList.tailPred)->number; nItem++){ + if(lg->position >= 0) { // selected? + LoadGame(f, nItem, "", TRUE); + SaveGamePGN2(g); // leaves g open + cnt++; DoEvents(); + } + lg = (ListGame *) lg->node.succ; + } + + fclose(g); + creatingBook = FALSE; + + return cnt; +} + void CreateBookEvent () { @@ -15781,8 +15905,11 @@ CreateBookEvent () /* Get list size */ for (nItem = 1; nItem <= ((ListGame *) gameList.tailPred)->number; nItem++){ - LoadGame(f, nItem, "", TRUE); - AddGameToBook(TRUE); + if(lg->position >= 0) { + LoadGame(f, nItem, "", TRUE); + AddGameToBook(TRUE); + DoEvents(); + } lg = (ListGame *) lg->node.succ; } @@ -16481,6 +16608,10 @@ EngineDefinedVariant (ChessProgramState *cps, int n) v = StringToVariant(s); if(v == VariantNormal && strcmp(s, "normal") && !strstr(s, "_normal")) v = VariantUnknown; // garbage is recognized as normal if(v == VariantUnknown) { // non-standard variant in list of engine-supported variants + if(!strcmp(s, "tenjiku") || !strcmp(s, "dai") || !strcmp(s, "dada") || // ignore Alien-Edition variants + !strcmp(s, "maka") || !strcmp(s, "tai") || !strcmp(s, "kyoku") || + !strcmp(s, "checkers") || !strcmp(s, "go") || !strcmp(s, "reversi") || + !strcmp(s, "dark") || !strcmp(s, "alien") || !strcmp(s, "multi") || !strcmp(s, "amazons") ) n++; if(--n < 0) safeStrCpy(buf, s, MSG_SIZ); } if(p) *p++ = ','; @@ -17589,12 +17720,12 @@ PositionToFEN (int move, char *overrideCastling, int moveCounts) if(PieceToChar(piece) == '+') { /* [HGM] write promoted pieces as '+' (Shogi) */ *p++ = '+'; - piece = (ChessSquare)(DEMOTED piece); + piece = (ChessSquare)(CHUDEMOTED piece); } *p++ = (piece == DarkSquare ? '*' : PieceToChar(piece)); if(p[-1] == '~') { /* [HGM] flag promoted pieces as '~' (Crazyhouse) */ - p[-1] = PieceToChar((ChessSquare)(DEMOTED piece)); + p[-1] = PieceToChar((ChessSquare)(CHUDEMOTED piece)); *p++ = '~'; } } @@ -17640,7 +17771,7 @@ PositionToFEN (int move, char *overrideCastling, int moveCounts) } else { if(nrCastlingRights) { q = p; - if(gameInfo.variant == VariantFischeRandom || gameInfo.variant == VariantCapaRandom) { + if(appData.fischerCastling) { /* [HGM] write directly from rights */ if(boards[move][CASTLING][2] != NoRights && boards[move][CASTLING][0] != NoRights ) @@ -17748,7 +17879,7 @@ PositionToFEN (int move, char *overrideCastling, int moveCounts) Boolean ParseFEN (Board board, int *blackPlaysFirst, char *fen, Boolean autoSize) { - int i, j, k, w=0; + int i, j, k, w=0, subst=0, shuffle=0; char *p, c; int emptycount, virgin[BOARD_FILES]; ChessSquare piece; @@ -17772,7 +17903,7 @@ ParseFEN (Board board, int *blackPlaysFirst, char *fen, Boolean autoSize) appData.NrRanks = gameInfo.boardHeight - i; i=0; } break; -#if(BOARD_FILES >= 10) +#if(BOARD_FILES >= 10)*0 } else if(*p=='x' || *p=='X') { /* [HGM] X means 10 */ p++; emptycount=10; if (j + emptycount > gameInfo.boardWidth) return FALSE; @@ -17787,6 +17918,16 @@ ParseFEN (Board board, int *blackPlaysFirst, char *fen, Boolean autoSize) if (j + emptycount > gameInfo.boardWidth) return FALSE; while (emptycount--) board[i][(j++)+gameInfo.holdingsWidth] = EmptySquare; + } else if (*p == '<') { + if(i == BOARD_HEIGHT-1) shuffle = 1; + else if (i != 0 || !shuffle) return FALSE; + p++; + } else if (shuffle && *p == '>') { + p++; // for now ignore closing shuffle range, and assume rank-end + } else if (*p == '?') { + if (j >= gameInfo.boardWidth) return FALSE; + if (i != 0 && i != BOARD_HEIGHT-1) return FALSE; // only on back-rank + board[i][(j++)+gameInfo.holdingsWidth] = ClearBoard; p++; subst++; // placeHolder } else if (*p == '+' || isalpha(*p)) { if (j >= gameInfo.boardWidth) return FALSE; if(*p=='+') { @@ -17825,7 +17966,9 @@ ParseFEN (Board board, int *blackPlaysFirst, char *fen, Boolean autoSize) /* [HGM] look for Crazyhouse holdings here */ while(*p==' ') p++; if( gameInfo.holdingsWidth && p[-1] == '/' || *p == '[') { + int swap=0, wcnt=0, bcnt=0; if(*p == '[') p++; + if(*p == '<') swap++, p++; if(*p == '-' ) p++; /* empty holdings */ else { if( !gameInfo.holdingsWidth ) return FALSE; /* no room to put holdings! */ /* if we would allow FEN reading to set board size, we would */ @@ -17838,18 +17981,46 @@ ParseFEN (Board board, int *blackPlaysFirst, char *fen, Boolean autoSize) if( i >= gameInfo.holdingsSize ) return FALSE; board[BOARD_HEIGHT-1-i][0] = piece; /* black holdings */ board[BOARD_HEIGHT-1-i][1]++; /* black counts */ + bcnt++; } else { i = (int)piece - (int)WhitePawn; i = PieceToNumber((ChessSquare)i); if( i >= gameInfo.holdingsSize ) return FALSE; board[i][BOARD_WIDTH-1] = piece; /* white holdings */ board[i][BOARD_WIDTH-2]++; /* black holdings */ + wcnt++; + } + } + if(subst) { // substitute back-rank question marks by holdings pieces + for(j=BOARD_LEFT; j= bcnt) n = rand() % bcnt; // use same randomization for black and white if possible + for(k=0, m=n; k= 'A' && *p <= 'Z' || *p >= 'a' && *p <= 'z' || *p=='-') { /* castling indicator present, so default becomes no castlings */ @@ -17898,7 +18070,7 @@ ParseFEN (Board board, int *blackPlaysFirst, char *fen, Boolean autoSize) } } while(*p=='K' || *p=='Q' || *p=='k' || *p=='q' || *p=='-' || - (gameInfo.variant == VariantFischeRandom || gameInfo.variant == VariantCapaRandom || gameInfo.variant == VariantSChess) && + (appData.fischerCastling || gameInfo.variant == VariantSChess) && ( *p >= 'a' && *p < 'a' + gameInfo.boardWidth) || ( *p >= 'A' && *p < 'A' + gameInfo.boardWidth) ) { int c = *p++, whiteKingFile=NoRights, blackKingFile=NoRights; @@ -17920,6 +18092,7 @@ ParseFEN (Board board, int *blackPlaysFirst, char *fen, Boolean autoSize) board[CASTLING][2] = whiteKingFile; if(board[CASTLING][0] != NoRights) virgin[board[CASTLING][0]] |= VIRGIN_W; if(board[CASTLING][2] != NoRights) virgin[board[CASTLING][2]] |= VIRGIN_W; + if(whiteKingFile != BOARD_WIDTH>>1|| i != BOARD_RGHT-1) fischer = 1; break; case'Q': for(i=BOARD_LEFT; i>1|| i != BOARD_LEFT) fischer = 1; break; case'k': for(i=BOARD_RGHT-1; board[BOARD_HEIGHT-1][i]!=BlackRook && i>blackKingFile; i--); @@ -17934,6 +18108,7 @@ ParseFEN (Board board, int *blackPlaysFirst, char *fen, Boolean autoSize) board[CASTLING][5] = blackKingFile; if(board[CASTLING][3] != NoRights) virgin[board[CASTLING][3]] |= VIRGIN_B; if(board[CASTLING][5] != NoRights) virgin[board[CASTLING][5]] |= VIRGIN_B; + if(blackKingFile != BOARD_WIDTH>>1|| i != BOARD_RGHT-1) fischer = 1; break; case'q': for(i=BOARD_LEFT; i>1|| i != BOARD_LEFT) fischer = 1; case '-': break; default: /* FRC castlings */ @@ -17974,7 +18150,9 @@ ParseFEN (Board board, int *blackPlaysFirst, char *fen, Boolean autoSize) } for(i=0; i