From d9f4b584106f4d7477158476ff792f6fa0b2630a Mon Sep 17 00:00:00 2001 From: H.G. Muller Date: Tue, 2 Aug 2011 22:58:34 +0200 Subject: [PATCH] Implement searching games in Game List for a position For this it was needed to reconstruct all positions of the games without loading them, so they could be compared to the currently displayed position. A new routine GameContainsPosition is made for that. In Disambiguate, when the global quickFlag is set, we now do the disambiguation ignoring check,and only if this leads to an ambiguous move, we repeat it taking check into account to see if that resolves the ambiguity. This causes us to miss if unambiguous moves are illegal because they put their King in check, but who cares? We now do not make game-list line when not needed. If we do not filter by text, but by position, there is no need for preparing the game-list line for games that are not selected. All for efficiency. --- args.h | 4 + backend.c | 154 +++++++++++++++++++++++++++++++++++++++++++++++++- backend.h | 1 + common.h | 4 + moves.c | 11 +++- moves.h | 2 + winboard/resource.h | 11 ++++ winboard/wgamelist.c | 31 +++++++--- winboard/winboard.rc | 30 +++++++--- winboard/woptions.c | 31 ++++++++++ xgamelist.c | 80 +++++++++++++++----------- xoptions.c | 18 ++++++- 12 files changed, 322 insertions(+), 55 deletions(-) diff --git a/args.h b/args.h index 3a6449f..858ee44 100644 --- a/args.h +++ b/args.h @@ -630,6 +630,10 @@ ArgDescriptor argDescriptors[] = { { "sSAN", ArgTrue, (void *) &appData.pvSAN[1], FALSE, FALSE }, { "pairingEngine", ArgFilename, (void *) &appData.pairingEngine, TRUE, "" }, { "defaultTourneyName", ArgFilename, (void *) &appData.defName, TRUE, "" }, + { "eloThresholdAny", ArgInt, (void *) &appData.eloThreshold1, FALSE, (ArgIniType) 0 }, + { "eloThresholdBoth", ArgInt, (void *) &appData.eloThreshold2, FALSE, (ArgIniType) 0 }, + { "dateThreshold", ArgInt, (void *) &appData.dateThreshold, FALSE, (ArgIniType) 0 }, + { "searchMode", ArgInt, (void *) &appData.searchMode, FALSE, (ArgIniType) 1 }, #if ZIPPY { "zippyTalk", ArgBoolean, (void *) &appData.zippyTalk, FALSE, (ArgIniType) ZIPPY_TALK }, diff --git a/backend.c b/backend.c index a3ce9ed..804da7d 100644 --- a/backend.c +++ b/backend.c @@ -7374,6 +7374,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 @@ -11021,7 +11043,133 @@ 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 ^ rand()<<6 ^rand()<<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 + ParsePGNTag(yy_text, &dummyInfo); + if(dummyInfo.fen) ParseFEN(boards[scratch], &btm, dummyInfo.fen), free(dummyInfo.fen), dummyInfo.fen = NULL; + 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 @@ -11036,7 +11184,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 */ @@ -11062,6 +11210,7 @@ LoadGame(f, gameNumber, title, useList) if (lg) { fseek(f, lg->offset, 0); GameListHighlight(gameNumber); + pos = lg->position; gn = 1; } else { @@ -11461,6 +11610,9 @@ 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(); } else if (appData.timeDelay > 0) { diff --git a/backend.h b/backend.h index 56c9d3e..4055ba4 100644 --- a/backend.h +++ b/backend.h @@ -279,6 +279,7 @@ char *PGNResult P((ChessMove result)); typedef struct _ListGame { ListNode node; int number; + int position; unsigned long offset; /* Byte offset of game within file. */ GameInfo gameInfo; /* Note that some entries may be NULL. */ } ListGame; diff --git a/common.h b/common.h index 6b3266f..08d45e5 100644 --- a/common.h +++ b/common.h @@ -627,6 +627,10 @@ typedef struct { int NPS[ENGINES]; Boolean autoKibitz; int engineComments; + int eloThreshold1; /* [HGM] select */ + int eloThreshold2; + int dateThreshold; + int searchMode; char *userName; int rewindIndex; /* [HGM] autoinc */ int sameColorGames; /* [HGM] alternate */ diff --git a/moves.c b/moves.c index 2a8e187..194047d 100644 --- a/moves.c +++ b/moves.c @@ -70,7 +70,7 @@ int SameColor P((ChessSquare, ChessSquare)); int PosFlags(int index); extern signed char initialRights[BOARD_FILES]; /* [HGM] all rights enabled, set in InitPosition */ - +int quickFlag; int WhitePiece(piece) ChessSquare piece; @@ -1345,6 +1345,15 @@ void Disambiguate(board, flags, closure) } rFilter = closure->rtIn; // [HGM] speed: only consider moves to given to-square fFilter = closure->ftIn; + if(quickFlag) { // [HGM] speed: try without check test first, because if that is not ambiguous, we are happy + GenLegal(board, flags|F_IGNORE_CHECK, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn); + if(closure->count > 1) { // gamble did not pay off. retry with check test to resolve ambiguity + closure->count = closure->captures = 0; + closure->rf = closure->ff = closure->rt = closure->ft = 0; + closure->kind = ImpossibleMove; + GenLegal(board, flags, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn); // [HGM] speed: only pieces of requested type + } + } else GenLegal(board, flags, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn); // [HGM] speed: only pieces of requested type if (closure->count == 0) { /* See if it's an illegal move due to check */ diff --git a/moves.h b/moves.h index 105906b..d8f3953 100644 --- a/moves.h +++ b/moves.h @@ -171,3 +171,5 @@ void Disambiguate P((Board board, int flags, DisambiguateClosure *closure)); ChessMove CoordsToAlgebraic P((Board board, int flags, int rf, int ff, int rt, int ft, int promoChar, char out[MOVE_LEN])); + +extern int quickFlag; diff --git a/winboard/resource.h b/winboard/resource.h index 91b0b3a..f124467 100644 --- a/winboard/resource.h +++ b/winboard/resource.h @@ -601,6 +601,17 @@ #define OPT_MessageFont7 1955 #define OPT_SampleGameListFont 1956 #define OPT_ChooseGameListFont 1957 +#define OPT_elo1 1958 +#define OPT_elo2 1959 +#define OPT_date 1960 +#define OPT_elo1t 1961 +#define OPT_elo2t 1962 +#define OPT_datet 1963 +#define OPT_thresholds 1964 +#define OPT_Exact 1965 +#define OPT_Subset 1966 +#define OPT_Struct 1967 +#define OPT_Material 1968 // Next default values for new objects diff --git a/winboard/wgamelist.c b/winboard/wgamelist.c index 863725a..c5a25f5 100644 --- a/winboard/wgamelist.c +++ b/winboard/wgamelist.c @@ -57,10 +57,11 @@ struct GameListStats }; /* [AS] Setup the game list according to the specified filter */ -static int GameListToListBox( HWND hDlg, BOOL boReset, char * pszFilter, struct GameListStats * stats ) +static int GameListToListBox( HWND hDlg, BOOL boReset, char * pszFilter, struct GameListStats * stats, BOOL byPos ) { ListGame * lg = (ListGame *) gameList.head; int nItem; + char buf[MSG_SIZ]; BOOL hasFilter = FALSE; int count = 0; struct GameListStats dummy; @@ -86,16 +87,23 @@ static int GameListToListBox( HWND hDlg, BOOL boReset, char * pszFilter, struct } for (nItem = 0; nItem < ((ListGame *) gameList.tailPred)->number; nItem++){ - char * st = GameListLine(lg->number, &lg->gameInfo); + char * st = NULL; BOOL skip = FALSE; + int pos = -1; if( hasFilter ) { - if( ! SearchPattern( st, pszFilter ) ) { - skip = TRUE; - } + st = GameListLine(lg->number, &lg->gameInfo); + if( !SearchPattern( st, pszFilter) ) skip = TRUE; + } + + if( !skip && byPos) { + if( (pos = GameContainsPosition(gameFile, lg)) < 0) skip = TRUE; } + lg->position = pos; + if( ! skip ) { + if(!st) st = GameListLine(lg->number, &lg->gameInfo); SendDlgItemMessage(hDlg, OPT_GameListText, LB_ADDSTRING, 0, (LPARAM) st); count++; @@ -110,7 +118,7 @@ static int GameListToListBox( HWND hDlg, BOOL boReset, char * pszFilter, struct stats->unfinished++; } - free(st); + if(st) free(st); lg = (ListGame *) lg->node.succ; } @@ -168,7 +176,7 @@ GameListDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) /* Set font */ SendDlgItemMessage( hDlg, OPT_GameListText, WM_SETFONT, (WPARAM)font[boardSize][GAMELIST_FONT]->hf, MAKELPARAM(TRUE, 0 )); - count = GameListToListBox( hDlg, gameListDialog ? TRUE : FALSE, NULL, &stats ); + count = GameListToListBox( hDlg, gameListDialog ? TRUE : FALSE, NULL, &stats, FALSE ); SendDlgItemMessage( hDlg, IDC_GameListFilter, WM_SETTEXT, 0, (LPARAM) "" ); SendDlgItemMessage( hDlg, IDC_GameListFilter, EM_SETLIMITTEXT, MAX_FILTER_LENGTH, 0 ); @@ -262,8 +270,10 @@ GameListDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) /* [AS] End command replacement */ switch (LOWORD(wParam)) { - case IDOK: case OPT_GameListLoad: + LoadOptionsPopup(hDlg); + return TRUE; + case IDOK: nItem = SendDlgItemMessage(hDlg, OPT_GameListText, LB_GETCURSEL, 0, 0); if (nItem < 0) { /* is this possible? */ @@ -283,7 +293,7 @@ GameListDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) SendDlgItemMessage(hDlg, OPT_GameListText, LB_SETCURSEL, nItem, 0); break; /* load the game*/ - case OPT_GameListPrev: +// case OPT_GameListPrev: nItem = SendDlgItemMessage(hDlg, OPT_GameListText, LB_GETCURSEL, 0, 0); nItem--; if (nItem < 0) { @@ -295,13 +305,14 @@ GameListDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) break; /* load the game*/ /* [AS] */ + case OPT_GameListPrev: case IDC_GameListDoFilter: { char filter[MAX_FILTER_LENGTH+1]; if( GetDlgItemText( hDlg, IDC_GameListFilter, filter, sizeof(filter) ) >= 0 ) { filter[ sizeof(filter)-1 ] = '\0'; - count = GameListToListBox( hDlg, TRUE, filter, &stats ); + count = GameListToListBox( hDlg, TRUE, filter, &stats, LOWORD(wParam)!=IDC_GameListDoFilter ); GameListUpdateTitle( hDlg, _("Game List"), count, ((ListGame *) gameList.tailPred)->number, &stats ); } } diff --git a/winboard/winboard.rc b/winboard/winboard.rc index aedb1c1..29b1f91 100644 --- a/winboard/winboard.rc +++ b/winboard/winboard.rc @@ -90,7 +90,7 @@ BEGIN PUSHBUTTON "Cancel",IDCANCEL,195,190,40,14 END -DLG_LoadOptions DIALOG DISCARDABLE 10, 18, 166, 55 +DLG_LoadOptions DIALOG DISCARDABLE 10, 18, 166, 184 STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU CAPTION "Load Game Options" FONT 8, "MS Sans Serif" @@ -99,8 +99,23 @@ BEGIN BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,4,4,160,10 EDITTEXT OPT_ASTimeDelay,16,16,28,14,ES_AUTOHSCROLL LTEXT "seconds per move",OPT_AStext1,46,20,90,8,NOT WS_GROUP - PUSHBUTTON "OK",IDOK,56,36,50,14,WS_GROUP - PUSHBUTTON "Cancel",IDCANCEL,112,36,50,14 + LTEXT "when filtering game list on position, use thresholds:",OPT_thresholds,4,36,160,8,NOT WS_GROUP + EDITTEXT OPT_elo1,16,50,28,14,ES_AUTOHSCROLL + LTEXT "Elo for at least one player",OPT_elo1t,46,54,90,8,NOT WS_GROUP + EDITTEXT OPT_elo2,16,70,28,14,ES_AUTOHSCROLL + LTEXT "Elo for both players",OPT_elo2t,46,74,90,8,NOT WS_GROUP + EDITTEXT OPT_date,16,90,28,14,ES_AUTOHSCROLL + LTEXT "or later year",OPT_datet,46,94,94,8,NOT WS_GROUP + CONTROL "Match exact position",OPT_Exact,"Button", + BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,4,111,159,10 + CONTROL "Match if position is subset",OPT_Subset,"Button", + BS_AUTORADIOBUTTON | WS_TABSTOP,4,124,159,10 + CONTROL "Match material with exact pawn structure",OPT_Struct,"Button", + BS_AUTORADIOBUTTON | WS_TABSTOP,4,137,159,10 + CONTROL "Match material",OPT_Material,"Button", + BS_AUTORADIOBUTTON | WS_TABSTOP,4,150,159,10 + PUSHBUTTON "OK",IDOK,56,165,50,14,WS_GROUP + PUSHBUTTON "Cancel",IDCANCEL,112,165,50,14 END DLG_SaveOptions DIALOG DISCARDABLE 6, 17, 178, 119 @@ -240,11 +255,10 @@ FONT 8, "MS Sans Serif" BEGIN LISTBOX OPT_GameListText,2,2,254,130,LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_HSCROLL | WS_TABSTOP - PUSHBUTTON "&Load",OPT_GameListLoad,2,135,32,15 - PUSHBUTTON "&<",OPT_GameListPrev,38,135,22,15 - PUSHBUTTON "&>",OPT_GameListNext,64,135,24,15 - PUSHBUTTON "&Close",OPT_GameListClose,92,135,32,15 - PUSHBUTTON "Filter",IDC_GameListDoFilter,144,136,30,14 + PUSHBUTTON "&Thresholds",OPT_GameListLoad,2,136,40,15 + PUSHBUTTON "&Find Position",OPT_GameListPrev,45,136,48,15 + PUSHBUTTON "&Close",OPT_GameListClose,96,136,30,15 + PUSHBUTTON "&Filter",IDC_GameListDoFilter,149,136,25,14 EDITTEXT IDC_GameListFilter,178,136,78,14,ES_AUTOHSCROLL END diff --git a/winboard/woptions.c b/winboard/woptions.c index a48b924..94da5b8 100644 --- a/winboard/woptions.c +++ b/winboard/woptions.c @@ -2391,6 +2391,15 @@ CommPortOptionsPopup(HWND hwnd) * \*---------------------------------------------------------------------------*/ +int +LoadOptionsWhichRadio(HWND hDlg) +{ + return (IsDlgButtonChecked(hDlg, OPT_Exact) ? 1 : + (IsDlgButtonChecked(hDlg, OPT_Subset) ? 2 : + (IsDlgButtonChecked(hDlg, OPT_Struct) ? 3 : + (IsDlgButtonChecked(hDlg, OPT_Material) ? 4 : -1)))); +} + VOID SetLoadOptionEnables(HWND hDlg) { @@ -2406,6 +2415,7 @@ LoadOptions(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { char buf[MSG_SIZ]; float fnumber; + int ok; switch (message) { case WM_INITDIALOG: /* message: initialize dialog box */ @@ -2421,6 +2431,23 @@ LoadOptions(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) CheckDlgButton(hDlg, OPT_Autostep, FALSE); } SetLoadOptionEnables(hDlg); + SetDlgItemInt(hDlg, OPT_elo1, appData.eloThreshold1, FALSE); + SetDlgItemInt(hDlg, OPT_elo2, appData.eloThreshold2, FALSE); + SetDlgItemInt(hDlg, OPT_date, appData.dateThreshold, FALSE); + switch (appData.searchMode) { + case 1: + CheckDlgButton(hDlg, OPT_Exact, TRUE); + break; + case 2: + CheckDlgButton(hDlg, OPT_Subset, TRUE); + break; + case 3: + CheckDlgButton(hDlg, OPT_Struct, TRUE); + break; + case 4: + CheckDlgButton(hDlg, OPT_Material, TRUE); + break; + } return TRUE; case WM_COMMAND: /* message: received a command */ @@ -2438,6 +2465,10 @@ LoadOptions(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) } else { appData.timeDelay = (float) -1.0; } + appData.eloThreshold1 = GetDlgItemInt(hDlg, OPT_elo1, &ok, FALSE); + appData.eloThreshold2 = GetDlgItemInt(hDlg, OPT_elo2, &ok, FALSE); + appData.dateThreshold = GetDlgItemInt(hDlg, OPT_date, &ok, FALSE); + appData.searchMode = LoadOptionsWhichRadio(hDlg); EndDialog(hDlg, TRUE); return TRUE; diff --git a/xgamelist.c b/xgamelist.c index 46200c5..d68c00b 100644 --- a/xgamelist.c +++ b/xgamelist.c @@ -186,7 +186,7 @@ GameListCreate(name, callback, client_data) XtSetArg(args[j], XtNleft, XtChainLeft); j++; XtSetArg(args[j], XtNright, XtChainLeft); j++; b_load = - XtCreateManagedWidget(_("load"), commandWidgetClass, form, args, j); + XtCreateManagedWidget(_("thresholds"), commandWidgetClass, form, args, j); XtAddCallback(b_load, XtNcallback, callback, client_data); j = 0; @@ -197,9 +197,9 @@ GameListCreate(name, callback, client_data) XtSetArg(args[j], XtNleft, XtChainLeft); j++; XtSetArg(args[j], XtNright, XtChainLeft); j++; b_loadprev = - XtCreateManagedWidget(_("prev"), commandWidgetClass, form, args, j); + XtCreateManagedWidget(_("find position"), commandWidgetClass, form, args, j); XtAddCallback(b_loadprev, XtNcallback, callback, client_data); - +#if 1 j = 0; XtSetArg(args[j], XtNfromVert, viewport); j++; XtSetArg(args[j], XtNfromHoriz, b_loadprev); j++; @@ -210,7 +210,9 @@ GameListCreate(name, callback, client_data) b_loadnext = XtCreateManagedWidget(_("next"), commandWidgetClass, form, args, j); XtAddCallback(b_loadnext, XtNcallback, callback, client_data); - +#else + b_loadnext = b_loadprev; +#endif j = 0; XtSetArg(args[j], XtNfromVert, viewport); j++; XtSetArg(args[j], XtNfromHoriz, b_loadnext); j++; @@ -240,7 +242,7 @@ GameListCreate(name, callback, client_data) XtSetArg(args[j], XtNbottom, XtChainBottom); j++; XtSetArg(args[j], XtNleft, XtChainLeft); j++; XtSetArg(args[j], XtNright, XtChainRight); j++; - XtSetArg(args[j], XtNwidth, fw_width - 225 - squareSize); j++; + XtSetArg(args[j], XtNwidth, fw_width - 275 - squareSize); j++; XtSetArg(args[j], XtNstring, filterString); j++; XtSetArg(args[j], XtNdisplayCaret, False); j++; XtSetArg(args[j], XtNresizable, True); j++; @@ -321,7 +323,7 @@ GameListCreate(name, callback, client_data) } static int -GameListPrepare() +GameListPrepare(int byPos) { // [HGM] filter: put in separate routine, to make callable from call-back int nstrings; ListGame *lg; @@ -333,11 +335,13 @@ GameListPrepare() lg = (ListGame *) gameList.head; listLength = 0; while (nstrings--) { + int pos = -1; line = GameListLine(lg->number, &lg->gameInfo); - if(filterString[0] == NULLCHAR || SearchPattern( line, filterString ) ) { + if((filterString[0] == NULLCHAR || SearchPattern( line, filterString )) && (!byPos || (pos=GameContainsPosition(glc->fp, lg)) >= 0) ) { *st++ = line; // [HGM] filter: make adding line conditional listLength++; } + lg->position = pos; lg = (ListGame *) lg->node.succ; } *st = NULL; @@ -376,6 +380,14 @@ GameListCallback(w, client_data, call_data) GameListPopDown(); return; } + if (strcmp(name, _("thresholds")) == 0) { + LoadOptionsProc(); + return; + } + if (strcmp(name, _("find position")) == 0) { + if(GameListPrepare(True)) GameListReplace(); // crashes on empty list... + return; + } listwidg = XtNameToWidget(glc->shell, "*form.viewport.list"); rs = XawListShowCurrent(listwidg); if (strcmp(name, _("load")) == 0) { @@ -405,10 +417,10 @@ GameListCallback(w, client_data, call_data) XtGetValues(filterText, args, j); safeStrCpy(filterString, name, sizeof(filterString)/sizeof(filterString[0])); XawListHighlight(listwidg, 0); - if(GameListPrepare()) GameListReplace(); // crashes on empty list... + if(GameListPrepare(False)) GameListReplace(); // crashes on empty list... return; } -#if 0 +#if 1 index = atoi(glc->strings[index])-1; // [HGM] filter: read true index from sequence nr of line if (cmailMsgLoaded) { CmailLoadGame(glc->fp, index + 1, glc->filename, True); @@ -442,7 +454,7 @@ GameListPopUp(fp, filename) free(glc->strings); } - GameListPrepare(); // [HGM] filter: code put in separate routine + GameListPrepare(False); // [HGM] filter: code put in separate routine glc->fp = fp; @@ -558,7 +570,7 @@ SetFilterProc(w, event, prms, nprms) XtSetArg(args[j], XtNstring, &name); j++; XtGetValues(filterText, args, j); safeStrCpy(filterString, name, sizeof(filterString)/sizeof(filterString[0])); - if(GameListPrepare()) GameListReplace(); // crashes on empty list... + if(GameListPrepare(False)) GameListReplace(); // crashes on empty list... list = XtNameToWidget(glc->shell, "*form.viewport.list"); XawListHighlight(list, 0); j = 0; @@ -612,35 +624,35 @@ GameListIsUp() return glc && glc->up; } -int SaveGameListAsText(FILE *f) -{ - ListGame * lg = (ListGame *) gameList.head; - int nItem; - - if( !glc || ((ListGame *) gameList.tailPred)->number <= 0 ) { - DisplayError("Game list not loaded or empty", 0); - return False; - } - - /* Copy the list into the global memory block */ - if( f != NULL ) { +int SaveGameListAsText(FILE *f) +{ + ListGame * lg = (ListGame *) gameList.head; + int nItem; + + if( !glc || ((ListGame *) gameList.tailPred)->number <= 0 ) { + DisplayError("Game list not loaded or empty", 0); + return False; + } + + /* Copy the list into the global memory block */ + if( f != NULL ) { - lg = (ListGame *) gameList.head; - - for (nItem = 0; nItem < ((ListGame *) gameList.tailPred)->number; nItem++){ - char * st = GameListLineFull(lg->number, &lg->gameInfo); + lg = (ListGame *) gameList.head; + + for (nItem = 0; nItem < ((ListGame *) gameList.tailPred)->number; nItem++){ + char * st = GameListLineFull(lg->number, &lg->gameInfo); char *line = GameListLine(lg->number, &lg->gameInfo); if(filterString[0] == NULLCHAR || SearchPattern( line, filterString ) ) fprintf( f, "%s\n", st ); - free(st); free(line); - lg = (ListGame *) lg->node.succ; - } - + free(st); free(line); + lg = (ListGame *) lg->node.succ; + } + fclose(f); - return True; + return True; } - return False; -} + return False; +} //--------------------------------- Game-List options dialog ------------------------------------------ Widget gameListOptShell, listwidg; diff --git a/xoptions.c b/xoptions.c index 437ebae..31750ba 100644 --- a/xoptions.c +++ b/xoptions.c @@ -554,12 +554,27 @@ Option icsOptions[] = { { 0, 0, 0, NULL, (void*) &IcsOptionsOK, "", NULL, EndMark , "" } }; +char *modeNames[] = { N_("Exact match"), N_("Shown position is subset"), N_("Same material and Pawn chain"), N_("Same material"), NULL }; +char *modeValues[] = { "1", "2", "3", "4" }; +char *searchMode; + +int LoadOptionsOK() +{ + appData.searchMode = atoi(searchMode); + return 1; +} + Option loadOptions[] = { { 0, 0, 0, NULL, (void*) &appData.autoDisplayTags, "", NULL, CheckBox, N_("Auto-Display Tags") }, { 0, 0, 0, NULL, (void*) &appData.autoDisplayComment, "", NULL, CheckBox, N_("Auto-Display Comment") }, { 0, 0, 0, NULL, NULL, NULL, NULL, Label, N_("Auto-Play speed of loaded games\n(0 = instant, -1 = off):") }, { 0, -1, 10000000, NULL, (void*) &appData.timeDelay, "", NULL, Fractional, N_("Seconds per Move:") }, -{ 0, 0, 0, NULL, NULL, "", NULL, EndMark , "" } +{ 0, 0, 0, NULL, NULL, NULL, NULL, Label, N_("\nThresholds for position filtering in game list:") }, +{ 0, 0, 5000, NULL, (void*) &appData.eloThreshold1, "", NULL, Spin, N_("Elo of strongest player at least:") }, +{ 0, 0, 5000, NULL, (void*) &appData.eloThreshold2, "", NULL, Spin, N_("Elo of weakest player at least:") }, +{ 0, 0, 5000, NULL, (void*) &appData.dateThreshold, "", NULL, Spin, N_("No games before year:") }, +{ 1, 0, 180, NULL, (void*) &searchMode, (char*) modeNames, modeValues, ComboBox, N_("Seach mode:") }, +{ 0, 0, 0, NULL, (void*) &LoadOptionsOK, "", NULL, EndMark , "" } }; Option saveOptions[] = { @@ -1219,6 +1234,7 @@ void LoadOptionsProc(w, event, prms, nprms) String *prms; Cardinal *nprms; { + ASSIGN(searchMode, modeValues[appData.searchMode-1]); GenericPopUp(loadOptions, _("Load Game Options"), 0); } -- 1.7.0.4