X-Git-Url: http://winboard.nl/cgi-bin?a=blobdiff_plain;f=backend.c;h=57719962d645760f726a8100f5c5e6b2d594c7af;hb=7295e7b8f71c001f64930229b3d99cf8b2245a9e;hp=2db140521d9d753e693aa996ba41d2dfa73930e0;hpb=69ce008d53a3c2fe16f7522b2c2dc7957fb5ea13;p=xboard.git diff --git a/backend.c b/backend.c index 2db1405..5771996 100644 --- a/backend.c +++ b/backend.c @@ -5,7 +5,7 @@ * Massachusetts. * * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006, - * 2007, 2008, 2009, 2010, 2011, 2012, 2013 Free Software Foundation, Inc. + * 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014 Free Software Foundation, Inc. * * Enhancements Copyright 2005 Alessandro Scotti * @@ -55,14 +55,32 @@ #ifdef WIN32 #include -int flock(int f, int code); -#define LOCK_EX 2 -#define SLASH '\\' + int flock(int f, int code); +# define LOCK_EX 2 +# define SLASH '\\' + +# ifdef ARC_64BIT +# define EGBB_NAME "egbbdll64.dll" +# else +# define EGBB_NAME "egbbdll.dll" +# endif #else -#include -#define SLASH '/' +# include +# define SLASH '/' + +# include +# ifdef ARC_64BIT +# define EGBB_NAME "egbbso64.so" +# else +# define EGBB_NAME "egbbso.so" +# endif + // kludge to allow Windows code in back-end by converting it to corresponding Linux code +# define CDECL +# define HMODULE void * +# define LoadLibrary(x) dlopen(x, RTLD_LAZY) +# define GetProcAddress dlsym #endif @@ -130,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 @@ -232,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 @@ -271,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; @@ -397,6 +416,7 @@ PosFlags (index) default: break; } + if(appData.fischerCastling) flags |= F_FRC_TYPE_CASTLING, flags &= ~F_ALL_CASTLE_OK; // [HGM] fischer return flags; } @@ -759,8 +779,7 @@ UnloadEngine (ChessProgramState *cps) ExitAnalyzeMode(); DoSleep( appData.delayBeforeQuit ); SendToProgram("quit\n", cps); - DoSleep( appData.delayAfterQuit ); - DestroyChildProcess(cps->pr, cps->useSigterm); + DestroyChildProcess(cps->pr, 4 + cps->useSigterm); } cps->pr = NoProc; if(appData.debugMode) fprintf(debugFP, "Unload %s\n", cps->which); @@ -828,6 +847,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; @@ -845,6 +865,7 @@ InitEngine (ChessProgramState *cps, int n) /* [HGM] debug */ cps->debug = FALSE; + cps->drawDepth = appData.drawDepth[n]; cps->supportsNPS = UNKNOWN; cps->memSize = FALSE; cps->maxCores = FALSE; @@ -2810,7 +2831,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... @@ -3052,8 +3073,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); } @@ -3313,18 +3349,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 @@ -3408,7 +3449,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; } @@ -4813,7 +4854,7 @@ ParseBoard12 (char *string) default: break; case MT_CHECK: - if(gameInfo.variant != VariantShogi) + if(!IS_SHOGI(gameInfo.variant)) strcat(parseList[moveNum - 1], "+"); break; case MT_CHECKMATE: @@ -5075,8 +5116,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; @@ -5629,6 +5669,9 @@ 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; @@ -5950,7 +5993,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 */ { @@ -5991,6 +6034,7 @@ InitPosition (int redraw) switch (gameInfo.variant) { case VariantFischeRandom: shuffleOpenings = TRUE; + appData.fischerCastling = TRUE; default: break; case VariantShatranj: @@ -6021,6 +6065,7 @@ InitPosition (int redraw) break; case VariantCapaRandom: shuffleOpenings = TRUE; + appData.fischerCastling = TRUE; case VariantCapablanca: pieces = CapablancaArray; gameInfo.boardWidth = 10; @@ -6501,8 +6546,8 @@ HasPromotionChoice (int fromX, int fromY, int toX, int toY, char *promoChoice, i promotionZoneSize = 3; } - // Treat Lance as Pawn when it is not representing Amazon - if(gameInfo.variant != VariantSuper) { + // Treat Lance as Pawn when it is not representing Amazon or Lance + if(gameInfo.variant != VariantSuper && gameInfo.variant != VariantChu) { if(piece == WhiteLance) piece = WhitePawn; else if(piece == BlackLance) piece = BlackPawn; } @@ -6553,7 +6598,11 @@ HasPromotionChoice (int fromX, int fromY, int toX, int toY, char *promoChoice, i // we either have a choice what to promote to, or (in Shogi) whether to promote if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier || gameInfo.variant == VariantMakruk || gameInfo.variant == VariantASEAN) { - *promoChoice = PieceToChar(BlackFerz); // no choice + ChessSquare p=BlackFerz; // no choice + while(p < EmptySquare) { //but make sure we use piece that exists + *promoChoice = PieceToChar(p++); + if(*promoChoice != '.') break; + } return FALSE; } // no sense asking what we must promote to if it is going to explode... @@ -6768,6 +6817,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) @@ -6947,6 +6997,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); } @@ -7419,7 +7479,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 && @@ -8030,6 +8090,7 @@ Adjudicate (ChessProgramState *cps) case MT_NONE: default: break; + case MT_STEALMATE: case MT_STALEMATE: case MT_STAINMATE: reason = "Xboard adjudication: Stalemate"; @@ -8268,11 +8329,66 @@ Adjudicate (ChessProgramState *cps) return 0; } +typedef int (CDECL *PPROBE_EGBB) (int player, int *piece, int *square); +typedef int (CDECL *PLOAD_EGBB) (char *path, int cache_size, int load_options); +static int egbbCode[] = { 6, 5, 4, 3, 2, 1 }; + +static int +BitbaseProbe () +{ + int pieces[10], squares[10], cnt=0, r, f, res; + static int loaded; + static PPROBE_EGBB probeBB; + if(!appData.testLegality) return 10; + if(BOARD_HEIGHT != 8 || BOARD_RGHT-BOARD_LEFT != 8) return 12; + if(gameInfo.holdingsSize && gameInfo.variant != VariantSuper && gameInfo.variant != VariantSChess) return 12; + if(loaded == 2 && forwardMostMove < 2) loaded = 0; // retry on new game + for(r=0; r= BlackPawn); + int type = piece - black*BlackPawn; + if(piece == EmptySquare) continue; + if(type != WhiteKing && type > WhiteQueen) return 12; // unorthodox piece + if(type == WhiteKing) type = WhiteQueen + 1; + type = egbbCode[type]; + squares[cnt] = r*(BOARD_RGHT - BOARD_LEFT) + f - BOARD_LEFT; + pieces[cnt] = type + black*6; + if(++cnt > 5) return 11; + } + pieces[cnt] = squares[cnt] = 0; + // 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; + if(!path) return 13; // no egbb installed + strncpy(buf, path + 8, MSG_SIZ); + if(p = strchr(buf, ',')) *p = NULLCHAR; else p = buf + strlen(buf); + snprintf(p, MSG_SIZ - strlen(buf), "%c%s", SLASH, EGBB_NAME); + lib = LoadLibrary(buf); + if(!lib) { DisplayError(_("could not load EGBB library"), 0); return 13; } + loadBB = (PLOAD_EGBB) GetProcAddress(lib, "load_egbb_xmen"); + probeBB = (PPROBE_EGBB) GetProcAddress(lib, "probe_egbb_xmen"); + if(!loadBB || !probeBB) { DisplayError(_("wrong EGBB version"), 0); return 13; } + p[1] = NULLCHAR; loadBB(buf, 64*1028, 2); // 2 = SMART_LOAD + loaded = 1; // success! + } + res = probeBB(forwardMostMove & 1, pieces, squares); + return res > 0 ? 1 : res < 0 ? -1 : 0; +} + char * SendMoveToBookUser (int moveNr, ChessProgramState *cps, int initial) { // [HGM] book: this routine intercepts moves to simulate book replies char *bookHit = NULL; + if(cps->drawDepth && BitbaseProbe() == 0) { // [HG} egbb: reduce depth in drawn position + char buf[MSG_SIZ]; + snprintf(buf, MSG_SIZ, "sd %d\n", cps->drawDepth); + SendToProgram(buf, cps); + } //first determine if the incoming move brings opponent into his book if(appData.usePolyglotBook && (cps == &first ? !appData.firstHasOwnBookUCI : !appData.secondHasOwnBookUCI)) bookHit = ProbeBook(moveNr+1, appData.polyglotBook); // returns move @@ -8546,7 +8662,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) { @@ -8590,7 +8706,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 ) { @@ -8599,8 +8715,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; } @@ -8644,7 +8760,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 @@ -8738,7 +8853,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) { @@ -9019,6 +9137,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. @@ -9705,7 +9827,7 @@ ParseGameHistory (char *game) default: break; case MT_CHECK: - if(gameInfo.variant != VariantShogi) + if(!IS_SHOGI(gameInfo.variant)) strcat(parseList[boardIndex - 1], "+"); break; case MT_CHECKMATE: @@ -9739,11 +9861,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; @@ -10147,7 +10269,7 @@ MakeMove (int fromX, int fromY, int toX, int toY, int promoChar) default: break; case MT_CHECK: - if(gameInfo.variant != VariantShogi) + if(!IS_SHOGI(gameInfo.variant)) strcat(parseList[forwardMostMove - 1], "+"); break; case MT_CHECKMATE: @@ -10310,6 +10432,7 @@ InitChessProgram (ChessProgramState *cps, int setup) SendToProgram(buf, cps); } + setboardSpoiledMachineBlack = FALSE; SendToProgram(cps->initString, cps); if (gameInfo.variant != VariantNormal && gameInfo.variant != VariantLoadable @@ -10721,7 +10844,9 @@ SwapEngines (int n) SWAP(fenOverride, p) SWAP(NPS, h) SWAP(accumulateTC, h) + SWAP(drawDepth, h) SWAP(host, p) + SWAP(pseudo, h) } int @@ -11272,8 +11397,7 @@ GameEnds (ChessMove result, char *resultDetails, int whosays) ExitAnalyzeMode(); DoSleep( appData.delayBeforeQuit ); SendToProgram("quit\n", &first); - DoSleep( appData.delayAfterQuit ); - DestroyChildProcess(first.pr, first.useSigterm); + DestroyChildProcess(first.pr, 4 + first.useSigterm); first.reload = TRUE; } first.pr = NoProc; @@ -11298,8 +11422,7 @@ GameEnds (ChessMove result, char *resultDetails, int whosays) if (second.pr != NoProc) { DoSleep( appData.delayBeforeQuit ); SendToProgram("quit\n", &second); - DoSleep( appData.delayAfterQuit ); - DestroyChildProcess(second.pr, second.useSigterm); + DestroyChildProcess(second.pr, 4 + second.useSigterm); second.reload = TRUE; } second.pr = NoProc; @@ -12116,22 +12239,40 @@ void PackMove (int fromX, int fromY, int toX, int toY, ChessSquare promoPiece) { int sq = fromX + (fromY<<4); - int piece = quickBoard[sq]; + int piece = quickBoard[sq], rook; quickBoard[sq] = 0; moveDatabase[movePtr].to = pieceList[piece] = sq = toX + (toY<<4); - if(piece == pieceList[1] && fromY == toY && (toX > fromX+1 || toX < fromX-1) && fromX != BOARD_LEFT && fromX != BOARD_RGHT-1) { + if(piece == pieceList[1] && fromY == toY) { + if((toX > fromX+1 || toX < fromX-1) && fromX != BOARD_LEFT && fromX != BOARD_RGHT-1) { int from = toX>fromX ? BOARD_RGHT-1 : BOARD_LEFT; moveDatabase[movePtr++].piece = Q_WCASTL; quickBoard[sq] = piece; piece = quickBoard[from]; quickBoard[from] = 0; moveDatabase[movePtr].to = pieceList[piece] = sq = toX>fromX ? sq-1 : sq+1; + } else if((rook = quickBoard[sq]) && pieceType[rook] == WhiteRook) { // FRC castling + quickBoard[sq] = 0; // remove Rook + moveDatabase[movePtr].to = sq = (toX>fromX ? BOARD_RGHT-2 : BOARD_LEFT+2); // King to-square + moveDatabase[movePtr++].piece = Q_WCASTL; + quickBoard[sq] = pieceList[1]; // put King + piece = rook; + moveDatabase[movePtr].to = pieceList[rook] = sq = toX>fromX ? sq-1 : sq+1; + } } else - if(piece == pieceList[2] && fromY == toY && (toX > fromX+1 || toX < fromX-1) && fromX != BOARD_LEFT && fromX != BOARD_RGHT-1) { + if(piece == pieceList[2] && fromY == toY) { + if((toX > fromX+1 || toX < fromX-1) && fromX != BOARD_LEFT && fromX != BOARD_RGHT-1) { int from = (toX>fromX ? BOARD_RGHT-1 : BOARD_LEFT) + (BOARD_HEIGHT-1 <<4); moveDatabase[movePtr++].piece = Q_BCASTL; quickBoard[sq] = piece; piece = quickBoard[from]; quickBoard[from] = 0; moveDatabase[movePtr].to = pieceList[piece] = sq = toX>fromX ? sq-1 : sq+1; + } else if((rook = quickBoard[sq]) && pieceType[rook] == BlackRook) { // FRC castling + quickBoard[sq] = 0; // remove Rook + moveDatabase[movePtr].to = sq = (toX>fromX ? BOARD_RGHT-2 : BOARD_LEFT+2); + moveDatabase[movePtr++].piece = Q_BCASTL; + quickBoard[sq] = pieceList[2]; // put King + piece = rook; + moveDatabase[movePtr].to = pieceList[rook] = sq = toX>fromX ? sq-1 : sq+1; + } } else if(epOK && (pieceType[piece] == WhitePawn || pieceType[piece] == BlackPawn) && fromX != toX && quickBoard[sq] == 0) { quickBoard[(fromY<<4)+toX] = 0; @@ -13851,6 +13992,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) { @@ -13877,14 +14019,12 @@ ExitEvent (int status) DoSleep( appData.delayBeforeQuit ); SendToProgram("quit\n", &first); - DoSleep( appData.delayAfterQuit ); - DestroyChildProcess(first.pr, 10 /* [AS] first.useSigterm */ ); + DestroyChildProcess(first.pr, 4 + first.useSigterm /* [AS] first.useSigterm */ ); } if (second.pr != NoProc) { DoSleep( appData.delayBeforeQuit ); SendToProgram("quit\n", &second); - DoSleep( appData.delayAfterQuit ); - DestroyChildProcess(second.pr, 10 /* [AS] second.useSigterm */ ); + DestroyChildProcess(second.pr, 4 + second.useSigterm /* [AS] second.useSigterm */ ); } if (first.isr != NULL) { RemoveInputSource(first.isr); @@ -14115,7 +14255,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(); @@ -17547,7 +17690,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 ) @@ -17655,7 +17798,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; @@ -17679,7 +17822,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; @@ -17694,6 +17837,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=='+') { @@ -17732,7 +17885,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 */ @@ -17745,18 +17900,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 */ @@ -17805,7 +17989,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; @@ -17827,6 +18011,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--); @@ -17841,6 +18027,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 */ @@ -17881,7 +18069,9 @@ ParseFEN (Board board, int *blackPlaysFirst, char *fen, Boolean autoSize) } for(i=0; i