From 8ee0292f69ffa3ebef03640cc5944d9c60e86bb8 Mon Sep 17 00:00:00 2001
From: H.G.Muller <hgm@hgm-xboard.(none)>
Date: Mon, 1 Sep 2014 12:03:58 +0200
Subject: [PATCH] Add final piece count to search criteria

The Load Options dialog now has a text field in which a range can be
entered (like 8-10). Position search will then only select games that
had their final number of pieces in this range.
---
 backend.c            |   29 ++++++++++++++++-------------
 common.h             |    2 ++
 dialogs.c            |    7 ++++++-
 winboard/resource.h  |    2 ++
 winboard/winboard.c  |    4 +++-
 winboard/winboard.rc |    8 +++++---
 winboard/woptions.c  |    5 +++++
 7 files changed, 39 insertions(+), 18 deletions(-)

diff --git a/backend.c b/backend.c
index f05359c..c81e0fd 100644
--- a/backend.c
+++ b/backend.c
@@ -12364,12 +12364,26 @@ QuickCompare (Board board, int *minCounts, int *maxCounts)
 int
 QuickScan (Board board, Move *move)
 {   // reconstruct game,and compare all positions in it
-    int cnt=0, stretch=0, total = MakePieceList(board, counts);
+    int cnt=0, stretch=0, found = -1, total = MakePieceList(board, counts);
     do {
 	int piece = move->piece;
 	int to = move->to, from = pieceList[piece];
+	if(!found) { // if already found just scan to game end for final piece count
+	  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<EmptySquare; i++) if(lastCounts[i] != counts[i]) { stretch = 0; break; } // reset if material changes
+	    if(stretch++ == 0) for(i=0; i<EmptySquare; i++) lastCounts[i] = counts[i]; // remember actual material
+	  } else stretch = 0;
+	  if(stretch && (appData.searchMode == 1 || stretch >= appData.stretch)) found = cnt + 1 - stretch;
+	  if(found && !appData.minPieces) return found;
+	}
 	if(piece <= Q_PROMO) { // special moves encoded by otherwise invalid piece numbers 1-4
-	  if(!piece) return -1;
+	  if(!piece) return (appData.minPieces && (total < appData.minPieces || total > appData.maxPieces) ? -1 : found);
 	  if(piece == Q_PROMO) { // promotion, encoded as (Q_PROMO, to) + (piece, promoType)
 	    piece = (++move)->piece;
 	    from = pieceList[piece];
@@ -12400,17 +12414,6 @@ QuickScan (Board board, Move *move)
 	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<EmptySquare; i++) if(lastCounts[i] != counts[i]) { stretch = 0; break; } // reset if material changes
-	    if(stretch++ == 0) for(i=0; i<EmptySquare; i++) lastCounts[i] = counts[i]; // remember actual material
-	} else stretch = 0;
-	if(stretch && (appData.searchMode == 1 || stretch >= appData.stretch)) return cnt + 1 - stretch;
 	move++;
     } while(1);
 }
