X-Git-Url: http://winboard.nl/cgi-bin?a=blobdiff_plain;f=backend.c;h=ca3e342c89807ee7a89750aeef68c9ff008f2ac3;hb=0c082fce711802c67feae5dd1ddadad022a48147;hp=cd344b6c31f5b8c028ba5be8d4fe44e7e0d31032;hpb=2e25ba964f3e2faf1a328b3de269367c3bba8c58;p=xboard.git diff --git a/backend.c b/backend.c index cd344b6..ca3e342 100644 --- a/backend.c +++ b/backend.c @@ -168,8 +168,6 @@ int LoadGameOneMove P((ChessMove readAhead)); int LoadGameFromFile P((char *filename, int n, char *title, int useList)); int LoadPositionFromFile P((char *filename, int n, char *title)); int SavePositionToFile P((char *filename)); -void ApplyMove P((int fromX, int fromY, int toX, int toY, int promoChar, - Board board)); void MakeMove P((int fromX, int fromY, int toX, int toY, int promoChar)); void ShowMove P((int fromX, int fromY, int toX, int toY)); int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY, @@ -204,7 +202,6 @@ void StopClocks P((void)); void ResetClocks P((void)); char *PGNDate P((void)); void SetGameInfo P((void)); -Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen)); int RegisterMove P((void)); void MakeRegisteredMove P((void)); void TruncateGame P((void)); @@ -837,6 +834,7 @@ InitEngine(ChessProgramState *cps, int n) } InitEngineUCI( installDir, cps ); // [HGM] moved here from winboard.c, to make available in xboard + ParseFeatures(appData.featureDefaults, cps); } ChessProgramState *savCps; @@ -1868,7 +1866,7 @@ SendToICS(s) { int count, outCount, outError; - if (icsPR == NULL) return; + if (icsPR == NoProc) return; count = strlen(s); outCount = OutputMaybeTelnet(icsPR, s, count, &outError); @@ -1887,7 +1885,7 @@ SendToICSDelayed(s,msdelay) { int count, outCount, outError; - if (icsPR == NULL) return; + if (icsPR == NoProc) return; count = strlen(s); if (appData.debugMode) { @@ -3900,7 +3898,7 @@ read_from_ics(isr, closure, data, count, error) #if ZIPPY if (appData.zippyPlay && first.initDone) { ZippyGameEnd(endtype, why); - if (first.pr == NULL) { + if (first.pr == NoProc) { /* Start the next process early so that we'll be ready for the next challenge */ StartChessProgram(&first); @@ -5139,7 +5137,7 @@ Sweep(int step) 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; + if(!step) step = -1; } while(PieceToChar(promoSweep) == '.' || PieceToChar(promoSweep) == '~' || promoSweep == pawn || appData.testLegality && (promoSweep == king || gameInfo.variant == VariantShogi && promoSweep != PROMOTED last && last != PROMOTED promoSweep && last != promoSweep)); @@ -7629,8 +7627,10 @@ Adjudicate(ChessProgramState *cps) hisPerpetual = PerpetualChase(k, forwardMostMove); ourPerpetual = PerpetualChase(k+1, forwardMostMove); if(ourPerpetual && !hisPerpetual) { // we are actively chasing him: forfeit + static char resdet[MSG_SIZ]; result = WhiteOnMove(forwardMostMove) ? WhiteWins : BlackWins; - details = "Xboard adjudication: perpetual chasing"; + details = resdet; + snprintf(resdet, MSG_SIZ, "Xboard adjudication: perpetual chasing of %c%c", ourPerpetual>>8, ourPerpetual&255); } else if(hisPerpetual && !ourPerpetual) // he is chasing us, but did not repeat yet break; // Abort repetition-checking loop. @@ -8119,10 +8119,12 @@ if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats. return; // [HGM] This return was missing, causing option features to be recognized as non-compliant commands! } - if (!appData.testLegality && !strncmp(message, "setup ", 6)) { // [HGM] allow first engine to define opening position + if ((!appData.testLegality || gameInfo.variant == VariantFairy) && + !strncmp(message, "setup ", 6)) { // [HGM] allow first engine to define opening position int dummy, s=6; char buf[MSG_SIZ]; - if(appData.icsActive || forwardMostMove != 0 || cps != &first || startedFromSetupPosition) return; + if(appData.icsActive || forwardMostMove != 0 || cps != &first) return; if(sscanf(message, "setup (%s", buf) == 1) s = 8 + strlen(buf), buf[s-9] = NULLCHAR, SetCharTable(pieceToChar, buf); + if(startedFromSetupPosition) return; ParseFEN(boards[0], &dummy, message+s); DrawPosition(TRUE, boards[0]); startedFromSetupPosition = TRUE; @@ -9538,6 +9540,7 @@ InitChessProgram(cps, setup) hintRequested = FALSE; bookRequested = FALSE; + ParseFeatures(appData.features[cps == &second], cps); // [HGM] allow user to overrule features /* [HGM] some new WB protocol commands to configure engine are sent now, if engine supports them */ /* moved to before sending initstring in 4.3.15, so Polyglot can delay UCI 'isready' to recepton of 'new' */ if(cps->memSize) { /* [HGM] memory */ @@ -9924,6 +9927,7 @@ void SwapEngines(int n) SWAP(logo, p) SWAP(pgnName, p) SWAP(pvSAN, h) + SWAP(engOptions, p) } void @@ -10040,7 +10044,7 @@ NextTourneyGame(int nr, int *swapColors) matchGame = 1; roundNr = nr / syncInterval + 1; } - if(first.pr != NoProc) return 1; // engines already loaded + if(first.pr != NoProc || second.pr != NoProc) return 1; // engines already loaded // redefine engines, engine dir, etc. NamesToList(firstChessProgramNames, command, mnemonic); // get mnemonics of installed engines @@ -10648,7 +10652,7 @@ Reset(redraw, init) timeRemaining[0][0] = whiteTimeRemaining; timeRemaining[1][0] = blackTimeRemaining; - if (first.pr == NULL) { + if (first.pr == NoProc) { StartChessProgram(&first); } if (init) { @@ -11173,6 +11177,231 @@ PositionMatches(Board b1, Board b2) return TRUE; } +#define Q_PROMO 4 +#define Q_EP 3 +#define Q_BCASTL 2 +#define Q_WCASTL 1 + +int pieceList[256], quickBoard[256]; +ChessSquare pieceType[256] = { EmptySquare }; +Board soughtBoard, reverseBoard, flipBoard, rotateBoard; +int counts[EmptySquare], minSought[EmptySquare], minReverse[EmptySquare], maxSought[EmptySquare], maxReverse[EmptySquare]; +int soughtTotal, turn; +Boolean epOK, flipSearch; + +typedef struct { + unsigned char piece, to; +} Move; + +#define DSIZE (250000) + +Move initialSpace[DSIZE+1000]; // gamble on that game will not be more than 500 moves +Move *moveDatabase = initialSpace; +unsigned int movePtr, dataSize = DSIZE; + +int MakePieceList(Board board, int *counts) +{ + int r, f, n=Q_PROMO, total=0; + for(r=0;r 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(piece == pieceList[2] && fromY == toY && (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(epOK && (pieceType[piece] == WhitePawn || pieceType[piece] == BlackPawn) && fromX != toX && quickBoard[sq] == 0) { + quickBoard[(fromY<<4)+toX] = 0; + moveDatabase[movePtr].piece = Q_EP; + moveDatabase[movePtr++].to = (fromY<<4)+toX; + moveDatabase[movePtr].to = sq; + } else + if(promoPiece != pieceType[piece]) { + moveDatabase[movePtr++].piece = Q_PROMO; + moveDatabase[movePtr].to = pieceType[piece] = (int) promoPiece; + } + moveDatabase[movePtr].piece = piece; + quickBoard[sq] = piece; + movePtr++; +} + +int PackGame(Board board) +{ + Move *newSpace = NULL; + moveDatabase[movePtr].piece = 0; // terminate previous game + if(movePtr > dataSize) { + if(appData.debugMode) fprintf(debugFP, "move-cache overflow, enlarge to %d MB\n", dataSize/128); + dataSize *= 8; // increase size by factor 8 (512KB -> 4MB -> 32MB -> 256MB -> 2GB) + if(dataSize) newSpace = (Move*) calloc(8*dataSize + 1000, sizeof(Move)); + if(newSpace) { + int i; + Move *p = moveDatabase, *q = newSpace; + for(i=0; i 8*DSIZE) free(moveDatabase); // and free old space (if it was allocated) + moveDatabase = newSpace; + } else { // calloc failed, we must be out of memory. Too bad... + dataSize = 0; // prevent calloc events for all subsequent games + return 0; // and signal this one isn't cached + } + } + movePtr++; + MakePieceList(board, counts); + return movePtr; +} + +int QuickCompare(Board board, int *minCounts, int *maxCounts) +{ // compare according to search mode + int r, f; + switch(appData.searchMode) + { + case 1: // exact position match + if(!(turn & board[EP_STATUS-1])) return FALSE; // wrong side to move + for(r=0; r maxCounts[r]) return FALSE; + } + return TRUE; +} + +int QuickScan(Board board, Move *move) +{ // reconstruct game,and compare all positions in it + int cnt=0, stretch=0, total = MakePieceList(board, counts); + do { + int piece = move->piece; + int to = move->to, from = pieceList[piece]; + if(piece <= Q_PROMO) { // special moves encoded by otherwise invalid piece numbers 1-4 + if(!piece) return -1; + if(piece == Q_PROMO) { // promotion, encoded as (Q_PROMO, to) + (piece, promoType) + piece = (++move)->piece; + from = pieceList[piece]; + counts[pieceType[piece]]--; + pieceType[piece] = (ChessSquare) move->to; + counts[move->to]++; + } else if(piece == Q_EP) { // e.p. capture, encoded as (Q_EP, ep-sqr) + (piece, to) + counts[pieceType[quickBoard[to]]]--; + quickBoard[to] = 0; total--; + move++; + continue; + } else if(piece <= Q_BCASTL) { // castling, encoded as (Q_XCASTL, king-to) + (rook, rook-to) + piece = pieceList[piece]; // first two elements of pieceList contain King numbers + from = pieceList[piece]; // so this must be King + quickBoard[from] = 0; + quickBoard[to] = piece; + pieceList[piece] = to; + move++; + continue; + } + } + 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 + quickBoard[from] = 0; + 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); +} + +void InitSearch() +{ + int r, f; + flipSearch = FALSE; + CopyBoard(soughtBoard, boards[currentMove]); + soughtTotal = MakePieceList(soughtBoard, maxSought); + soughtBoard[EP_STATUS-1] = (currentMove & 1) + 1; + if(currentMove == 0 && gameMode == EditPosition) soughtBoard[EP_STATUS-1] = blackPlaysFirst + 1; // (!) + CopyBoard(reverseBoard, boards[currentMove]); + for(r=0; r= 5) { + for(r=BOARD_HEIGHT/2; rgameInfo.variant != gameInfo.variant) return -1; // wrong variant + if(appData.eloThreshold1 && (lg->gameInfo.whiteRating < appData.eloThreshold1 && lg->gameInfo.blackRating < appData.eloThreshold1)) return -1; + if(appData.eloThreshold2 && (lg->gameInfo.whiteRating < appData.eloThreshold2 || lg->gameInfo.blackRating < appData.eloThreshold2)) return -1; + if(appData.dateThreshold && (!lg->gameInfo.date || atoi(lg->gameInfo.date) < appData.dateThreshold)) return -1; if(!initDone) { for(next = WhitePawn; next>8 ^ random()<<6 ^random()<<20; initDone = TRUE; } - dummyInfo.variant = VariantNormal; - FREE(dummyInfo.fen); dummyInfo.fen = NULL; - dummyInfo.whiteRating = 0; - dummyInfo.blackRating = 0; - FREE(dummyInfo.date); dummyInfo.date = NULL; + if(lg->gameInfo.fen) ParseFEN(boards[scratch], &btm, lg->gameInfo.fen); + else CopyBoard(boards[scratch], initialPosition); // default start position + if(lg->moves) { + turn = btm + 1; + if((next = QuickScan( boards[scratch], &moveDatabase[lg->moves] )) < 0) return -1; // quick scan rules out it is there + if(appData.searchMode >= 4) return next; // for material searches, trust QuickScan. + } + if(btm) plyNr++; + if(PositionMatches(boards[scratch], boards[currentMove])) return plyNr; fseek(f, lg->offset, 0); yynewfile(f); - CopyBoard(boards[scratch], initialPosition); // default start position while(1) { - yyboardindex = scratch + (plyNr&1); - quickFlag = 1; + yyboardindex = scratch; + quickFlag = plyNr+1; next = Myylex(); - quickFlag = 0; + quickFlag = 0; switch(next) { case PGNTag: if(plyNr) return -1; // after we have seen moves, any tags will be start of next game -#if 0 - ParsePGNTag(yy_text, &dummyInfo); // this has a bad memory leak... - if(dummyInfo.fen) ParseFEN(boards[scratch], &btm, dummyInfo.fen), free(dummyInfo.fen), dummyInfo.fen = NULL; -#else - // do it ourselves avoiding malloc - { char *p = yy_text+1, *q; - while(!isdigit(*p) && !isalpha(*p)) p++; - q = p; while(*p != ' ' && *p != '\t' && *p != '\n') p++; - *p = NULLCHAR; - if(!StrCaseCmp(q, "Date") && (p = strchr(p+1, '"'))) { if(atoi(p+1) < appData.dateThreshold) return -1; } else - if(!StrCaseCmp(q, "Variant") && (p = strchr(p+1, '"'))) dummyInfo.variant = StringToVariant(p+1); else - if(!StrCaseCmp(q, "WhiteElo") && (p = strchr(p+1, '"'))) dummyInfo.whiteRating = atoi(p+1); else - if(!StrCaseCmp(q, "BlackElo") && (p = strchr(p+1, '"'))) dummyInfo.blackRating = atoi(p+1); else - if(!StrCaseCmp(q, "WhiteUSCF") && (p = strchr(p+1, '"'))) dummyInfo.whiteRating = atoi(p+1); else - if(!StrCaseCmp(q, "BlackUSCF") && (p = strchr(p+1, '"'))) dummyInfo.blackRating = atoi(p+1); else - if(!StrCaseCmp(q, "FEN") && (p = strchr(p+1, '"'))) ParseFEN(boards[scratch], &btm, p+1); - } -#endif default: continue; @@ -11271,21 +11490,18 @@ int GameContainsPosition(FILE *f, ListGame *lg) fromY = DROP_RANK; toX = currentMoveString[2] - AAA; toY = currentMoveString[3] - ONE; + promoChar = 0; break; } // Move encountered; peform it. We need to shuttle between two boards, as even/odd index determines side to move - if(plyNr == 0) { // but first figure out variant and initial position - if(dummyInfo.variant != gameInfo.variant) return -1; // wrong variant - if(appData.eloThreshold1 && (dummyInfo.whiteRating < appData.eloThreshold1 && dummyInfo.blackRating < appData.eloThreshold1)) return -1; - if(appData.eloThreshold2 && (dummyInfo.whiteRating < appData.eloThreshold2 || dummyInfo.blackRating < appData.eloThreshold2)) return -1; - if(appData.dateThreshold && (!dummyInfo.date || atoi(dummyInfo.date) < appData.dateThreshold)) return -1; - if(btm) CopyBoard(boards[scratch+1], boards[scratch]), plyNr++; - if(PositionMatches(boards[scratch + plyNr], boards[currentMove])) return plyNr; - } - CopyBoard(boards[scratch + (plyNr+1&1)], boards[scratch + (plyNr&1)]); plyNr++; - ApplyMove(fromX, fromY, toX, toY, promoChar, boards[scratch + (plyNr&1)]); - if(PositionMatches(boards[scratch + (plyNr&1)], boards[currentMove])) return plyNr; + ApplyMove(fromX, fromY, toX, toY, promoChar, boards[scratch]); + if(PositionMatches(boards[scratch], boards[currentMove])) return plyNr; + if(appData.ignoreColors && PositionMatches(boards[scratch], reverseBoard)) return plyNr; + if(appData.findMirror) { + if(PositionMatches(boards[scratch], flipBoard)) return plyNr; + if(appData.ignoreColors && PositionMatches(boards[scratch], rotateBoard)) return plyNr; + } } } @@ -13132,7 +13348,7 @@ int WaitForEngine(ChessProgramState *cps, DelayedEventCallback retry) { char buf[MSG_SIZ]; - if (cps->pr == NULL) { + if (cps->pr == NoProc) { StartChessProgram(cps); if (cps->protocolVersion == 1) { retry(); @@ -14014,6 +14230,8 @@ ForwardInner(target) if (gameMode == EditPosition) return; + MarkTargetSquares(1); + if (gameMode == PlayFromGameFile && !pausing) PauseEvent(); @@ -14119,6 +14337,7 @@ BackwardInner(target) target, currentMove, forwardMostMove); if (gameMode == EditPosition) return; + MarkTargetSquares(1); if (currentMove <= backwardMostMove) { ClearHighlights(); DrawPosition(full_redraw, boards[currentMove]); @@ -14813,7 +15032,7 @@ SendToProgram(message, cps) int count, outCount, error; char buf[MSG_SIZ]; - if (cps->pr == NULL) return; + if (cps->pr == NoProc) return; Attention(cps); if (appData.debugMode) {