X-Git-Url: http://winboard.nl/cgi-bin?a=blobdiff_plain;f=backend.c;h=26ebe3cc49562e90042c511f8638f1ad0320c812;hb=f29ff21f23c2a22fc55e14ba48c53d704c22289d;hp=b1e5e93fd2eeb6dd01a1643ffba9a6a92f08270a;hpb=98d170abf0ab910767ea165d9cb94ece49ff3e08;p=xboard.git diff --git a/backend.c b/backend.c index b1e5e93..26ebe3c 100644 --- a/backend.c +++ b/backend.c @@ -234,7 +234,7 @@ void InitDrawingSizes(int x, int y); void NextMatchGame P((void)); int NextTourneyGame P((int nr, int *swap)); int Pairing P((int nr, int nPlayers, int *w, int *b, int *sync)); -FILE *WriteTourneyFile P((char *results)); +FILE *WriteTourneyFile P((char *results, FILE *f)); void DisplayTwoMachinesTitle P(()); #ifdef WIN32 @@ -981,6 +981,7 @@ InitBackEnd1() GetTimeMark(&programStartTime); srandom((programStartTime.ms + 1000*programStartTime.sec)*0x1001001); // [HGM] book: makes sure random is unpredictabe to msec level + appData.seedBase = random() + (random()<<15); pauseStart = programStartTime; pauseStart.sec -= 100; // [HGM] matchpause: fake a pause that has long since ended ClearProgramStats(); @@ -1436,7 +1437,7 @@ MatchEvent(int mode) if(strchr(appData.results, '*') == NULL) { FILE *f; appData.tourneyCycles++; - if(f = WriteTourneyFile(appData.results)) { // make a tourney file with increased number of cycles + if(f = WriteTourneyFile(appData.results, NULL)) { // make a tourney file with increased number of cycles fclose(f); NextTourneyGame(-1, &dummy); ReserveGame(-1, 0); @@ -6231,7 +6232,6 @@ OKToStartUserMove(x, y) (int)from_piece < (int)BlackPawn; /* [HGM] can be > King! */ switch (gameMode) { - case PlayFromGameFile: case AnalyzeFile: case TwoMachinesPlay: case EndOfGame: @@ -6259,6 +6259,8 @@ OKToStartUserMove(x, y) } break; + case PlayFromGameFile: + if(!shiftKey || !appData.variations) return FALSE; // [HGM] allow starting variation in this mode case EditGame: if (!white_piece && WhiteOnMove(currentMove)) { DisplayMoveError(_("It is White's turn")); @@ -6302,6 +6304,7 @@ OKToStartUserMove(x, y) } if (currentMove != forwardMostMove && gameMode != AnalyzeMode && gameMode != EditGame // [HGM] vari: treat as AnalyzeMode + && gameMode != PlayFromGameFile // [HGM] as EditGame, with protected main line && gameMode != AnalyzeFile && gameMode != Training) { DisplayMoveError(_("Displayed position is not current")); return FALSE; @@ -6393,7 +6396,6 @@ UserMoveEvent(fromX, fromY, toX, toY, promoChar) */ switch (gameMode) { - case PlayFromGameFile: case AnalyzeFile: case TwoMachinesPlay: case EndOfGame: @@ -6419,6 +6421,8 @@ UserMoveEvent(fromX, fromY, toX, toY, promoChar) } break; + case PlayFromGameFile: + if(!shiftKey ||!appData.variations) return; // [HGM] only variations case EditGame: case IcsExamining: case BeginningOfGame: @@ -6613,7 +6617,7 @@ FinishMove(moveType, fromX, fromY, toX, toY, promoChar) /* Ok, now we know that the move is good, so we can kill the previous line in Analysis Mode */ - if ((gameMode == AnalyzeMode || gameMode == EditGame) + if ((gameMode == AnalyzeMode || gameMode == EditGame || gameMode == PlayFromGameFile && appData.variations && shiftKey) && currentMove < forwardMostMove) { if(appData.variations && shiftKey) PushTail(currentMove, forwardMostMove); // [HGM] vari: save tail of game else forwardMostMove = currentMove; @@ -7371,6 +7375,28 @@ MatingPotential(int pCnt[], int side, int nMine, int nHis, int stale, int bisCol } int +CompareWithRights(Board b1, Board b2) +{ + int rights = 0; + if(!CompareBoards(b1, b2)) return FALSE; + if(b1[EP_STATUS] != b2[EP_STATUS]) return FALSE; + /* compare castling rights */ + if( b1[CASTLING][2] != b2[CASTLING][2] && (b2[CASTLING][0] != NoRights || b2[CASTLING][1] != NoRights) ) + rights++; /* King lost rights, while rook still had them */ + if( b1[CASTLING][2] != NoRights ) { /* king has rights */ + if( b1[CASTLING][0] != b2[CASTLING][0] || b1[CASTLING][1] != b2[CASTLING][1] ) + rights++; /* but at least one rook lost them */ + } + if( b1[CASTLING][5] != b1[CASTLING][5] && (b2[CASTLING][3] != NoRights || b2[CASTLING][4] != NoRights) ) + rights++; + if( b1[CASTLING][5] != NoRights ) { + if( b1[CASTLING][3] != b2[CASTLING][3] || b1[CASTLING][4] != b2[CASTLING][4] ) + rights++; + } + return rights == 0; +} + +int Adjudicate(ChessProgramState *cps) { // [HGM] some adjudications useful with buggy engines // [HGM] adjudicate: made into separate routine, which now can be called after every move @@ -9695,12 +9721,13 @@ CountPlayers(char *p) } FILE * -WriteTourneyFile(char *results) +WriteTourneyFile(char *results, FILE *f) { // write tournament parameters on tourneyFile; on success return the stream pointer for closing - FILE *f = fopen(appData.tourneyFile, "w"); + if(f == NULL) f = fopen(appData.tourneyFile, "w"); if(f == NULL) DisplayError(_("Could not write on tourney file"), 0); else { // create a file with tournament description fprintf(f, "-participants {%s}\n", appData.participants); + fprintf(f, "-seedBase %d\n", appData.seedBase); fprintf(f, "-tourneyType %d\n", appData.tourneyType); fprintf(f, "-tourneyCycles %d\n", appData.tourneyCycles); fprintf(f, "-defaultMatchGames %d\n", appData.defaultMatchGames); @@ -9724,10 +9751,73 @@ WriteTourneyFile(char *results) return f; } +#define MAXENGINES 1000 +char *command[MAXENGINES], *mnemonic[MAXENGINES]; + +void Substitute(char *participants, int expunge) +{ + int i, changed, changes=0, nPlayers=0; + char *p, *q, *r, buf[MSG_SIZ]; + if(participants == NULL) return; + if(appData.tourneyFile[0] == NULLCHAR) { free(participants); return; } + r = p = participants; q = appData.participants; + while(*p && *p == *q) { + if(*p == '\n') r = p+1, nPlayers++; + p++; q++; + } + if(*p) { // difference + while(*p && *p++ != '\n'); + while(*q && *q++ != '\n'); + changed = nPlayers; + changes = 1 + (strcmp(p, q) != 0); + } + if(changes == 1) { // a single engine mnemonic was changed + q = r; while(*q) nPlayers += (*q++ == '\n'); + p = buf; while(*r && (*p = *r++) != '\n') p++; + *p = NULLCHAR; + NamesToList(firstChessProgramNames, command, mnemonic); + for(i=1; mnemonic[i]; i++) if(!strcmp(buf, mnemonic[i])) break; + if(mnemonic[i]) { // The substitute is valid + FILE *f; + if(appData.tourneyFile[0] && (f = fopen(appData.tourneyFile, "r+")) ) { + flock(fileno(f), LOCK_EX); + ParseArgsFromFile(f); + fseek(f, 0, SEEK_SET); + FREE(appData.participants); appData.participants = participants; + if(expunge) { // erase results of replaced engine + int len = strlen(appData.results), w, b, dummy; + for(i=0; i 1) DisplayError(_("You can only change one engine at the time"), 0); + free(participants); + return; +} + int CreateTourney(char *name) { FILE *f; + if(matchMode && strcmp(name, appData.tourneyFile)) { + ASSIGN(name, appData.tourneyFile); //do not allow change of tourneyfile while playing + } if(name[0] == NULLCHAR) { if(appData.participants[0]) DisplayError(_("You must supply a tournament file,\nfor storing the tourney progress"), 0); @@ -9745,7 +9835,7 @@ CreateTourney(char *name) } ASSIGN(appData.tourneyFile, name); if(appData.tourneyType < 0) appData.defaultMatchGames = 1; // Swiss forces games/pairing = 1 - if((f = WriteTourneyFile("")) == NULL) return 0; + if((f = WriteTourneyFile("", NULL)) == NULL) return 0; } fclose(f); appData.noChessProgram = FALSE; @@ -9754,9 +9844,6 @@ CreateTourney(char *name) return 1; } -#define MAXENGINES 1000 -char *command[MAXENGINES], *mnemonic[MAXENGINES]; - void NamesToList(char *names, char **engineList, char **engineMnemonic) { char buf[MSG_SIZ], *p, *q; @@ -9779,7 +9866,7 @@ void NamesToList(char *names, char **engineList, char **engineMnemonic) names = p; i++; if(i > MAXENGINES - 2) break; } - engineList[i] = NULL; + engineList[i] = engineMnemonic[i] = NULL; } // following implemented as macro to avoid type limitations @@ -9941,6 +10028,7 @@ NextMatchGame() first.twoMachinesColor = firstWhite ? "white\n" : "black\n"; // perform actual color assignement second.twoMachinesColor = firstWhite ? "black\n" : "white\n"; appData.noChessProgram = (first.pr == NoProc); // kludge to prevent Reset from starting up chess program + if(appData.loadGameIndex == -2) srandom(appData.seedBase + 68163*(nextGame & ~1)); // deterministic seed to force same opening Reset(FALSE, first.pr != NoProc); appData.noChessProgram = FALSE; if(!LoadGameOrPosition(matchGame)) return; // setup game; abort when bad game/pos file @@ -10570,8 +10658,8 @@ AutoPlayOneMove() if (currentMove >= forwardMostMove) { if(gameMode == AnalyzeFile) { ExitAnalyzeMode(); SendToProgram("force\n", &first); } - gameMode = EditGame; - ModeHighlight(); +// gameMode = EndOfGame; +// ModeHighlight(); /* [AS] Clear current move marker at the end of a game */ /* HistorySet(parseList, backwardMostMove, forwardMostMove, -1); */ @@ -11018,7 +11106,149 @@ ReloadGame(offset) } } +int keys[EmptySquare+1]; +int +PositionMatches(Board b1, Board b2) +{ + int r, f, sum=0; + switch(appData.searchMode) { + case 1: return CompareWithRights(b1, b2); + case 2: + for(r=0; r>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; + fseek(f, lg->offset, 0); + yynewfile(f); + CopyBoard(boards[scratch], initialPosition); // default start position + while(1) { + yyboardindex = scratch + (plyNr&1); + quickFlag = 1; + next = Myylex(); + 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; + + case XBoardGame: + case GNUChessGame: + if(plyNr) return -1; // after we have seen moves, this is for new game + continue; + + case AmbiguousMove: // we cannot reconstruct the game beyond these two + case ImpossibleMove: + case WhiteWins: // game ends here with these four + case BlackWins: + case GameIsDrawn: + case GameUnfinished: + return -1; + + case IllegalMove: + if(appData.testLegality) return -1; + case WhiteCapturesEnPassant: + case BlackCapturesEnPassant: + case WhitePromotion: + case BlackPromotion: + case WhiteNonPromotion: + case BlackNonPromotion: + case NormalMove: + case WhiteKingSideCastle: + case WhiteQueenSideCastle: + case BlackKingSideCastle: + case BlackQueenSideCastle: + case WhiteKingSideCastleWild: + case WhiteQueenSideCastleWild: + case BlackKingSideCastleWild: + case BlackQueenSideCastleWild: + case WhiteHSideCastleFR: + case WhiteASideCastleFR: + case BlackHSideCastleFR: + case BlackASideCastleFR: + fromX = currentMoveString[0] - AAA; + fromY = currentMoveString[1] - ONE; + toX = currentMoveString[2] - AAA; + toY = currentMoveString[3] - ONE; + promoChar = currentMoveString[4]; + break; + case WhiteDrop: + case BlackDrop: + fromX = next == WhiteDrop ? + (int) CharToPiece(ToUpper(currentMoveString[0])) : + (int) CharToPiece(ToLower(currentMoveString[0])); + fromY = DROP_RANK; + toX = currentMoveString[2] - AAA; + toY = currentMoveString[3] - ONE; + 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; + } +} /* Load the nth game from open file f */ int @@ -11033,7 +11263,7 @@ LoadGame(f, gameNumber, title, useList) int gn = gameNumber; ListGame *lg = NULL; int numPGNTags = 0; - int err; + int err, pos = -1; GameMode oldGameMode; VariantClass oldVariant = gameInfo.variant; /* [HGM] PGNvariant */ @@ -11059,6 +11289,7 @@ LoadGame(f, gameNumber, title, useList) if (lg) { fseek(f, lg->offset, 0); GameListHighlight(gameNumber); + pos = lg->position; gn = 1; } else { @@ -11458,10 +11689,11 @@ LoadGame(f, gameNumber, title, useList) AnalyzeFileEvent(); } + if (!matchMode && pos >= 0) { + ToNrEvent(pos); // [HGM] no autoplay if selected on position + } else if (matchMode || appData.timeDelay == 0) { ToEndEvent(); - gameMode = EditGame; - ModeHighlight(); } else if (appData.timeDelay > 0) { AutoPlayGameLoop(); } @@ -16392,6 +16624,7 @@ int wrap(char *dest, char *src, int count, int width, int *lp) } // [HGM] vari: routines for shelving variations +Boolean modeRestore = FALSE; void PushInner(int firstMove, int lastMove) @@ -16435,6 +16668,7 @@ PushTail(int firstMove, int lastMove) PushInner(firstMove, lastMove); if(storedGames == 1) GreyRevert(FALSE); + if(gameMode == PlayFromGameFile) gameMode = EditGame, modeRestore = TRUE; } void @@ -16492,9 +16726,10 @@ PopTail(Boolean annotate) CommentPopDown(); // make sure no stale variation comments to the destroyed line can remain open PopInner(annotate); + if(currentMove < forwardMostMove) ForwardEvent(); else HistorySet(parseList, backwardMostMove, forwardMostMove, currentMove-1); - if(storedGames == 0) GreyRevert(TRUE); + if(storedGames == 0) { GreyRevert(TRUE); if(modeRestore) modeRestore = FALSE, gameMode = PlayFromGameFile; } return TRUE; } @@ -16521,7 +16756,7 @@ LoadVariation(int index, char *text) char *p = text, *start = NULL, *end = NULL, wait = NULLCHAR; int level = 0, move; - if(gameMode != EditGame && gameMode != AnalyzeMode) return; + if(gameMode != EditGame && gameMode != AnalyzeMode && gameMode != PlayFromGameFile) return; // first find outermost bracketing variation while(*p) { // hope I got this right... Non-nesting {} and [] can screen each other and nesting () if(!wait) { // while inside [] pr {}, ignore everyting except matching closing ]}