X-Git-Url: http://winboard.nl/cgi-bin?a=blobdiff_plain;f=backend.c;h=ec6b5fc5ec417819cf803edbe3f1bc0a7bce69db;hb=38d1b8471964ebf9d4da576d74a452dc3a12b057;hp=0e0f80eec18f95d78e9a46defdb1b336a71bd612;hpb=2cbef99e7202b7af01c5fef277501fc5baa7b50a;p=xboard.git diff --git a/backend.c b/backend.c index 0e0f80e..ec6b5fc 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 @@ -225,14 +244,13 @@ void DisplayTwoMachinesTitle P(()); static void ExcludeClick P((int index)); void ToggleSecond P((void)); void PauseEngine P((ChessProgramState *cps)); -static int NonStandardBoardSize P((void)); +static int NonStandardBoardSize P((VariantClass v, int w, int h, int s)); #ifdef WIN32 extern void ConsoleCreate(); #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 @@ -275,6 +293,7 @@ ChessSquare pieceSweep = EmptySquare; ChessSquare promoSweep = EmptySquare, defaultPromoChoice; int promoDefaultAltered; int keepInfo = 0; /* [HGM] to protect PGN tags in auto-step game analysis */ +static int initPing = -1; /* States for ics_getting_history */ #define H_FALSE 0 @@ -566,6 +585,13 @@ ChessSquare aseanArray[2][BOARD_FILES] = { /* [HGM] (movGen knows about Shatranj BlackKing, BlackMan, BlackKnight, BlackRook } }; +ChessSquare lionArray[2][BOARD_FILES] = { + { WhiteRook, WhiteLion, WhiteBishop, WhiteQueen, + WhiteKing, WhiteBishop, WhiteKnight, WhiteRook }, + { BlackRook, BlackLion, BlackBishop, BlackQueen, + BlackKing, BlackBishop, BlackKnight, BlackRook } +}; + #if (BOARD_FILES>=10) ChessSquare ShogiArray[2][BOARD_FILES] = { @@ -610,6 +636,13 @@ ChessSquare GrandArray[2][BOARD_FILES] = { BlackMarshall, BlackAngel, BlackBishop, BlackKnight, EmptySquare } }; +ChessSquare ChuChessArray[2][BOARD_FILES] = { + { WhiteMan, WhiteKnight, WhiteBishop, WhiteCardinal, WhiteLion, + WhiteQueen, WhiteDragon, WhiteBishop, WhiteKnight, WhiteMan }, + { BlackMan, BlackKnight, BlackBishop, BlackDragon, BlackQueen, + BlackLion, BlackCardinal, BlackBishop, BlackKnight, BlackMan } +}; + #ifdef GOTHIC ChessSquare GothicArray[2][BOARD_FILES] = { { WhiteRook, WhiteKnight, WhiteBishop, WhiteQueen, WhiteMarshall, @@ -744,8 +777,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); @@ -830,6 +862,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; @@ -1195,6 +1228,8 @@ InitBackEnd1 () case VariantSChess: /* S-Chess, should work */ case VariantGrand: /* should work */ case VariantSpartan: /* should work */ + case VariantLion: /* should work */ + case VariantChuChess: /* should work */ break; } } @@ -1547,6 +1582,23 @@ InitBackEnd3 P((void)) char buf[MSG_SIZ]; int err, len; + if(!appData.icsActive && !appData.noChessProgram && !appData.matchMode && // mode involves only first engine + !strcmp(appData.variant, "normal") && // no explicit variant request + appData.NrRanks == -1 && appData.NrFiles == -1 && appData.holdingsSize == -1 && // no size overrides requested + !SupportedVariant(first.variants, VariantNormal, 8, 8, 0, first.protocolVersion, "") && // but 'normal' won't work with engine + !SupportedVariant(first.variants, VariantFischeRandom, 8, 8, 0, first.protocolVersion, "") ) { // nor will Chess960 + char c, *q = first.variants, *p = strchr(q, ','); + if(p) *p = NULLCHAR; + if(StringToVariant(q) != VariantUnknown) { // the engine can play a recognized variant, however + int w, h, s; + if(sscanf(q, "%dx%d+%d_%c", &w, &h, &s, &c) == 4) // get size overrides the engine needs with it (if any) + appData.NrFiles = w, appData.NrRanks = h, appData.holdingsSize = s, q = strchr(q, '_') + 1; + ASSIGN(appData.variant, q); // fake user requested the first variant played by the engine + Reset(TRUE, FALSE); // and re-initialize + } + if(p) *p = ','; + } + InitChessProgram(&first, startedFromSetupPosition); if(!appData.noChessProgram) { /* [HGM] tidy: redo program version to use name from myname feature */ @@ -2059,7 +2111,8 @@ StringToVariant (char *e) found = TRUE; } else for (i=0; i= VariantShogi && isalpha(p[strlen(variantNames[i])])) continue; v = (VariantClass) i; found = TRUE; break; @@ -4778,7 +4831,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: @@ -5008,6 +5061,11 @@ SendMoveToProgram (int moveNum, ChessProgramState *cps) char buf[MSG_SIZ]; if(moveList[moveNum][1] == '@' && moveList[moveNum][0] == '@') { + if(gameInfo.variant == VariantLion || gameInfo.variant == VariantChuChess || gameInfo.variant == VariantChu) { + sprintf(buf, "%s@@@@\n", cps->useUsermove ? "usermove " : ""); + SendToProgram(buf, cps); + return; + } // null move in variant where engine does not understand it (for analysis purposes) SendBoard(cps, moveNum + 1); // send position after move in stead. return; @@ -5050,16 +5108,18 @@ SendMoveToProgram (int moveNum, ChessProgramState *cps) } else SendToProgram(moveList[moveNum], cps); } else + if(moveList[moveNum][4] == ';') { // [HGM] lion: move is double-step over intermediate square + snprintf(buf, MSG_SIZ, "%c%d%c%d,%c%d%c%d\n", moveList[moveNum][0], moveList[moveNum][1] - '0', // convert to two moves + moveList[moveNum][5], moveList[moveNum][6] - '0', + moveList[moveNum][5], moveList[moveNum][6] - '0', + moveList[moveNum][2], moveList[moveNum][3] - '0'); + SendToProgram(buf, cps); + } else if(BOARD_HEIGHT > 10) { // [HGM] big: convert ranks to double-digit where needed if(moveList[moveNum][1] == '@' && (BOARD_HEIGHT < 16 || moveList[moveNum][0] <= 'Z')) { // drop move if(moveList[moveNum][0]== '@') snprintf(buf, MSG_SIZ, "@@@@\n"); else snprintf(buf, MSG_SIZ, "%c@%c%d%s", moveList[moveNum][0], moveList[moveNum][2], moveList[moveNum][3] - '0', moveList[moveNum]+4); - } else if(moveList[moveNum][4] == ';') { // [HGM] lion: move is double-step over intermediate square - snprintf(buf, MSG_SIZ, "%c%d%c%d,%c%d%c%d\n", moveList[moveNum][0], moveList[moveNum][1] - '0', // convert to two moves - moveList[moveNum][5], moveList[moveNum][6] - '0', - moveList[moveNum][5], moveList[moveNum][6] - '0', - moveList[moveNum][2], moveList[moveNum][3] - '0'); } else snprintf(buf, MSG_SIZ, "%c%d%c%d%s", moveList[moveNum][0], moveList[moveNum][1] - '0', moveList[moveNum][2], moveList[moveNum][3] - '0', moveList[moveNum]+4); @@ -5235,7 +5295,7 @@ UploadGameEvent () SendToICS(ics_type == ICS_ICC ? "tag result Game in progress\n" : "commit\n"); } -static int killX = -1, killY = -1; // [HGM] lion: used for passing e.p. capture square to MakeMove +int killX = -1, killY = -1; // [HGM] lion: used for passing e.p. capture square to MakeMove void CoordsToComputerAlgebraic (int rf, int ff, int rt, int ft, char promoChar, char move[7]) @@ -5269,28 +5329,43 @@ ProcessICSInitScript (FILE *f) } -static int lastX, lastY, lastLeftX, lastLeftY, selectFlag, dragging; +static int lastX, lastY, lastLeftX, lastLeftY, selectFlag; +int dragging; static ClickType lastClickType; +int +Partner (ChessSquare *p) +{ // change piece into promotion partner if one shogi-promotes to the other + int stride = gameInfo.variant == VariantChu ? 22 : 11; + ChessSquare partner; + partner = (*p/stride & 1 ? *p - stride : *p + stride); + if(PieceToChar(*p) != '+' && PieceToChar(partner) != '+') return 0; + *p = partner; + return 1; +} + void Sweep (int step) { ChessSquare king = WhiteKing, pawn = WhitePawn, last = promoSweep; + static int toggleFlag; if(gameInfo.variant == VariantKnightmate) king = WhiteUnicorn; if(gameInfo.variant == VariantSuicide || gameInfo.variant == VariantGiveaway) king = EmptySquare; if(promoSweep >= BlackPawn) king = WHITE_TO_BLACK king, pawn = WHITE_TO_BLACK pawn; if(gameInfo.variant == VariantSpartan && pawn == BlackPawn) pawn = BlackLance, king = EmptySquare; - if(fromY != BOARD_HEIGHT-2 && fromY != 1) pawn = EmptySquare; + if(fromY != BOARD_HEIGHT-2 && fromY != 1 && gameInfo.variant != VariantChuChess) pawn = EmptySquare; + if(!step) toggleFlag = Partner(&last); // piece has shogi-promotion do { - promoSweep -= step; + if(step && !(toggleFlag && Partner(&promoSweep))) promoSweep -= step; if(promoSweep == EmptySquare) promoSweep = BlackPawn; // wrap else if((int)promoSweep == -1) promoSweep = WhiteKing; else if(promoSweep == BlackPawn && step < 0) promoSweep = WhitePawn; else if(promoSweep == WhiteKing && step > 0) promoSweep = BlackKing; if(!step) step = -1; } while(PieceToChar(promoSweep) == '.' || PieceToChar(promoSweep) == '~' || promoSweep == pawn || - appData.testLegality && (promoSweep == king || - IS_SHOGI(gameInfo.variant) && promoSweep != CHUPROMOTED last && last != CHUPROMOTED promoSweep && last != promoSweep)); + !toggleFlag && PieceToChar(promoSweep) == '+' || // skip promoted versions of other + appData.testLegality && (promoSweep == king || gameInfo.variant != VariantChuChess && + (promoSweep == WhiteLion || promoSweep == BlackLion))); if(toX >= 0) { int victim = boards[currentMove][toY][toX]; boards[currentMove][toY][toX] = promoSweep; @@ -5394,6 +5469,7 @@ ParseOneMove (char *move, int moveNum, ChessMove *moveType, int *fromX, int *fro case WhiteNonPromotion: case BlackNonPromotion: case NormalMove: + case FirstLeg: case WhiteCapturesEnPassant: case BlackCapturesEnPassant: case WhiteKingSideCastle: @@ -5427,7 +5503,8 @@ ParseOneMove (char *move, int moveNum, ChessMove *moveType, int *fromX, int *fro if (appData.testLegality) { return (*moveType != IllegalMove); } else { - return !(*fromX == *toX && *fromY == *toY) && boards[moveNum][*fromY][*fromX] != EmptySquare && + return !(*fromX == *toX && *fromY == *toY && killX < 0) && boards[moveNum][*fromY][*fromX] != EmptySquare && + // [HGM] lion: if this is a double move we are less critical WhiteOnMove(moveNum) == (boards[moveNum][*fromY][*fromX] < BlackPawn); } @@ -5570,6 +5647,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; @@ -6031,6 +6111,16 @@ InitPosition (int redraw) pieces = SpartanArray; SetCharTable(pieceToChar, "PNBRQ................K......lwg.....c...h..k"); break; + case VariantLion: + pieces = lionArray; + SetCharTable(pieceToChar, "PNBRQ................LKpnbrq................lk"); + break; + case VariantChuChess: + pieces = ChuChessArray; + gameInfo.boardWidth = 10; + gameInfo.boardHeight = 10; + SetCharTable(pieceToChar, "PNBRQ.....M.+++......LKpnbrq.....m.+++......lk"); + break; case VariantFairy: pieces = fairyArray; SetCharTable(pieceToChar, "PNBRQFEACWMOHIJGDVLSUKpnbrqfeacwmohijgdvlsuk"); @@ -6085,7 +6175,8 @@ InitPosition (int redraw) pawnRow = gameInfo.boardHeight - 7; /* seems to work in all common variants */ if(pawnRow < 1) pawnRow = 1; - if(gameInfo.variant == VariantMakruk || gameInfo.variant == VariantASEAN || gameInfo.variant == VariantGrand) pawnRow = 2; + if(gameInfo.variant == VariantMakruk || gameInfo.variant == VariantASEAN || + gameInfo.variant == VariantGrand || gameInfo.variant == VariantChuChess) pawnRow = 2; if(gameInfo.variant == VariantChu) pawnRow = 3; /* User pieceToChar list overrules defaults */ @@ -6100,7 +6191,7 @@ InitPosition (int redraw) initialPosition[i][j] = s; if(j < BOARD_LEFT || j >= BOARD_RGHT || overrule) continue; - initialPosition[gameInfo.variant == VariantGrand][j] = pieces[0][j-gameInfo.holdingsWidth]; + initialPosition[gameInfo.variant == VariantGrand || gameInfo.variant == VariantChuChess][j] = pieces[0][j-gameInfo.holdingsWidth]; initialPosition[pawnRow][j] = WhitePawn; initialPosition[BOARD_HEIGHT-pawnRow-1][j] = gameInfo.variant == VariantSpartan ? BlackLance : BlackPawn; if(gameInfo.variant == VariantXiangqi) { @@ -6122,14 +6213,15 @@ InitPosition (int redraw) initialPosition[BOARD_HEIGHT-1-i][j] = pieces[2*i+1][j-gameInfo.holdingsWidth]; } } - if(gameInfo.variant == VariantGrand) { + if(gameInfo.variant == VariantGrand || gameInfo.variant == VariantChuChess) { if(j==BOARD_LEFT || j>=BOARD_RGHT-1) { initialPosition[0][j] = WhiteRook; initialPosition[BOARD_HEIGHT-1][j] = BlackRook; } } - initialPosition[BOARD_HEIGHT-1-(gameInfo.variant == VariantGrand)][j] = pieces[1][j-gameInfo.holdingsWidth]; + initialPosition[BOARD_HEIGHT-1-(gameInfo.variant == VariantGrand || gameInfo.variant == VariantChuChess)][j] = pieces[1][j-gameInfo.holdingsWidth]; } + if(gameInfo.variant == VariantChuChess) initialPosition[0][BOARD_WIDTH/2] = WhiteKing, initialPosition[BOARD_HEIGHT-1][BOARD_WIDTH/2-1] = BlackKing; if( (gameInfo.variant == VariantShogi) && !overrule ) { j=BOARD_LEFT+1; @@ -6407,7 +6499,7 @@ HasPromotionChoice (int fromX, int fromY, int toX, int toY, char *promoChoice, i /* [HGM] rewritten IsPromotion to only flag promotions that offer a choice */ /* [HGM] add Shogi promotions */ int promotionZoneSize=1, highestPromotingPiece = (int)WhitePawn; - ChessSquare piece; + ChessSquare piece, partner; ChessMove moveType; Boolean premove; @@ -6422,16 +6514,16 @@ HasPromotionChoice (int fromX, int fromY, int toX, int toY, char *promoChoice, i if(gameInfo.variant == VariantChu) { int p = piece >= BlackPawn ? BLACK_TO_WHITE piece : piece; promotionZoneSize = BOARD_HEIGHT/3; - highestPromotingPiece = (p >= WhiteLion || PieceToChar(piece + 22) == '.') ? WhitePawn : WhiteKing; - } else if(gameInfo.variant == VariantShogi) { + highestPromotingPiece = (p >= WhiteLion || PieceToChar(piece + 22) == '.') ? WhitePawn : WhiteLion; + } else if(gameInfo.variant == VariantShogi || gameInfo.variant == VariantChuChess) { promotionZoneSize = BOARD_HEIGHT/3; highestPromotingPiece = (int)WhiteAlfil; - } else if(gameInfo.variant == VariantMakruk || gameInfo.variant == VariantGrand) { + } else if(gameInfo.variant == VariantMakruk || gameInfo.variant == VariantGrand || gameInfo.variant == VariantChuChess) { 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; } @@ -6440,10 +6532,13 @@ HasPromotionChoice (int fromX, int fromY, int toX, int toY, char *promoChoice, i if((int)piece >= BlackPawn) { if(toY >= promotionZoneSize && fromY >= promotionZoneSize) return FALSE; + if(fromY < promotionZoneSize && gameInfo.variant == VariantChuChess) return FALSE; highestPromotingPiece = WHITE_TO_BLACK highestPromotingPiece; } else { if( toY < BOARD_HEIGHT - promotionZoneSize && fromY < BOARD_HEIGHT - promotionZoneSize) return FALSE; + if(fromY >= BOARD_HEIGHT - promotionZoneSize && gameInfo.variant == VariantChuChess) + return FALSE; } if( (int)piece > highestPromotingPiece ) return FALSE; // non-promoting piece @@ -6479,7 +6574,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... @@ -6489,7 +6588,9 @@ HasPromotionChoice (int fromX, int fromY, int toX, int toY, char *promoChoice, i } // give caller the default choice even if we will not make it *promoChoice = ToLower(PieceToChar(defaultPromoChoice)); - if(IS_SHOGI(gameInfo.variant)) *promoChoice = (defaultPromoChoice == piece ? '=' : '+'); + partner = piece; // pieces can promote if the pieceToCharTable says so + if(IS_SHOGI(gameInfo.variant)) *promoChoice = (defaultPromoChoice == piece && sweepSelect ? '=' : '+'); // obsolete? + else if(Partner(&partner)) *promoChoice = (defaultPromoChoice == piece && sweepSelect ? NULLCHAR : '+'); if( sweepSelect && gameInfo.variant != VariantGreat && gameInfo.variant != VariantGrand && gameInfo.variant != VariantSuper) return FALSE; @@ -6500,7 +6601,8 @@ HasPromotionChoice (int fromX, int fromY, int toX, int toY, char *promoChoice, i gameMode == IcsPlayingBlack && WhiteOnMove(currentMove); if(appData.testLegality && !premove) { moveType = LegalityTest(boards[currentMove], PosFlags(currentMove), - fromY, fromX, toY, toX, IS_SHOGI(gameInfo.variant) ? '+' : NULLCHAR); + fromY, fromX, toY, toX, IS_SHOGI(gameInfo.variant) || gameInfo.variant == VariantChuChess ? '+' : NULLCHAR); + if(moveType == IllegalMove) *promoChoice = NULLCHAR; // could be the fact we promoted was illegal if(moveType != WhitePromotion && moveType != BlackPromotion) return FALSE; } @@ -7101,24 +7203,29 @@ MarkByFEN(char *fen) DrawPosition(TRUE, NULL); } +static char baseMarker[BOARD_RANKS][BOARD_FILES], baseLegal[BOARD_RANKS][BOARD_FILES]; + void Mark (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure) { typedef char Markers[BOARD_RANKS][BOARD_FILES]; Markers *m = (Markers *) closure; - if(rf == fromY && ff == fromX) + if(rf == fromY && ff == fromX && (killX < 0 && !(rt == rf && ft == ff) || abs(ft-killX) < 2 && abs(rt-killY) < 2)) (*m)[rt][ft] = 1 + (board[rt][ft] != EmptySquare || kind == WhiteCapturesEnPassant - || kind == BlackCapturesEnPassant); + || kind == BlackCapturesEnPassant) + 3*(kind == FirstLeg && killX < 0); else if(flags & F_MANDATORY_CAPTURE && board[rt][ft] != EmptySquare) (*m)[rt][ft] = 3; } +static int hoverSavedValid; + void MarkTargetSquares (int clear) { int x, y, sum=0; if(clear) { // no reason to ever suppress clearing for(x=0; x= BOARD_HEIGHT-1-zone || piece == BlackLance && y == 1 || piece == WhiteLance && y == BOARD_HEIGHT-2 ); } @@ -7168,19 +7276,19 @@ CanPromote (ChessSquare piece, int y) void HoverEvent (int xPix, int yPix, int x, int y) { - static char baseMarker[BOARD_RANKS][BOARD_FILES], baseLegal[BOARD_RANKS][BOARD_FILES]; static int oldX = -1, oldY = -1, oldFromX = -1, oldFromY = -1; int r, f; - if(dragging == 2) DragPieceMove(xPix, yPix); // [HGM] lion: drag without button for second leg if(!first.highlight) return; if(fromX != oldFromX || fromY != oldFromY) oldX = oldY = -1; // kludge to fake entry on from-click if(x == oldX && y == oldY) return; // only do something if we enter new square oldFromX = fromX; oldFromY = fromY; - if(oldX == -1 && oldY == -1 && x == fromX && y == fromY) // record markings after from-change + if(oldX == -1 && oldY == -1 && x == fromX && y == fromY) { // record markings after from-change 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 @@ -8265,7 +8441,7 @@ HandleMachineMove (char *message, ChessProgramState *cps) char realname[MSG_SIZ]; int fromX, fromY, toX, toY; ChessMove moveType; - char promoChar; + char promoChar, roar; char *p, *pv=buf1; int machineWhite, oldError; char *bookHit; @@ -8427,8 +8603,8 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h snprintf(buf1, MSG_SIZ*10, _("Illegal move \"%s\" from %s machine"), machineMove, _(cps->which)); DisplayMoveError(buf1); - snprintf(buf1, MSG_SIZ*10, "Xboard: Forfeit due to invalid move: %s (%c%c%c%c) res=%d", - machineMove, fromX+AAA, fromY+ONE, toX+AAA, toY+ONE, moveType); + snprintf(buf1, MSG_SIZ*10, "Xboard: Forfeit due to invalid move: %s (%c%c%c%c via %c%c) res=%d", + machineMove, fromX+AAA, fromY+ONE, toX+AAA, toY+ONE, killX+AAA, killY+ONE, moveType); if (gameMode == TwoMachinesPlay) { GameEnds(machineWhite ? BlackWins : WhiteWins, buf1, GE_XBOARD); @@ -8581,10 +8757,12 @@ if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats. cps->other->maybeThinking = TRUE; } + roar = (killX >= 0 && IS_LION(boards[forwardMostMove][toY][toX])); + ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/ if (!pausing && appData.ringBellAfterMoves) { - RingBell(); + if(!roar) RingBell(); } /* @@ -8635,7 +8813,8 @@ if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats. } if (!strncmp(message, "setup ", 6) && - (!appData.testLegality || gameInfo.variant == VariantFairy || gameInfo.variant == VariantUnknown || NonStandardBoardSize()) + (!appData.testLegality || gameInfo.variant == VariantFairy || gameInfo.variant == VariantUnknown || + NonStandardBoardSize(gameInfo.variant, gameInfo.boardWidth, gameInfo.boardHeight, gameInfo.holdingsSize)) ) { // [HGM] allow first engine to define opening position int dummy, w, h, hand, s=6; char buf[MSG_SIZ], varName[MSG_SIZ]; if(appData.icsActive || forwardMostMove != 0 || cps != &first) return; @@ -8766,6 +8945,14 @@ if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats. } } if (sscanf(message, "pong %d", &cps->lastPong) == 1) { + if(initPing == cps->lastPong) { + if(gameInfo.variant == VariantUnknown) { + DisplayError(_("Engine did not send setup for non-standard variant"), 0); + *engineVariant = NULLCHAR; appData.variant = VariantNormal; // back to normal as error recovery? + GameEnds(GameUnfinished, NULL, GE_XBOARD); + } + initPing = -1; + } return; } if(!strncmp(message, "highlight ", 10)) { @@ -9475,6 +9662,7 @@ ParseGameHistory (char *game) case WhiteNonPromotion: case BlackNonPromotion: case NormalMove: + case FirstLeg: case WhiteCapturesEnPassant: case BlackCapturesEnPassant: case WhiteKingSideCastle: @@ -9598,7 +9786,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: @@ -9615,7 +9803,7 @@ void ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board) { ChessSquare captured = board[toY][toX], piece, king; int p, oldEP = EP_NONE, berolina = 0; - int promoRank = gameInfo.variant == VariantMakruk || gameInfo.variant == VariantGrand ? 3 : 1; + int promoRank = gameInfo.variant == VariantMakruk || gameInfo.variant == VariantGrand || gameInfo.variant == VariantChuChess ? 3 : 1; /* [HGM] compute & store e.p. status and castling rights for new position */ /* we can always do that 'in place', now pointers to these rights are passed to ApplyMove */ @@ -9632,14 +9820,22 @@ ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board) } piece = board[toY][toX] = (ChessSquare) fromX; } else { + ChessSquare victim; int i; if( killX >= 0 && killY >= 0 ) // [HGM] lion: Lion trampled over something + victim = board[killY][killX], board[killY][killX] = EmptySquare, board[EP_STATUS] = EP_CAPTURE; - if( board[toY][toX] != EmptySquare ) + if( board[toY][toX] != EmptySquare ) { board[EP_STATUS] = EP_CAPTURE; + if( (fromX != toX || fromY != toY) && // not igui! + (captured == WhiteLion && board[fromY][fromX] != BlackLion || + captured == BlackLion && board[fromY][fromX] != WhiteLion ) ) { // [HGM] lion: Chu Lion-capture rules + board[EP_STATUS] = EP_IRON_LION; // non-Lion x Lion: no counter-strike allowed + } + } if( board[fromY][fromX] == WhiteLance || board[fromY][fromX] == BlackLance ) { if( gameInfo.variant != VariantSuper && gameInfo.variant != VariantShogi ) @@ -9727,7 +9923,7 @@ ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board) board[toY][toX+1] = board[fromY][BOARD_LEFT]; board[fromY][BOARD_LEFT] = EmptySquare; } else if ((board[fromY][fromX] == WhitePawn && gameInfo.variant != VariantXiangqi || - board[fromY][fromX] == WhiteLance && gameInfo.variant != VariantSuper && gameInfo.variant != VariantShogi) + board[fromY][fromX] == WhiteLance && gameInfo.variant != VariantSuper && gameInfo.variant != VariantChu) && toY >= BOARD_HEIGHT-promoRank && promoChar // defaulting to Q is done elsewhere ) { /* white pawn promotion */ @@ -9788,7 +9984,7 @@ ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board) board[fromY][0] = EmptySquare; board[toY][2] = BlackRook; } else if ((board[fromY][fromX] == BlackPawn && gameInfo.variant != VariantXiangqi || - board[fromY][fromX] == BlackLance && gameInfo.variant != VariantSuper && gameInfo.variant != VariantShogi) + board[fromY][fromX] == BlackLance && gameInfo.variant != VariantSuper && gameInfo.variant != VariantChu) && toY < promoRank && promoChar ) { /* black pawn promotion */ @@ -9901,12 +10097,15 @@ ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board) board[toY][toX] = EmptySquare; } } + if(gameInfo.variant == VariantSChess && promoChar != NULLCHAR && promoChar != '=' && piece != WhitePawn && piece != BlackPawn) { board[fromY][fromX] = CharToPiece(piece < BlackPawn ? ToUpper(promoChar) : ToLower(promoChar)); // S-Chess gating } else if(promoChar == '+') { /* [HGM] Shogi-style promotions, to piece implied by original (Might overwrite ordinary Pawn promotion) */ board[toY][toX] = (ChessSquare) (CHUPROMOTED piece); + if(gameInfo.variant == VariantChuChess && (piece == WhiteKnight || piece == BlackKnight)) + board[toY][toX] = piece + WhiteLion - WhiteKnight; // adjust Knight promotions to Lion } else if(!appData.testLegality && promoChar != NULLCHAR && promoChar != '=') { // without legality testing, unconditionally believe promoChar ChessSquare newPiece = CharToPiece(piece < BlackPawn ? ToUpper(promoChar) : ToLower(promoChar)); if((newPiece <= WhiteMan || newPiece >= BlackPawn && newPiece <= BlackMan) // unpromoted piece specified @@ -9925,7 +10124,6 @@ ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board) board[BOARD_HEIGHT-1-k][0] = EmptySquare; } } - } /* Updates forwardMostMove */ @@ -10030,7 +10228,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: @@ -10038,8 +10236,6 @@ MakeMove (int fromX, int fromY, int toX, int toY, int promoChar) strcat(parseList[forwardMostMove - 1], "#"); break; } - - killX = killY = -1; // [HGM] lion: used up } /* Updates currentMove if not pausing */ @@ -10059,6 +10255,8 @@ ShowMove (int fromX, int fromY, int toX, int toY) currentMove = forwardMostMove; } + killX = killY = -1; // [HGM] lion: used up + if (instant) return; DisplayMove(currentMove - 1); @@ -10107,40 +10305,75 @@ SendEgtPath (ChessProgramState *cps) } static int -NonStandardBoardSize () -{ - /* [HGM] Awkward testing. Should really be a table */ - int overruled = gameInfo.boardWidth != 8 || gameInfo.boardHeight != 8 || gameInfo.holdingsSize != 0; - if( gameInfo.variant == VariantUnknown || *engineVariant) return 0; // engine-defined name never needs prefix - if( gameInfo.variant == VariantXiangqi ) - overruled = gameInfo.boardWidth != 9 || gameInfo.boardHeight != 10 || gameInfo.holdingsSize != 0; - if( gameInfo.variant == VariantShogi ) - overruled = gameInfo.boardWidth != 9 || gameInfo.boardHeight != 9 || gameInfo.holdingsSize != 7; - if( gameInfo.variant == VariantBughouse || gameInfo.variant == VariantCrazyhouse ) - overruled = gameInfo.boardWidth != 8 || gameInfo.boardHeight != 8 || gameInfo.holdingsSize != 5; - if( gameInfo.variant == VariantCapablanca || gameInfo.variant == VariantCapaRandom || - gameInfo.variant == VariantGothic || gameInfo.variant == VariantFalcon || gameInfo.variant == VariantJanus ) - overruled = gameInfo.boardWidth != 10 || gameInfo.boardHeight != 8 || gameInfo.holdingsSize != 0; - if( gameInfo.variant == VariantCourier ) - overruled = gameInfo.boardWidth != 12 || gameInfo.boardHeight != 8 || gameInfo.holdingsSize != 0; - if( gameInfo.variant == VariantSuper ) - overruled = gameInfo.boardWidth != 8 || gameInfo.boardHeight != 8 || gameInfo.holdingsSize != 8; - if( gameInfo.variant == VariantGreat ) - overruled = gameInfo.boardWidth != 10 || gameInfo.boardHeight != 8 || gameInfo.holdingsSize != 8; - if( gameInfo.variant == VariantSChess ) - overruled = gameInfo.boardWidth != 8 || gameInfo.boardHeight != 8 || gameInfo.holdingsSize != 7; - if( gameInfo.variant == VariantGrand ) - overruled = gameInfo.boardWidth != 10 || gameInfo.boardHeight != 10 || gameInfo.holdingsSize != 7; - if( gameInfo.variant == VariantChu ) - overruled = gameInfo.boardWidth != 12 || gameInfo.boardHeight != 12 || gameInfo.holdingsSize != 0; - return overruled; +NonStandardBoardSize (VariantClass v, int boardWidth, int boardHeight, int holdingsSize) +{ + int width = 8, height = 8, holdings = 0; // most common sizes + if( v == VariantUnknown || *engineVariant) return 0; // engine-defined name never needs prefix + // correct the deviations default for each variant + if( v == VariantXiangqi ) width = 9, height = 10; + if( v == VariantShogi ) width = 9, height = 9, holdings = 7; + if( v == VariantBughouse || v == VariantCrazyhouse) holdings = 5; + if( v == VariantCapablanca || v == VariantCapaRandom || + v == VariantGothic || v == VariantFalcon || v == VariantJanus ) + width = 10; + if( v == VariantCourier ) width = 12; + if( v == VariantSuper ) holdings = 8; + if( v == VariantGreat ) width = 10, holdings = 8; + if( v == VariantSChess ) holdings = 7; + if( v == VariantGrand ) width = 10, height = 10, holdings = 7; + if( v == VariantChuChess) width = 10, height = 10; + if( v == VariantChu ) width = 12, height = 12; + return boardWidth >= 0 && boardWidth != width || // -1 is default, + boardHeight >= 0 && boardHeight != height || // and thus by definition OK + holdingsSize >= 0 && holdingsSize != holdings; +} + +char variantError[MSG_SIZ]; + +char * +SupportedVariant (char *list, VariantClass v, int boardWidth, int boardHeight, int holdingsSize, int proto, char *engine) +{ // returns error message (recognizable by upper-case) if engine does not support the variant + char *p, *variant = VariantName(v); + static char b[MSG_SIZ]; + if(NonStandardBoardSize(v, boardWidth, boardHeight, holdingsSize)) { /* [HGM] make prefix for non-standard board size. */ + snprintf(b, MSG_SIZ, "%dx%d+%d_%s", boardWidth, boardHeight, + holdingsSize, variant); // cook up sized variant name + /* [HGM] varsize: try first if this deviant size variant is specifically known */ + if(StrStr(list, b) == NULL) { + // specific sized variant not known, check if general sizing allowed + if(proto != 1 && StrStr(list, "boardsize") == NULL) { + snprintf(variantError, MSG_SIZ, "Board size %dx%d+%d not supported by %s", + boardWidth, boardHeight, holdingsSize, engine); + return NULL; + } + /* [HGM] here we really should compare with the maximum supported board size */ + } + } else snprintf(b, MSG_SIZ,"%s", variant); + if(proto == 1) return b; // for protocol 1 we cannot check and hope for the best + p = StrStr(list, b); + while(p && (p != list && p[-1] != ',' || p[strlen(b)] && p[strlen(b)] != ',') ) p = StrStr(p+1, b); + if(p == NULL) { + // occurs not at all in list, or only as sub-string + snprintf(variantError, MSG_SIZ, _("Variant %s not supported by %s"), b, engine); + if(p = StrStr(list, b)) { // handle requesting parent variant when only size-overridden is supported + int l = strlen(variantError); + char *q; + while(p != list && p[-1] != ',') p--; + q = strchr(p, ','); + if(q) *q = NULLCHAR; + snprintf(variantError + l, MSG_SIZ - l, _(", but %s is"), p); + if(q) *q= ','; + } + return NULL; + } + return b; } void InitChessProgram (ChessProgramState *cps, int setup) /* setup needed to setup FRC opening position */ { - char buf[MSG_SIZ], b[MSG_SIZ]; + char buf[MSG_SIZ], *b; if (appData.noChessProgram) return; hintRequested = FALSE; bookRequested = FALSE; @@ -10162,33 +10395,15 @@ InitChessProgram (ChessProgramState *cps, int setup) if (gameInfo.variant != VariantNormal && gameInfo.variant != VariantLoadable /* [HGM] also send variant if board size non-standard */ - || gameInfo.boardWidth != 8 || gameInfo.boardHeight != 8 || gameInfo.holdingsSize != 0 - ) { - char *v = VariantName(gameInfo.variant); - if (cps->protocolVersion != 1 && StrStr(cps->variants, v) == NULL) { - /* [HGM] in protocol 1 we have to assume all variants valid */ - snprintf(buf, MSG_SIZ, _("Variant %s not supported by %s"), v, cps->tidy); - DisplayFatalError(buf, 0, 1); + || gameInfo.boardWidth != 8 || gameInfo.boardHeight != 8 || gameInfo.holdingsSize != 0) { + + b = SupportedVariant(cps->variants, gameInfo.variant, gameInfo.boardWidth, + gameInfo.boardHeight, gameInfo.holdingsSize, cps->protocolVersion, cps->tidy); + if (b == NULL) { + DisplayFatalError(variantError, 0, 1); return; } - if(NonStandardBoardSize()) { /* [HGM] make prefix for non-standard board size. */ - snprintf(b, MSG_SIZ, "%dx%d+%d_%s", gameInfo.boardWidth, gameInfo.boardHeight, - gameInfo.holdingsSize, VariantName(gameInfo.variant)); // cook up sized variant name - /* [HGM] varsize: try first if this defiant size variant is specifically known */ - if(StrStr(cps->variants, b) == NULL) { - // specific sized variant not known, check if general sizing allowed - if (cps->protocolVersion != 1) { // for protocol 1 we cannot check and hope for the best - if(StrStr(cps->variants, "boardsize") == NULL) { - snprintf(buf, MSG_SIZ, "Board size %dx%d+%d not supported by %s", - gameInfo.boardWidth, gameInfo.boardHeight, gameInfo.holdingsSize, cps->tidy); - DisplayFatalError(buf, 0, 1); - return; - } - /* [HGM] here we really should compare with the maximum supported board size */ - } - } - } else snprintf(b, MSG_SIZ,"%s", VariantName(gameInfo.variant)); snprintf(buf, MSG_SIZ, "variant %s\n", b); SendToProgram(buf, cps); } @@ -10228,7 +10443,7 @@ InitChessProgram (ChessProgramState *cps, int setup) SendToProgram("easy\n", cps); } if (cps->usePing) { - snprintf(buf, MSG_SIZ, "ping %d\n", ++cps->lastPing); + snprintf(buf, MSG_SIZ, "ping %d\n", initPing = ++cps->lastPing); SendToProgram(buf, cps); } cps->initDone = TRUE; @@ -10587,6 +10802,7 @@ SwapEngines (int n) SWAP(fenOverride, p) SWAP(NPS, h) SWAP(accumulateTC, h) + SWAP(drawDepth, h) SWAP(host, p) } @@ -10868,7 +11084,7 @@ GameEnds (ChessMove result, char *resultDetails, int whosays) result, resultDetails ? resultDetails : "(null)", whosays); } - fromX = fromY = -1; // [HGM] abort any move the user is entering. + fromX = fromY = killX = killY = -1; // [HGM] abort any move the user is entering. // [HGM] lion if(pausing) PauseEvent(); // can happen when we abort a paused game (New Game or Quit) @@ -11138,8 +11354,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; @@ -11164,8 +11379,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; @@ -11267,7 +11481,8 @@ FeedMovesToProgram (ChessProgramState *cps, int upto) if(currentlyInitializedVariant != gameInfo.variant) { char buf[MSG_SIZ]; // [HGM] variantswitch: make engine aware of new variant - if(cps->protocolVersion > 1 && StrStr(cps->variants, VariantName(gameInfo.variant)) == NULL) + if(!SupportedVariant(cps->variants, gameInfo.variant, gameInfo.boardWidth, + gameInfo.boardHeight, gameInfo.holdingsSize, cps->protocolVersion, "")) return; // [HGM] refrain from feeding moves altogether if variant is unsupported! snprintf(buf, MSG_SIZ, "variant %s\n", VariantName(gameInfo.variant)); SendToProgram(buf, cps); @@ -11364,6 +11579,7 @@ Reset (int redraw, int init) ClearPremoveHighlights(); gotPremove = FALSE; alarmSounded = FALSE; + killX = killY = -1; // [HGM] lion GameEnds(EndOfFile, NULL, GE_PLAYER); if(appData.serverMovesName != NULL) { @@ -11544,6 +11760,7 @@ LoadGameOneMove (ChessMove readAhead) case WhiteNonPromotion: case BlackNonPromotion: case NormalMove: + case FirstLeg: case WhiteKingSideCastle: case WhiteQueenSideCastle: case BlackKingSideCastle: @@ -11565,6 +11782,7 @@ LoadGameOneMove (ChessMove readAhead) toX = currentMoveString[2] - AAA; toY = currentMoveString[3] - ONE; promoChar = currentMoveString[4]; + if(promoChar == ';') promoChar = NULLCHAR; break; case WhiteDrop: @@ -11731,6 +11949,7 @@ LoadGameOneMove (ChessMove readAhead) thinkOutput[0] = NULLCHAR; MakeMove(fromX, fromY, toX, toY, promoChar); + killX = killY = -1; // [HGM] lion: used up currentMove = forwardMostMove; return TRUE; } @@ -11977,22 +12196,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; @@ -12228,6 +12465,7 @@ GameContainsPosition (FILE *f, ListGame *lg) case WhiteNonPromotion: case BlackNonPromotion: case NormalMove: + case FirstLeg: case WhiteKingSideCastle: case WhiteQueenSideCastle: case BlackKingSideCastle: @@ -12292,6 +12530,7 @@ LoadGame (FILE *f, int gameNumber, char *title, int useList) if (gameMode != BeginningOfGame) { Reset(FALSE, TRUE); } + killX = killY = -1; // [HGM] lion: in case we did not Reset gameFileFP = f; if (lastLoadGameFP != NULL && lastLoadGameFP != f) { @@ -12445,6 +12684,7 @@ LoadGame (FILE *f, int gameNumber, char *title, int useList) break; case NormalMove: + case FirstLeg: /* Only a NormalMove can be at the start of a game * without a position diagram. */ if (lastLoadGameStart == EndOfFile ) { @@ -13735,14 +13975,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); @@ -14294,7 +14532,8 @@ TwoMachinesEvent P((void)) } if(WaitForEngine(&second, TwoMachinesEventIfReady)) return; // (if needed:) started up second engine, so wait for features - if(second.protocolVersion >= 2 && !strstr(second.variants, VariantName(gameInfo.variant))) { + if(!SupportedVariant(second.variants, gameInfo.variant, gameInfo.boardWidth, + gameInfo.boardHeight, gameInfo.holdingsSize, second.protocolVersion, second.tidy)) { startingEngine = FALSE; DisplayError("second engine does not play this", 0); return;