diff --git a/common.h b/common.h
index 132c48d..6340c50 100644
--- a/common.h
+++ b/common.h
@@ -736,6 +736,8 @@ typedef struct {
     int dateThreshold;
     int searchMode;
     int stretch;
+    int minPieces;
+    int maxPieces;
     Boolean ignoreColors;
     Boolean findMirror;
     char *userName;
diff --git a/dialogs.c b/dialogs.c
index 23f8633..badad64 100644
--- a/dialogs.c
+++ b/dialogs.c
@@ -697,11 +697,14 @@ IcsOptionsProc ()
 static char *modeNames[] = { N_("Exact position match"), N_("Shown position is subset"), N_("Same material with exactly same Pawn chain"),
 		      N_("Same material"), N_("Material range (top board half optional)"), N_("Material difference (optional stuff balanced)"), NULL };
 static char *modeValues[] = { "1", "2", "3", "4", "5", "6" };
-static char *searchMode;
+static char *searchMode, *countRange;
 
 static int
 LoadOptionsOK ()
 {
+    appData.minPieces = appData.maxPieces = 0;
+    sscanf(countRange, "%d-%d", &appData.minPieces, &appData.maxPieces);
+    if(appData.maxPieces < appData.minPieces) appData.maxPieces = appData.minPieces;
     appData.searchMode = atoi(searchMode);
     return 1;
 }
@@ -718,6 +721,7 @@ static Option loadOptions[] = {
 { 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:") },
 { 0, 1,50,      NULL, (void*) &appData.stretch, "", NULL, Spin, N_("Minimum nr consecutive positions:") },
+{ 0, 0,197,     NULL, (void*) &countRange, "", NULL, TextBox,  "Final nr of pieces" },
 { 0, 0,205,     NULL, (void*) &searchMode, (char*) modeValues, modeNames, ComboBox, N_("Search mode:") },
 { 0, 0, 0,      NULL, (void*) &appData.ignoreColors, "", NULL, CheckBox, N_("Also match reversed colors") },
 { 0, 0, 0,      NULL, (void*) &appData.findMirror, "", NULL, CheckBox, N_("Also match left-right flipped position") },
@@ -727,6 +731,7 @@ static Option loadOptions[] = {
 void
 LoadOptionsPopUp (DialogClass parent)
 {
+   ASSIGN(countRange, "");
    ASSIGN(searchMode, modeValues[appData.searchMode-1]);
    GenericPopUp(loadOptions, _("Load Game Options"), TransientDlg, parent, MODAL, 0);
 }
diff --git a/winboard/resource.h b/winboard/resource.h
index 785bc84..1634a3d 100644
--- a/winboard/resource.h
+++ b/winboard/resource.h
@@ -639,6 +639,8 @@
 #define OPT_GameListFind                1981
 #define OPT_Grid                        1983
 #define IDM_LoadProg2                   1984
+#define OPT_Range                       1985
+#define OPT_Ranget                      1986
 
 
 // Next default values for new objects
diff --git a/winboard/winboard.c b/winboard/winboard.c
index 3738ddc..a31fa7c 100644
--- a/winboard/winboard.c
+++ b/winboard/winboard.c
@@ -260,7 +260,8 @@ int dialogItems[][42] = {
 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed, 
   OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors,   IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL }, 
 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, OPT_Exact, OPT_Subset, OPT_Struct, OPT_Material, OPT_Range, OPT_Difference,
-  OPT_elo1t, OPT_elo2t, OPT_datet, OPT_Stretch, OPT_Stretcht, OPT_Reversed, OPT_SearchMode, OPT_Mirror, OPT_thresholds, IDOK, IDCANCEL }, 
+  OPT_elo1t, OPT_elo2t, OPT_datet, OPT_Stretch, OPT_Stretcht, OPT_Reversed, OPT_SearchMode, OPT_Mirror, OPT_thresholds,
+  OPT_Ranget, IDOK, IDCANCEL }, 
 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,
   801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL }, 
 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 }, 
@@ -1811,6 +1812,7 @@ static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )
     RECT rc;
     SIZE sz;
 
+
     POINT pt;
     int backColor = whitePieceColor; 
     int foreColor = blackPieceColor;
diff --git a/winboard/winboard.rc b/winboard/winboard.rc
index 4455d32..7e06085 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, 170, 261
+DLG_LoadOptions DIALOG DISCARDABLE  10, 18, 170, 281
 STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
 CAPTION "Load Game Options"
 FONT 8, "MS Sans Serif"
@@ -125,8 +125,10 @@ BEGIN
                     BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,4,214,160,10
     CONTROL         "Also match &left-right mirror image",OPT_Mirror,"Button",
                     BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,4,227,160,10
-    PUSHBUTTON      "OK",IDOK,56,242,50,14,WS_GROUP
-    PUSHBUTTON      "Cancel",IDCANCEL,112,242,50,14
+    LTEXT           "final piece count",OPT_Ranget,46,242,94,8,NOT WS_GROUP
+    EDITTEXT        OPT_Range,16,242,28,14,ES_AUTOHSCROLL
+    PUSHBUTTON      "OK",IDOK,56,262,50,14,WS_GROUP
+    PUSHBUTTON      "Cancel",IDCANCEL,112,262,50,14
 END
 
 DLG_SaveOptions DIALOG DISCARDABLE  6, 17, 218, 119
diff --git a/winboard/woptions.c b/winboard/woptions.c
index 977c0c6..2c92e4d 100644
--- a/winboard/woptions.c
+++ b/winboard/woptions.c
@@ -2535,6 +2535,7 @@ LoadOptions(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
     SetDlgItemInt(hDlg, OPT_Stretch, appData.stretch, FALSE);
     CheckDlgButton(hDlg, OPT_Reversed, appData.ignoreColors);
     CheckDlgButton(hDlg, OPT_Mirror, appData.findMirror);
+    SetDlgItemText(hDlg, OPT_Range,  "");
     switch (appData.searchMode) {
     case 1:
       CheckDlgButton(hDlg, OPT_Exact, TRUE);
@@ -2579,6 +2580,10 @@ LoadOptions(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
       appData.searchMode = LoadOptionsWhichRadio(hDlg);
       appData.ignoreColors = IsDlgButtonChecked(hDlg, OPT_Reversed);
       appData.findMirror   = IsDlgButtonChecked(hDlg, OPT_Mirror);
+      appData.eloThreshold1 = GetDlgItemText(hDlg, OPT_Range, buf, MSG_SIZ);
+      appData.minPieces = appData.maxPieces = 0;
+      sscanf(buf, "%d-%d", appData.minPieces, appData.maxPieces);
+      if(appData.maxPieces < appData.minPieces) appData.maxPieces = appData.minPieces;
       EndDialog(hDlg, TRUE);
       return TRUE;
 
-- 
1.7.0.4