Rev 108 | Go to most recent revision | Details | Last modification | View Log | RSS feed
| Rev | Author | Line No. | Line | 
|---|---|---|---|
| 33 | pmbaty | 1 | #include <math.h> | 
| 2 | #include "chess.h" | ||
| 3 | #include "data.h" | ||
| 4 | #if defined(UNIX) | ||
| 5 | #  include <unistd.h> | ||
| 6 | #endif | ||
| 7 | /* last modified 05/08/14 */ | ||
| 8 | /* | ||
| 9 |  ******************************************************************************* | ||
| 10 |  *                                                                             * | ||
| 11 |  *   Book() is used to determine if the current position is in the book data-  * | ||
| 12 |  *   base.  It simply takes the set of moves produced by root_moves() and then * | ||
| 13 |  *   tries each position's hash key to see if it can be found in the data-     * | ||
| 14 |  *   base.  If so, such a move represents a "book move."  The set of flags is  * | ||
| 15 |  *   used to decide on a sub-set of moves to be used as the "book move pool"   * | ||
| 16 |  *   from which a move is chosen randomly.                                     * | ||
| 17 |  *                                                                             * | ||
| 18 |  *   The format of a book position is as follows:                              * | ||
| 19 |  *                                                                             * | ||
| 20 |  *   64 bits:  hash key for this position.                                     * | ||
| 21 |  *                                                                             * | ||
| 22 |  *    8 bits:  flag bits defined as  follows:                                  * | ||
| 23 |  *                                                                             * | ||
| 24 |  *      0000 0001  ?? flagged move                       (0x01)                * | ||
| 25 |  *      0000 0010   ? flagged move                       (0x02)                * | ||
| 26 |  *      0000 0100   = flagged move                       (0x04)                * | ||
| 27 |  *      0000 1000   ! flagged move                       (0x08)                * | ||
| 28 |  *      0001 0000  !! flagged move                       (0x10)                * | ||
| 29 |  *      0010 0000     black won at least 1 game          (0x20)                * | ||
| 30 |  *      0100 0000     at least one game was drawn        (0x40)                * | ||
| 31 |  *      1000 0000     white won at least 1 game          (0x80)                * | ||
| 32 |  *                                                                             * | ||
| 33 |  *   24 bits:  number of games this move was played.                           * | ||
| 34 |  *                                                                             * | ||
| 35 |  *   32 bits:  learned value (floating point).                                 * | ||
| 36 |  *                                                                             * | ||
| 37 |  *     (Note:  counts are normalized to a max of 255.                          * | ||
| 38 |  *                                                                             * | ||
| 39 |  ******************************************************************************* | ||
| 40 |  */ | ||
| 41 | #define BAD_MOVE  0x02 | ||
| 42 | #define GOOD_MOVE 0x08 | ||
| 43 | int Book(TREE * RESTRICT tree, int wtm, int root_list_done) { | ||
| 44 | static int book_moves[200]; | ||
| 45 | static BOOK_POSITION start_moves[200]; | ||
| 46 | static uint64_t selected_key[200]; | ||
| 47 | static int selected[200]; | ||
| 48 | static int selected_order_played[200]; | ||
| 49 | static float selected_value[200]; // Pierre-Marie Baty -- fixed type | ||
| 50 | static int selected_status[200], selected_percent[200], | ||
| 51 | book_development[200]; | ||
| 52 | static int bs_played[200], bs_percent[200]; | ||
| 53 | static int book_status[200], evaluations[200], bs_learn[200]; | ||
| 54 | static float bs_value[200], total_value; | ||
| 55 | static uint64_t book_key[200], bs_key[200]; | ||
| 56 | int m1_status, forced = 0, total_percent, play_percentage = 0; | ||
| 57 | float tempr; | ||
| 58 | int done, i, j, last_move, temp, which, minlv = 999999, maxlv = -999999; | ||
| 59 | int maxp = -999999, minev = 999999, maxev = -999999; | ||
| 60 | int nflagged, im, value, np, book_ponder_move; | ||
| 61 | int cluster, scluster, test; | ||
| 62 | unsigned char buf32[4]; | ||
| 63 | uint64_t temp_hash_key, common, tempk; | ||
| 64 | int key, nmoves, num_selected, st; | ||
| 65 | int percent_played, total_played, total_moves, smoves; | ||
| 66 | int distribution; | ||
| 67 | int initial_development; | ||
| 68 | char *kibitz_p; | ||
| 69 | |||
| 70 | /* | ||
| 71 |  ************************************************************ | ||
| 72 |  *                                                          * | ||
| 73 |  *  If we have been out of book for several moves, return   * | ||
| 74 |  *  and start the normal tree search.                       * | ||
| 75 |  *                                                          * | ||
| 76 |  ************************************************************ | ||
| 77 |  */ | ||
| 78 | if (moves_out_of_book > 6) | ||
| 79 | return 0; | ||
| 80 | /* | ||
| 81 |  ************************************************************ | ||
| 82 |  *                                                          * | ||
| 83 |  *  Position is known, read the start book file and save    * | ||
| 84 |  *  each move found.  These will be used later to augment   * | ||
| 85 |  *  the flags in the normal book to offer better control.   * | ||
| 86 |  *                                                          * | ||
| 87 |  ************************************************************ | ||
| 88 |  */ | ||
| 89 | if (!root_list_done) | ||
| 90 | RootMoveList(wtm); | ||
| 91 | test = HashKey >> 49; | ||
| 92 | smoves = 0; | ||
| 93 | if (books_file) { | ||
| 94 | fseek(books_file, test * sizeof(int), SEEK_SET); | ||
| 95 | fread(buf32, 4, 1, books_file); | ||
| 96 | key = BookIn32(buf32); | ||
| 97 | if (key > 0) { | ||
| 98 | fseek(books_file, key, SEEK_SET); | ||
| 99 | fread(buf32, 4, 1, books_file); | ||
| 100 | scluster = BookIn32(buf32); | ||
| 101 | BookClusterIn(books_file, scluster, book_buffer); | ||
| 102 | for (im = 0; im < n_root_moves; im++) { | ||
| 103 | common = HashKey & ((uint64_t) 65535 << 48); | ||
| 104 | MakeMove(tree, 1, root_moves[im].move, wtm); | ||
| 105 | if (Repeat(tree, 2)) { | ||
| 106 | UnmakeMove(tree, 1, root_moves[im].move, wtm); | ||
| 107 | return 0; | ||
| 108 |         } | ||
| 109 | temp_hash_key = (wtm) ? HashKey : ~HashKey; | ||
| 110 | temp_hash_key = (temp_hash_key & ~((uint64_t) 65535 << 48)) | common; | ||
| 111 | for (i = 0; i < scluster; i++) | ||
| 112 | if (!(temp_hash_key ^ book_buffer[i].position)) { | ||
| 113 | start_moves[smoves++] = book_buffer[i]; | ||
| 114 | break; | ||
| 115 |           } | ||
| 116 | UnmakeMove(tree, 1, root_moves[im].move, wtm); | ||
| 117 |       } | ||
| 118 |     } | ||
| 119 |   } | ||
| 120 | /* | ||
| 121 |  ************************************************************ | ||
| 122 |  *                                                          * | ||
| 123 |  *  Position is known, read in the appropriate cluster.     * | ||
| 124 |  *  Note that this cluster will have all possible book      * | ||
| 125 |  *  moves from current position in it (as well as others of * | ||
| 126 |  *  course.)                                                * | ||
| 127 |  *                                                          * | ||
| 128 |  ************************************************************ | ||
| 129 |  */ | ||
| 130 | test = HashKey >> 49; | ||
| 131 | if (book_file) { | ||
| 132 | fseek(book_file, test * sizeof(int), SEEK_SET); | ||
| 133 | fread(buf32, 4, 1, book_file); | ||
| 134 | key = BookIn32(buf32); | ||
| 135 | if (key > 0) { | ||
| 136 | book_learn_seekto = key; | ||
| 137 | fseek(book_file, key, SEEK_SET); | ||
| 138 | fread(buf32, 4, 1, book_file); | ||
| 139 | cluster = BookIn32(buf32); | ||
| 140 | BookClusterIn(book_file, cluster, book_buffer); | ||
| 141 | } else | ||
| 142 | cluster = 0; | ||
| 143 | if (!cluster && !smoves) | ||
| 144 | return 0; | ||
| 145 | /* | ||
| 146 |  ************************************************************ | ||
| 147 |  *                                                          * | ||
| 148 |  *  Now add any moves from books.bin to the end of the      * | ||
| 149 |  *  cluster so that they will be played even if not in the  * | ||
| 150 |  *  regular database of moves.                              * | ||
| 151 |  *                                                          * | ||
| 152 |  ************************************************************ | ||
| 153 |  */ | ||
| 154 | for (i = 0; i < smoves; i++) { | ||
| 155 | for (j = 0; j < cluster; j++) | ||
| 156 | if (!(book_buffer[j].position ^ start_moves[i].position)) | ||
| 157 | break; | ||
| 158 | if (j >= cluster) { | ||
| 159 | book_buffer[cluster] = start_moves[i]; | ||
| 160 | book_buffer[cluster].status_played = | ||
| 161 | book_buffer[cluster].status_played & 037700000000; | ||
| 162 |         cluster++; | ||
| 163 |       } | ||
| 164 |     } | ||
| 165 | /* | ||
| 166 |  ************************************************************ | ||
| 167 |  *                                                          * | ||
| 168 |  *  First cycle through the root move list, make each move, * | ||
| 169 |  *  and see if the resulting hash key is in the book        * | ||
| 170 |  *  database.                                               * | ||
| 171 |  *                                                          * | ||
| 172 |  ************************************************************ | ||
| 173 |  */ | ||
| 174 | initial_development = tree->score_mg; | ||
| 175 | EvaluateDevelopment(tree, 1, wtm); | ||
| 176 | initial_development = tree->score_mg - initial_development; | ||
| 177 | total_moves = 0; | ||
| 178 | nmoves = 0; | ||
| 179 | for (im = 0; im < n_root_moves; im++) { | ||
| 180 | common = HashKey & ((uint64_t) 65535 << 48); | ||
| 181 | MakeMove(tree, 1, root_moves[im].move, wtm); | ||
| 182 | if (Repeat(tree, 2)) { | ||
| 183 | UnmakeMove(tree, 1, root_moves[im].move, wtm); | ||
| 184 | return 0; | ||
| 185 |       } | ||
| 186 | temp_hash_key = (wtm) ? HashKey : ~HashKey; | ||
| 187 | temp_hash_key = (temp_hash_key & ~((uint64_t) 65535 << 48)) | common; | ||
| 188 | for (i = 0; i < cluster; i++) { | ||
| 189 | if (!(temp_hash_key ^ book_buffer[i].position)) { | ||
| 190 | book_status[nmoves] = book_buffer[i].status_played >> 24; | ||
| 191 | bs_played[nmoves] = book_buffer[i].status_played & 077777777; | ||
| 192 | bs_learn[nmoves] = (int) (book_buffer[i].learn * 100.0); | ||
| 193 | if (puzzling) | ||
| 194 | bs_played[nmoves] += 1; | ||
| 195 | tree->curmv[1] = root_moves[im].move; | ||
| 196 | if (!Captured(root_moves[im].move)) { | ||
| 197 | book_development[nmoves] = tree->score_mg; | ||
| 198 | EvaluateDevelopment(tree, 2, wtm); | ||
| 199 | book_development[nmoves] = | ||
| 200 | tree->score_mg - book_development[nmoves]; | ||
| 201 | } else | ||
| 202 | book_development[nmoves] = 0; | ||
| 203 | total_moves += bs_played[nmoves]; | ||
| 204 | evaluations[nmoves] = Evaluate(tree, 2, wtm, -99999, 99999); | ||
| 205 | evaluations[nmoves] -= MaterialSTM(wtm); | ||
| 206 | bs_percent[nmoves] = 0; | ||
| 207 | for (j = 0; j < smoves; j++) { | ||
| 208 | if (!(book_buffer[i].position ^ start_moves[j].position)) { | ||
| 209 | book_status[nmoves] |= start_moves[j].status_played >> 24; | ||
| 210 | bs_percent[nmoves] = start_moves[j].status_played & 077777777; | ||
| 211 | break; | ||
| 212 |             } | ||
| 213 |           } | ||
| 214 | book_moves[nmoves] = root_moves[im].move; | ||
| 215 | book_key[nmoves] = temp_hash_key; | ||
| 216 |           nmoves++; | ||
| 217 | break; | ||
| 218 |         } | ||
| 219 |       } | ||
| 220 | UnmakeMove(tree, 1, root_moves[im].move, wtm); | ||
| 221 |     } | ||
| 222 | if (!nmoves) | ||
| 223 | return 0; | ||
| 224 | book_learn_nmoves = nmoves; | ||
| 225 | /* | ||
| 226 |  ************************************************************ | ||
| 227 |  *                                                          * | ||
| 228 |  *  If any moves have a very bad or a very good learn       * | ||
| 229 |  *  value, set the appropriate ? or ! flag so the move be   * | ||
| 230 |  *  played or avoided as appropriate.                       * | ||
| 231 |  *                                                          * | ||
| 232 |  ************************************************************ | ||
| 233 |  */ | ||
| 234 | for (i = 0; i < nmoves; i++) | ||
| 235 | if (!(book_status[i] & BAD_MOVE)) | ||
| 236 | maxp = Max(maxp, bs_played[i]); | ||
| 237 | for (i = 0; i < nmoves; i++) { | ||
| 238 | if (bs_learn[i] <= LEARN_COUNTER_BAD && !bs_percent[i] | ||
| 239 | && !(book_status[i] & 0x18)) | ||
| 240 | book_status[i] |= BAD_MOVE; | ||
| 241 | if (wtm && !(book_status[i] & 0x80) && !bs_percent[i] | ||
| 242 | && !(book_status[i] & 0x18)) | ||
| 243 | book_status[i] |= BAD_MOVE; | ||
| 244 | if (!wtm && !(book_status[i] & 0x20) && !bs_percent[i] | ||
| 245 | && !(book_status[i] & 0x18)) | ||
| 246 | book_status[i] |= BAD_MOVE; | ||
| 247 | if (bs_played[i] < maxp / 10 && !bs_percent[i] && book_random && | ||
| 248 | !(book_status[i] & 0x18)) | ||
| 249 | book_status[i] |= BAD_MOVE; | ||
| 250 | if (bs_learn[i] >= LEARN_COUNTER_GOOD && !(book_status[i] & 0x03)) | ||
| 251 | book_status[i] |= GOOD_MOVE; | ||
| 252 | if (bs_percent[i]) | ||
| 253 | book_status[i] |= GOOD_MOVE; | ||
| 254 |     } | ||
| 255 | /* | ||
| 256 |  ************************************************************ | ||
| 257 |  *                                                          * | ||
| 258 |  *  We have the book moves, now it's time to decide how     * | ||
| 259 |  *  they are supposed to be sorted and compute the sort     * | ||
| 260 |  *  index.                                                  * | ||
| 261 |  *                                                          * | ||
| 262 |  ************************************************************ | ||
| 263 |  */ | ||
| 264 | for (i = 0; i < nmoves; i++) { | ||
| 265 | if (!(book_status[i] & BAD_MOVE)) { | ||
| 266 | minlv = Min(minlv, bs_learn[i]); | ||
| 267 | maxlv = Max(maxlv, bs_learn[i]); | ||
| 268 | minev = Min(minev, evaluations[i]); | ||
| 269 | maxev = Max(maxev, evaluations[i]); | ||
| 270 | maxp = Max(maxp, bs_played[i]); | ||
| 271 |       } | ||
| 272 |     } | ||
| 273 |     maxp++; | ||
| 274 | for (i = 0; i < nmoves; i++) { | ||
| 275 | bs_value[i] = 1; | ||
| 276 | bs_value[i] += bs_played[i] / (float) maxp * 1000.0f * book_weight_freq; // Pierre-Marie Baty -- added float suffix | ||
| 277 | |||
| 278 | if (minlv < maxlv) | ||
| 279 | bs_value[i] += | ||
| 280 | (bs_learn[i] - minlv) / (float) (maxlv - | ||
| 281 | minlv) * 1000.0f * book_weight_learn; // Pierre-Marie Baty -- added float suffix | ||
| 282 | if (minev < maxev) | ||
| 283 | bs_value[i] += | ||
| 284 | (evaluations[i] - minev) / (float) (Max(maxev - minev, | ||
| 285 | 50)) * 1000.0f * book_weight_eval; // Pierre-Marie Baty -- added float suffix | ||
| 286 |     } | ||
| 287 | total_played = total_moves; | ||
| 288 | /* | ||
| 289 |  ************************************************************ | ||
| 290 |  *                                                          * | ||
| 291 |  *  If there are any ! moves, make their popularity count   * | ||
| 292 |  *  huge since they have to be considered.                  * | ||
| 293 |  *                                                          * | ||
| 294 |  ************************************************************ | ||
| 295 |  */ | ||
| 296 | for (i = 0; i < nmoves; i++) | ||
| 297 | if (book_status[i] & 0x18) | ||
| 298 | break; | ||
| 299 | if (i < nmoves) { | ||
| 300 | for (i = 0; i < nmoves; i++) { | ||
| 301 | if (book_status[i] & 0x18) | ||
| 302 | bs_value[i] += 8000.0; | ||
| 303 | if (!(book_status[i] & 0x03)) | ||
| 304 | bs_value[i] += 4000.0; | ||
| 305 |       } | ||
| 306 |     } | ||
| 307 | /* | ||
| 308 |  ************************************************************ | ||
| 309 |  *                                                          * | ||
| 310 |  *  Now sort the moves based on the complete sort value.    * | ||
| 311 |  *                                                          * | ||
| 312 |  ************************************************************ | ||
| 313 |  */ | ||
| 314 | if (nmoves) | ||
| 315 | do { | ||
| 316 | done = 1; | ||
| 317 | for (i = 0; i < nmoves - 1; i++) { | ||
| 318 | if (bs_percent[i] < bs_percent[i + 1] | ||
| 319 | || (bs_percent[i] == bs_percent[i + 1] | ||
| 320 | && bs_value[i] < bs_value[i + 1])) { | ||
| 321 | temp = bs_played[i]; // Pierre-Marie Baty -- the wrong variable type was used | ||
| 322 | bs_played[i] = bs_played[i + 1]; | ||
| 323 | bs_played[i + 1] = temp; | ||
| 324 | tempr = bs_value[i]; | ||
| 325 | bs_value[i] = bs_value[i + 1]; | ||
| 326 | bs_value[i + 1] = tempr; | ||
| 327 | temp = evaluations[i]; | ||
| 328 | evaluations[i] = evaluations[i + 1]; | ||
| 329 | evaluations[i + 1] = temp; | ||
| 330 | temp = bs_learn[i]; | ||
| 331 | bs_learn[i] = bs_learn[i + 1]; | ||
| 332 | bs_learn[i + 1] = temp; | ||
| 333 | temp = book_development[i]; | ||
| 334 | book_development[i] = book_development[i + 1]; | ||
| 335 | book_development[i + 1] = temp; | ||
| 336 | temp = book_moves[i]; | ||
| 337 | book_moves[i] = book_moves[i + 1]; | ||
| 338 | book_moves[i + 1] = temp; | ||
| 339 | temp = book_status[i]; | ||
| 340 | book_status[i] = book_status[i + 1]; | ||
| 341 | book_status[i + 1] = temp; | ||
| 342 | temp = bs_percent[i]; | ||
| 343 | bs_percent[i] = bs_percent[i + 1]; | ||
| 344 | bs_percent[i + 1] = temp; | ||
| 345 | tempk = book_key[i]; | ||
| 346 | book_key[i] = book_key[i + 1]; | ||
| 347 | book_key[i + 1] = tempk; | ||
| 348 | done = 0; | ||
| 349 |           } | ||
| 350 |         } | ||
| 351 | } while (!done); | ||
| 352 | /* | ||
| 353 |  ************************************************************ | ||
| 354 |  *                                                          * | ||
| 355 |  *  Display the book moves, and total counts, etc. if the   * | ||
| 356 |  *  operator has requested it.                              * | ||
| 357 |  *                                                          * | ||
| 358 |  ************************************************************ | ||
| 359 |  */ | ||
| 360 | if (show_book) { | ||
| 361 | Print(128, " after screening, the following moves can be played\n"); | ||
| 362 | Print(128, | ||
| 363 | " move played %% score learn " "sortv P%% P\n"); | ||
| 364 | for (i = 0; i < nmoves; i++) { | ||
| 365 | Print(128, "%6s", OutputMove(tree, book_moves[i], 1, wtm)); | ||
| 366 | st = book_status[i]; | ||
| 367 | if (st & 0x1f) { | ||
| 368 | if (st & 0x01) | ||
| 369 | Print(128, "??"); | ||
| 370 | else if (st & 0x02) | ||
| 371 | Print(128, "? "); | ||
| 372 | else if (st & 0x04) | ||
| 373 | Print(128, "= "); | ||
| 374 | else if (st & 0x08) | ||
| 375 | Print(128, "! "); | ||
| 376 | else if (st & 0x10) | ||
| 377 | Print(128, "!!"); | ||
| 378 | } else | ||
| 379 | Print(128, " "); | ||
| 380 | Print(128, " %6d", bs_played[i]); | ||
| 381 | Print(128, " %3d", 100 * bs_played[i] / Max(total_moves, 1)); | ||
| 382 | Print(128, "%s", DisplayEvaluation(evaluations[i], wtm)); | ||
| 383 | Print(128, "%9.2f", (float) bs_learn[i] / 100.0); | ||
| 384 | Print(128, " %9.1f", bs_value[i]); | ||
| 385 | Print(128, " %3d", bs_percent[i]); | ||
| 386 | if ((book_status[i] & book_accept_mask && | ||
| 387 | !(book_status[i] & book_reject_mask)) | ||
| 388 | || (!(book_status[i] & book_reject_mask) && (bs_percent[i] | ||
| 389 | || book_status[i] & 0x18 || (wtm && book_status[i] & 0x80) | ||
| 390 | || (!wtm && book_status[i] & 0x20)))) | ||
| 391 | Print(128, " Y"); | ||
| 392 |         else | ||
| 393 | Print(128, " N"); | ||
| 394 | Print(128, "\n"); | ||
| 395 |       } | ||
| 396 |     } | ||
| 397 | /* | ||
| 398 |  ************************************************************ | ||
| 399 |  *                                                          * | ||
| 400 |  *  Check for book moves with the play % value set.  if     * | ||
| 401 |  *  there are any such moves, then exclude all moves that   * | ||
| 402 |  *  do not have a play % or a !/!! flag set.                * | ||
| 403 |  *                                                          * | ||
| 404 |  ************************************************************ | ||
| 405 |  */ | ||
| 406 | for (i = 0; i < nmoves; i++) | ||
| 407 | if (bs_percent[i]) | ||
| 408 | play_percentage = 1; | ||
| 409 | /* | ||
| 410 |  ************************************************************ | ||
| 411 |  *                                                          * | ||
| 412 |  *  Delete ? and ?? moves first, which includes those moves * | ||
| 413 |  *  with bad learned results.  Here is where we also        * | ||
| 414 |  *  exclude moves with no play % if we find at least one    * | ||
| 415 |  *  with a non-zero value.                                  * | ||
| 416 |  *                                                          * | ||
| 417 |  ************************************************************ | ||
| 418 |  */ | ||
| 419 | num_selected = 0; | ||
| 420 | if (!play_percentage) { | ||
| 421 | for (i = 0; i < nmoves; i++) | ||
| 422 | if (!(book_status[i] & 0x03) || bs_percent[i]) { | ||
| 423 | selected_status[num_selected] = book_status[i]; | ||
| 424 | selected_order_played[num_selected] = bs_played[i]; | ||
| 425 | selected_value[num_selected] = bs_value[i]; | ||
| 426 | selected_percent[num_selected] = bs_percent[i]; | ||
| 427 | selected_key[num_selected] = book_key[i]; | ||
| 428 | selected[num_selected++] = book_moves[i]; | ||
| 429 |         } | ||
| 430 | } else { | ||
| 431 | for (i = 0; i < nmoves; i++) | ||
| 432 | if (bs_percent[i]) { | ||
| 433 | selected_status[num_selected] = book_status[i]; | ||
| 434 | selected_order_played[num_selected] = bs_played[i]; | ||
| 435 | selected_value[num_selected] = bs_value[i]; | ||
| 436 | selected_percent[num_selected] = bs_percent[i]; | ||
| 437 | selected_key[num_selected] = book_key[i]; | ||
| 438 | selected[num_selected++] = book_moves[i]; | ||
| 439 |         } | ||
| 440 |     } | ||
| 441 | for (i = 0; i < num_selected; i++) { | ||
| 442 | book_status[i] = selected_status[i]; | ||
| 443 | bs_played[i] = selected_order_played[i]; | ||
| 444 | bs_value[i] = selected_value[i]; | ||
| 445 | bs_percent[i] = selected_percent[i]; | ||
| 446 | book_moves[i] = selected[i]; | ||
| 447 |     } | ||
| 448 | nmoves = num_selected; | ||
| 449 | /* | ||
| 450 |  ************************************************************ | ||
| 451 |  *                                                          * | ||
| 452 |  *  If this is a real search (not a puzzling search to      * | ||
| 453 |  *  find a move by the opponent to ponder) then we need to  * | ||
| 454 |  *  set up the whisper info for later.                      * | ||
| 455 |  *                                                          * | ||
| 456 |  ************************************************************ | ||
| 457 |  */ | ||
| 458 | if (!puzzling) | ||
| 459 | do { | ||
| 460 | kibitz_text[0] = '\0'; | ||
| 461 | if (!nmoves) | ||
| 462 | break; | ||
| 463 | sprintf(kibitz_text, "book moves ("); | ||
| 464 | kibitz_p = kibitz_text + strlen(kibitz_text); | ||
| 465 | for (i = 0; i < nmoves; i++) { | ||
| 466 | sprintf(kibitz_p, "%s %d%%", OutputMove(tree, book_moves[i], 1, | ||
| 467 | wtm), 100 * bs_played[i] / Max(total_played, 1)); | ||
| 468 | kibitz_p = kibitz_text + strlen(kibitz_text); | ||
| 469 | if (i < nmoves - 1) { | ||
| 470 | sprintf(kibitz_p, ", "); | ||
| 471 | kibitz_p = kibitz_text + strlen(kibitz_text); | ||
| 472 |           } | ||
| 473 |         } | ||
| 474 | sprintf(kibitz_p, ")\n"); | ||
| 475 | } while (0); | ||
| 476 | /* | ||
| 477 |  ************************************************************ | ||
| 478 |  *                                                          * | ||
| 479 |  *  Now select a move from the set of moves just found. Do  * | ||
| 480 |  *  this in three distinct passes:  (1) look for !! moves;  * | ||
| 481 |  *  (2) look for ! moves;  (3) look for any other moves.    * | ||
| 482 |  *  Note: book_accept_mask *should* have a bit set for any  * | ||
| 483 |  *  move that is selected, including !! and ! type moves so * | ||
| 484 |  *  that they *can* be excluded if desired.  Note also that * | ||
| 485 |  *  book_reject_mask should have ?? and ? set (at a         * | ||
| 486 |  *  minimum) to exclude these types of moves.               * | ||
| 487 |  *                                                          * | ||
| 488 |  ************************************************************ | ||
| 489 |  */ | ||
| 490 | num_selected = 0; | ||
| 491 | if (!num_selected && !puzzling) | ||
| 492 | if (book_accept_mask & 16) | ||
| 493 | for (i = 0; i < nmoves; i++) | ||
| 494 | if (book_status[i] & 16) { | ||
| 495 | forced = 1; | ||
| 496 | selected_status[num_selected] = book_status[i]; | ||
| 497 | selected_order_played[num_selected] = bs_played[i]; | ||
| 498 | selected_value[num_selected] = bs_value[i]; | ||
| 499 | selected_key[num_selected] = book_key[i]; | ||
| 500 | selected[num_selected++] = book_moves[i]; | ||
| 501 |           } | ||
| 502 | if (!num_selected && !puzzling) | ||
| 503 | if (book_accept_mask & 8) | ||
| 504 | for (i = 0; i < nmoves; i++) | ||
| 505 | if (book_status[i] & 8) { | ||
| 506 | forced = 1; | ||
| 507 | selected_status[num_selected] = book_status[i]; | ||
| 508 | selected_order_played[num_selected] = bs_played[i]; | ||
| 509 | selected_value[num_selected] = bs_value[i]; | ||
| 510 | selected_key[num_selected] = book_key[i]; | ||
| 511 | selected[num_selected++] = book_moves[i]; | ||
| 512 |           } | ||
| 513 | if (!num_selected && !puzzling) | ||
| 514 | if (book_accept_mask & 4) | ||
| 515 | for (i = 0; i < nmoves; i++) | ||
| 516 | if (book_status[i] & 4) { | ||
| 517 | selected_status[num_selected] = book_status[i]; | ||
| 518 | selected_order_played[num_selected] = bs_played[i]; | ||
| 519 | selected_value[num_selected] = bs_value[i]; | ||
| 520 | selected_key[num_selected] = book_key[i]; | ||
| 521 | selected[num_selected++] = book_moves[i]; | ||
| 522 |           } | ||
| 523 | if (!num_selected && !puzzling) | ||
| 524 | for (i = 0; i < nmoves; i++) | ||
| 525 | if (book_status[i] & book_accept_mask) { | ||
| 526 | selected_status[num_selected] = book_status[i]; | ||
| 527 | selected_order_played[num_selected] = bs_played[i]; | ||
| 528 | selected_value[num_selected] = bs_value[i]; | ||
| 529 | selected_key[num_selected] = book_key[i]; | ||
| 530 | selected[num_selected++] = book_moves[i]; | ||
| 531 |         } | ||
| 532 | if (!num_selected) | ||
| 533 | for (i = 0; i < nmoves; i++) { | ||
| 534 | selected_status[num_selected] = book_status[i]; | ||
| 535 | selected_order_played[num_selected] = bs_played[i]; | ||
| 536 | selected_value[num_selected] = bs_value[i]; | ||
| 537 | selected_key[num_selected] = book_key[i]; | ||
| 538 | selected[num_selected++] = book_moves[i]; | ||
| 539 |       } | ||
| 540 | if (!num_selected) | ||
| 541 | return 0; | ||
| 542 | for (i = 0; i < num_selected; i++) { | ||
| 543 | book_status[i] = selected_status[i]; | ||
| 544 | book_moves[i] = selected[i]; | ||
| 545 | bs_played[i] = selected_order_played[i]; | ||
| 546 | bs_value[i] = selected_value[i]; | ||
| 547 | bs_key[i] = selected_key[i]; | ||
| 548 |     } | ||
| 549 | nmoves = num_selected; | ||
| 550 | if (nmoves == 0) | ||
| 551 | return 0; | ||
| 552 | Print(128, " book moves {"); | ||
| 553 | for (i = 0; i < nmoves; i++) { | ||
| 554 | Print(128, "%s", OutputMove(tree, book_moves[i], 1, wtm)); | ||
| 555 | if (i < nmoves - 1) | ||
| 556 | Print(128, ", "); | ||
| 557 |     } | ||
| 558 | Print(128, "}\n"); | ||
| 559 | nflagged = 0; | ||
| 560 | for (i = 0; i < nmoves; i++) | ||
| 561 | if (book_status[i] & 8) | ||
| 562 |         nflagged++; | ||
| 563 | nmoves = Max(Min(nmoves, book_selection_width), nflagged); | ||
| 564 | if (show_book) { | ||
| 565 | Print(128, " moves considered {"); | ||
| 566 | for (i = 0; i < nmoves; i++) { | ||
| 567 | Print(128, "%s", OutputMove(tree, book_moves[i], 1, wtm)); | ||
| 568 | if (i < nmoves - 1) | ||
| 569 | Print(128, ", "); | ||
| 570 |       } | ||
| 571 | Print(128, "}\n"); | ||
| 572 |     } | ||
| 573 | /* | ||
| 574 |  ************************************************************ | ||
| 575 |  *                                                          * | ||
| 576 |  *  We have the book moves, if any have specified percents  * | ||
| 577 |  *  for play, then adjust the bs_value[] to reflect this    * | ||
| 578 |  *  percentage.                                             * | ||
| 579 |  *                                                          * | ||
| 580 |  ************************************************************ | ||
| 581 |  */ | ||
| 582 | total_value = 0.0; | ||
| 583 | total_percent = 0; | ||
| 584 | for (i = 0; i < nmoves; i++) { | ||
| 585 | if (!bs_percent[i]) | ||
| 586 | total_value += bs_value[i]; | ||
| 587 | total_percent += bs_percent[i]; | ||
| 588 |     } | ||
| 589 | if (fabs(total_value) < 0.0001) | ||
| 590 | total_value = 1000.0; | ||
| 591 | total_percent = (total_percent > 99) ? 99 : total_percent; | ||
| 592 | for (i = 0; i < nmoves; i++) | ||
| 593 | if (bs_percent[i]) | ||
| 594 | bs_value[i] = | ||
| 595 | total_value / (1.0f - | ||
| 596 | (float) total_percent / 100.0f) * (float) bs_percent[i] / 100.0f; // Pierre-Marie Baty -- added float suffixes | ||
| 597 | /* | ||
| 598 |  ************************************************************ | ||
| 599 |  *                                                          * | ||
| 600 |  *  Display the book moves, and total counts, etc. if the   * | ||
| 601 |  *  operator has requested it.                              * | ||
| 602 |  *                                                          * | ||
| 603 |  ************************************************************ | ||
| 604 |  */ | ||
| 605 | if (show_book) { | ||
| 606 | Print(128, " move played %% score sortv P%% P\n"); | ||
| 607 | for (i = 0; i < nmoves; i++) { | ||
| 608 | Print(128, "%6s", OutputMove(tree, book_moves[i], 1, wtm)); | ||
| 609 | st = book_status[i]; | ||
| 610 | if (st & 0x1f) { | ||
| 611 | if (st & 0x01) | ||
| 612 | Print(128, "??"); | ||
| 613 | else if (st & 0x02) | ||
| 614 | Print(128, "? "); | ||
| 615 | else if (st & 0x04) | ||
| 616 | Print(128, "= "); | ||
| 617 | else if (st & 0x08) | ||
| 618 | Print(128, "! "); | ||
| 619 | else if (st & 0x10) | ||
| 620 | Print(128, "!!"); | ||
| 621 | } else | ||
| 622 | Print(128, " "); | ||
| 623 | Print(128, " %6d", bs_played[i]); | ||
| 624 | Print(128, " %3d", 100 * bs_played[i] / Max(total_moves, 1)); | ||
| 625 | Print(128, "%s", DisplayEvaluation(evaluations[i], wtm)); | ||
| 626 | Print(128, " %9.1f", bs_value[i]); | ||
| 627 | Print(128, " %3d", bs_percent[i]); | ||
| 628 | if ((book_status[i] & book_accept_mask && | ||
| 629 | !(book_status[i] & book_reject_mask)) | ||
| 630 | || (!(book_status[i] & book_reject_mask) && ((wtm && | ||
| 631 | book_status[i] & 0x80) || (!wtm && | ||
| 632 | book_status[i] & 0x20)))) | ||
| 633 | Print(128, " Y"); | ||
| 634 |         else | ||
| 635 | Print(128, " N"); | ||
| 636 | Print(128, "\n"); | ||
| 637 |       } | ||
| 638 |     } | ||
| 639 | /* | ||
| 640 |  ************************************************************ | ||
| 641 |  *                                                          * | ||
| 642 |  *  If random=0, then we search the set of legal book moves * | ||
| 643 |  *  with the normal search engine (but with a short time    * | ||
| 644 |  *  limit) to choose among them.                            * | ||
| 645 |  *                                                          * | ||
| 646 |  ************************************************************ | ||
| 647 |  */ | ||
| 648 | if (nmoves && (!puzzling || mode != tournament_mode)) { | ||
| 649 | np = bs_played[nmoves - 1]; | ||
| 650 | if (!puzzling && (!book_random || (mode == tournament_mode && | ||
| 651 | np < book_search_trigger))) { | ||
| 652 | if (!forced) { | ||
| 653 | n_root_moves = nmoves; | ||
| 654 | for (i = 0; i < n_root_moves; i++) { | ||
| 655 | root_moves[i].move = book_moves[i]; | ||
| 656 | root_moves[i].status = 0; | ||
| 657 |           } | ||
| 658 | last_pv.pathd = 0; | ||
| 659 | booking = 1; | ||
| 660 | value = Iterate(wtm, booking, 1); | ||
| 661 | booking = 0; | ||
| 662 | if (value < -50) { | ||
| 663 | last_pv.pathd = 0; | ||
| 664 | return 0; | ||
| 665 |           } | ||
| 666 | } else { | ||
| 667 | tree->pv[1].path[1] = book_moves[0]; | ||
| 668 | tree->pv[1].pathl = 2; | ||
| 669 | tree->pv[1].pathd = 0; | ||
| 670 |         } | ||
| 671 | return 1; | ||
| 672 |       } | ||
| 673 |     } | ||
| 674 | /* | ||
| 675 |  ************************************************************ | ||
| 676 |  *                                                          * | ||
| 677 |  *  If puzzling, in tournament mode we try to find the best * | ||
| 678 |  *  non-book move, because a book move will produce a quick * | ||
| 679 |  *  move anyway.  We therefore would rather search for a    * | ||
| 680 |  *  non-book move, just in case the opponent goes out of    * | ||
| 681 |  *  book here.                                              * | ||
| 682 |  *                                                          * | ||
| 683 |  ************************************************************ | ||
| 684 |  */ | ||
| 685 | else if (mode == tournament_mode && puzzling) { | ||
| 686 | RootMoveList(wtm); | ||
| 687 | for (i = 0; i < n_root_moves; i++) | ||
| 688 | for (j = 0; j < nmoves; j++) | ||
| 689 | if (root_moves[i].move == book_moves[j]) | ||
| 690 | root_moves[i].move = 0; | ||
| 691 | for (i = 0, j = 0; i < n_root_moves; i++) | ||
| 692 | if (root_moves[i].move != 0) | ||
| 693 | root_moves[j++] = root_moves[i]; | ||
| 694 | n_root_moves = j; | ||
| 695 | Print(128, " moves considered {only non-book moves}\n"); | ||
| 696 | nmoves = j; | ||
| 697 | if (nmoves > 1) { | ||
| 698 | last_pv.pathd = 0; | ||
| 699 | booking = 1; | ||
| 700 | (void) Iterate(wtm, booking, 1); | ||
| 701 | booking = 0; | ||
| 702 | } else { | ||
| 703 | tree->pv[1].path[1] = book_moves[0]; | ||
| 704 | tree->pv[1].pathl = 2; | ||
| 705 | tree->pv[1].pathd = 0; | ||
| 706 |       } | ||
| 707 | return 1; | ||
| 708 |     } | ||
| 709 | last_move = nmoves; | ||
| 710 | /* | ||
| 711 |  ************************************************************ | ||
| 712 |  *                                                          * | ||
| 713 |  *  Compute a random value and use this to generate a book  * | ||
| 714 |  *  move based on a probability distribution of the number  * | ||
| 715 |  *  of games won by each book move.                         * | ||
| 716 |  *                                                          * | ||
| 717 |  ************************************************************ | ||
| 718 |  */ | ||
| 719 | which = Random32(); | ||
| 720 | j = ReadClock() / 100 % 13; | ||
| 721 | for (i = 0; i < j; i++) | ||
| 722 | which = Random32(); | ||
| 723 | total_moves = 0; | ||
| 724 | for (i = 0; i < last_move; i++) { | ||
| 725 | if (bs_percent[0]) | ||
| 726 | total_moves += (int) bs_value[i]; // Pierre-Marie Baty -- added type cast | ||
| 727 |       else | ||
| 728 | total_moves += (int) (bs_value[i] * bs_value[i]); // Pierre-Marie Baty -- added type cast | ||
| 729 |     } | ||
| 730 | distribution = Abs(which) % Max(total_moves, 1); | ||
| 731 | for (which = 0; which < last_move; which++) { | ||
| 732 | if (bs_percent[0]) | ||
| 733 | distribution -= (int) bs_value[which]; // Pierre-Marie Baty -- added type cast | ||
| 734 |       else | ||
| 735 | distribution -= (int) (bs_value[which] * bs_value[which]); // Pierre-Marie Baty -- added type cast | ||
| 736 | if (distribution < 0) | ||
| 737 | break; | ||
| 738 |     } | ||
| 739 | which = Min(which, last_move - 1); | ||
| 740 | tree->pv[1].path[1] = book_moves[which]; | ||
| 741 | percent_played = 100 * bs_played[which] / Max(total_played, 1); | ||
| 742 | total_played = bs_played[which]; | ||
| 743 | m1_status = book_status[which]; | ||
| 744 | tree->pv[1].pathl = 2; | ||
| 745 | tree->pv[1].pathd = 0; | ||
| 746 | if (mode != tournament_mode) { | ||
| 747 | MakeMove(tree, 1, book_moves[which], wtm); | ||
| 748 | if ((book_ponder_move = BookPonderMove(tree, Flip(wtm)))) { | ||
| 749 | tree->pv[1].path[2] = book_ponder_move; | ||
| 750 | tree->pv[1].pathl = 3; | ||
| 751 |       } | ||
| 752 | UnmakeMove(tree, 1, book_moves[which], wtm); | ||
| 753 |     } | ||
| 754 | book_learn_key = bs_key[which]; | ||
| 755 | Print(128, " book 0.0s %3d%% ", percent_played); | ||
| 756 | Print(128, " %s", OutputMove(tree, tree->pv[1].path[1], 1, wtm)); | ||
| 757 | st = m1_status & book_accept_mask & (~224); | ||
| 758 | if (st) { | ||
| 759 | if (st & 1) | ||
| 760 | Print(128, "??"); | ||
| 761 | else if (st & 2) | ||
| 762 | Print(128, "?"); | ||
| 763 | else if (st & 4) | ||
| 764 | Print(128, "="); | ||
| 765 | else if (st & 8) | ||
| 766 | Print(128, "!"); | ||
| 767 | else if (st & 16) | ||
| 768 | Print(128, "!!"); | ||
| 769 |     } | ||
| 770 | MakeMove(tree, 1, tree->pv[1].path[1], wtm); | ||
| 771 | if (tree->pv[1].pathl > 2) | ||
| 772 | Print(128, " %s", OutputMove(tree, tree->pv[1].path[2], 2, Flip(wtm))); | ||
| 773 | UnmakeMove(tree, 1, tree->pv[1].path[1], wtm); | ||
| 774 | Print(128, "\n"); | ||
| 775 | return 1; | ||
| 776 |   } | ||
| 777 | return 0; | ||
| 778 | } | ||
| 779 | |||
| 780 | /* last modified 02/23/14 */ | ||
| 781 | /* | ||
| 782 |  ******************************************************************************* | ||
| 783 |  *                                                                             * | ||
| 784 |  *   BookPonderMove() is used to find a move to ponder, to avoid the overhead  * | ||
| 785 |  *   of a "puzzling" search early in the game (unless there are no book moves  * | ||
| 786 |  *   found, of course.)  The algorithm is much simpler than the normal book    * | ||
| 787 |  *   move code...  just find the move with the largest frequency counter and   * | ||
| 788 |  *   assume that will be played.                                               * | ||
| 789 |  *                                                                             * | ||
| 790 |  ******************************************************************************* | ||
| 791 |  */ | ||
| 792 | int BookPonderMove(TREE * RESTRICT tree, int wtm) { | ||
| 793 | uint64_t temp_hash_key, common; | ||
| 794 | static int book_moves[200]; | ||
| 795 | int i, key, *lastm, cluster, n_moves, im, played, tplayed; | ||
| 796 | int book_ponder_move = 0, test; | ||
| 797 | unsigned char buf32[4]; | ||
| 798 | |||
| 799 | /* | ||
| 800 |  ************************************************************ | ||
| 801 |  *                                                          * | ||
| 802 |  *  position is known, read in the appropriate cluster.     * | ||
| 803 |  *  note that this cluster will have all possible book      * | ||
| 804 |  *  moves from current position in it (as well as others of * | ||
| 805 |  *  course.)                                                * | ||
| 806 |  *                                                          * | ||
| 807 |  ************************************************************ | ||
| 808 |  */ | ||
| 809 | if (book_file) { | ||
| 810 | test = HashKey >> 49; | ||
| 811 | fseek(book_file, test * sizeof(int), SEEK_SET); | ||
| 812 | fread(buf32, 4, 1, book_file); | ||
| 813 | key = BookIn32(buf32); | ||
| 814 | if (key > 0) { | ||
| 815 | fseek(book_file, key, SEEK_SET); | ||
| 816 | fread(buf32, 4, 1, book_file); | ||
| 817 | cluster = BookIn32(buf32); | ||
| 818 | BookClusterIn(book_file, cluster, book_buffer); | ||
| 819 | } else | ||
| 820 | cluster = 0; | ||
| 821 | if (!cluster) | ||
| 822 | return 0; | ||
| 823 | lastm = GenerateCaptures(tree, 2, wtm, book_moves); | ||
| 824 | lastm = GenerateNoncaptures(tree, 2, wtm, lastm); | ||
| 825 | n_moves = lastm - book_moves; | ||
| 826 | /* | ||
| 827 |  ************************************************************ | ||
| 828 |  *                                                          * | ||
| 829 |  *  First cycle through the root move list, make each move, * | ||
| 830 |  *  and see if the resulting hash key is in the book        * | ||
| 831 |  *  database.                                               * | ||
| 832 |  *                                                          * | ||
| 833 |  ************************************************************ | ||
| 834 |  */ | ||
| 835 | played = -1; | ||
| 836 | for (im = 0; im < n_moves; im++) { | ||
| 837 | common = HashKey & ((uint64_t) 65535 << 48); | ||
| 838 | MakeMove(tree, 2, book_moves[im], wtm); | ||
| 839 | temp_hash_key = (wtm) ? HashKey : ~HashKey; | ||
| 840 | temp_hash_key = (temp_hash_key & ~((uint64_t) 65535 << 48)) | common; | ||
| 841 | for (i = 0; i < cluster; i++) { | ||
| 842 | if (!(temp_hash_key ^ book_buffer[i].position)) { | ||
| 843 | tplayed = book_buffer[i].status_played & 077777777; | ||
| 844 | if (tplayed > played) { | ||
| 845 | played = tplayed; | ||
| 846 | book_ponder_move = book_moves[im]; | ||
| 847 |           } | ||
| 848 | break; | ||
| 849 |         } | ||
| 850 |       } | ||
| 851 | UnmakeMove(tree, 2, book_moves[im], wtm); | ||
| 852 |     } | ||
| 853 |   } | ||
| 854 | return book_ponder_move; | ||
| 855 | } | ||
| 856 | |||
| 857 | /* last modified 05/08/14 */ | ||
| 858 | /* | ||
| 859 |  ******************************************************************************* | ||
| 860 |  *                                                                             * | ||
| 861 |  *   BookUp() is used to create/add to the opening book file.  typing "<file>  * | ||
| 862 |  *   create" will erase the old book file and start from scratch,              * | ||
| 863 |  *                                                                             * | ||
| 864 |  *   The format of the input data is a left bracket ("[") followed by any title* | ||
| 865 |  *   information desired, followed by a right bracket ("]") followed by a      * | ||
| 866 |  *   sequence of moves.  The sequence of moves is assumed to start at ply=1,   * | ||
| 867 |  *   with white-to-move (normal opening position) and can contain as many moves* | ||
| 868 |  *   as desired (no limit on the depth of each variation.)  The file *must* be * | ||
| 869 |  *   terminated with a line that begins with "end", since handling the EOF     * | ||
| 870 |  *   condition makes portable code difficult.                                  * | ||
| 871 |  *                                                                             * | ||
| 872 |  *   Book moves can either be typed in by hand, directly into book_add(), by   * | ||
| 873 |  *   using the "book create/add" command.  Using the command "book add/create  * | ||
| 874 |  *   filename" will cause book_add() to read its opening text moves from       * | ||
| 875 |  *   filename rather than from the keyboard                                    * | ||
| 876 |  *                                                                             * | ||
| 877 |  *   In addition to the normal text for a move (reduced or full algebraic is   * | ||
| 878 |  *   accepted, ie, e4, ed, exd4, e3d4, etc. are all acceptable) some special   * | ||
| 879 |  *   characters can be appended to a move.                                     * | ||
| 880 |  *                                                                             * | ||
| 881 |  *      ?? ->  Never play this move.  since the same book is used for both     * | ||
| 882 |  *             black and white, you can enter moves in that white might play,  * | ||
| 883 |  *             but prevent the program from choosing them on its own.          * | ||
| 884 |  *      ?  ->  Avoid this move except for non-important games.  These openings * | ||
| 885 |  *             are historically those that the program doesn't play very well, * | ||
| 886 |  *             but which aren't outright losing.                               * | ||
| 887 |  *      =  ->  Drawish move, only play this move if drawish moves are allowed  * | ||
| 888 |  *             by the operator.  This is used to encourage the program to play * | ||
| 889 |  *             drawish openings (Petrov's comes to mind) when the program needs* | ||
| 890 |  *             to draw or is facing a formidable opponent (deep thought comes  * | ||
| 891 |  *             to mind.)                                                       * | ||
| 892 |  *      !  ->  Always play this move, if there isn't a move with the !! flag   * | ||
| 893 |  *             set also.  This is a strong move, but not as strong as a !!     * | ||
| 894 |  *             move.                                                           * | ||
| 895 |  *      !! ->  Always play this move.  This can be used to make the program    * | ||
| 896 |  *             favor particular lines, or to mark a strong move for certain    * | ||
| 897 |  *             opening traps.                                                  * | ||
| 898 |  *                                                                             * | ||
| 899 |  *  {Play nn%} is used to force this specific book move to be played a specific* | ||
| 900 |  *             percentage of the time, and override the frequency of play that * | ||
| 901 |  *             comes from the large pgn database.                              * | ||
| 902 |  *                                                                             * | ||
| 903 |  ******************************************************************************* | ||
| 904 |  */ | ||
| 905 | void BookUp(TREE * RESTRICT tree, int nargs, char **args) { | ||
| 906 | BB_POSITION *bbuffer; | ||
| 907 | uint64_t temp_hash_key, common; | ||
| 908 | FILE *book_input; | ||
| 909 | char fname[128], start, *ch, output_filename[128]; | ||
| 910 | static char schar[2] = { "." }; | ||
| 911 | int result = 0, played, i, mask_word, total_moves; | ||
| 912 | int move, move_num, wtm, book_positions, major, minor; | ||
| 913 | int cluster, max_cluster, discarded = 0, discarded_mp = 0, discarded_lose = | ||
| 914 | 0; | ||
| 915 | int errors, data_read; | ||
| 916 | int start_elapsed_time, ply, max_ply = 256; | ||
| 917 | int stat, files = 0, buffered = 0, min_played = 0, games_parsed = 0; | ||
| 918 | int wins, losses; | ||
| 919 | BOOK_POSITION current, next; | ||
| 920 |   BB_POSITION temp; | ||
| 921 | int last, cluster_seek, next_cluster; | ||
| 922 | int counter, *index, max_search_depth; | ||
| 923 | double wl_percent = 0.0; | ||
| 924 | |||
| 925 | /* | ||
| 926 |  ************************************************************ | ||
| 927 |  *                                                          * | ||
| 928 |  *  Open the correct book file for writing/reading          * | ||
| 929 |  *                                                          * | ||
| 930 |  ************************************************************ | ||
| 931 |  */ | ||
| 932 | #if defined(POSITIONS) | ||
| 933 | unsigned int output_pos, output_wtm; | ||
| 934 | FILE *pout = fopen("positions", "w"); | ||
| 935 | #endif | ||
| 936 | if (!strcmp(args[1], "create")) { | ||
| 937 | if (nargs < 4) { | ||
| 938 | Print(4095, "usage: <binfile> create <pgn-filename> "); | ||
| 939 | Print(4095, "maxply [minplay] [win/lose %%]\n"); | ||
| 940 | return; | ||
| 941 |     } | ||
| 942 | max_ply = atoi(args[3]); | ||
| 943 | if (nargs >= 5) { | ||
| 944 | min_played = atoi(args[4]); | ||
| 945 |     } | ||
| 946 | if (nargs > 5) { | ||
| 947 | wl_percent = atof(args[5]) / 100.0; | ||
| 948 |     } | ||
| 949 | strcpy(output_filename, args[0]); | ||
| 950 | if (!strstr(output_filename, ".bin")) { | ||
| 951 | strcat(output_filename, ".bin"); | ||
| 952 |     } | ||
| 953 | } else if (!strcmp(args[1], "off")) { | ||
| 954 | if (book_file) | ||
| 955 | fclose(book_file); | ||
| 956 | if (books_file) | ||
| 957 | fclose(normal_bs_file); | ||
| 958 | if (computer_bs_file) | ||
| 959 | fclose(computer_bs_file); | ||
| 960 | book_file = 0; | ||
| 961 | books_file = 0; | ||
| 962 | computer_bs_file = 0; | ||
| 963 | normal_bs_file = 0; | ||
| 964 | Print(4095, "book file disabled.\n"); | ||
| 965 | return; | ||
| 966 | } else if (!strcmp(args[1], "on")) { | ||
| 967 | if (!book_file) { | ||
| 968 | sprintf(fname, "%s/book.bin", book_path); | ||
| 969 | book_file = fopen(fname, "rb+"); | ||
| 970 | sprintf(fname, "%s/books.bin", book_path); | ||
| 971 | books_file = fopen(fname, "rb+"); | ||
| 972 | Print(4095, "book file enabled.\n"); | ||
| 973 |     } | ||
| 974 | return; | ||
| 975 | } else if (!strcmp(args[1], "mask")) { | ||
| 976 | if (nargs < 4) { | ||
| 977 | Print(4095, "usage: book mask accept|reject value\n"); | ||
| 978 | return; | ||
| 979 | } else if (!strcmp(args[2], "accept")) { | ||
| 980 | book_accept_mask = BookMask(args[3]); | ||
| 981 | book_reject_mask = book_reject_mask & ~book_accept_mask; | ||
| 982 | return; | ||
| 983 | } else if (!strcmp(args[2], "reject")) { | ||
| 984 | book_reject_mask = BookMask(args[3]); | ||
| 985 | book_accept_mask = book_accept_mask & ~book_reject_mask; | ||
| 986 | return; | ||
| 987 |     } | ||
| 988 | } else if (!strcmp(args[1], "random")) { | ||
| 989 | if (nargs < 3) { | ||
| 990 | Print(4095, "usage: book random <n>\n"); | ||
| 991 | return; | ||
| 992 |     } | ||
| 993 | book_random = atoi(args[2]); | ||
| 994 | switch (book_random) { | ||
| 995 | case 0: | ||
| 996 | Print(4095, "play best book line after search.\n"); | ||
| 997 | Print(4095, " ..book selection width set to 99.\n"); | ||
| 998 | book_selection_width = 99; | ||
| 999 | break; | ||
| 1000 | case 1: | ||
| 1001 | Print(4095, "choose from book moves randomly (using weights.)\n"); | ||
| 1002 | break; | ||
| 1003 | default: | ||
| 1004 | Print(4095, "valid options are 0-1.\n"); | ||
| 1005 | break; | ||
| 1006 |     } | ||
| 1007 | return; | ||
| 1008 | } else if (!strcmp(args[1], "trigger")) { | ||
| 1009 | if (nargs < 3) { | ||
| 1010 | Print(4095, "usage: book trigger <n>\n"); | ||
| 1011 | return; | ||
| 1012 |     } | ||
| 1013 | book_search_trigger = atoi(args[2]); | ||
| 1014 | Print(4095, "search book moves if the most popular was not played\n"); | ||
| 1015 | Print(4095, "at least %d times.\n", book_search_trigger); | ||
| 1016 | return; | ||
| 1017 | } else if (!strcmp(args[1], "width")) { | ||
| 1018 | if (nargs < 3) { | ||
| 1019 | Print(4095, "usage: book width <n>\n"); | ||
| 1020 | return; | ||
| 1021 |     } | ||
| 1022 | book_selection_width = atoi(args[2]); | ||
| 1023 | book_random = 1; | ||
| 1024 | Print(4095, "choose from %d best moves.\n", book_selection_width); | ||
| 1025 | Print(4095, " ..book random set to 1.\n"); | ||
| 1026 | return; | ||
| 1027 | } else { | ||
| 1028 | Print(4095, "usage: book [option] [filename] [maxply] [minplay]\n"); | ||
| 1029 | return; | ||
| 1030 |   } | ||
| 1031 | if (!(book_input = fopen(args[2], "r"))) { | ||
| 1032 | printf("file %s does not exist.\n", args[2]); | ||
| 1033 | return; | ||
| 1034 |   } | ||
| 1035 | ReadPGN(0, 0); | ||
| 1036 | if (book_file) | ||
| 1037 | fclose(book_file); | ||
| 1038 | book_file = fopen(output_filename, "wb+"); | ||
| 1039 | bbuffer = (BB_POSITION *) malloc(sizeof(BB_POSITION) * SORT_BLOCK); | ||
| 1040 | if (!bbuffer) { | ||
| 1041 | Print(4095, "Unable to malloc() sort buffer, aborting\n"); | ||
| 1042 | CraftyExit(1); | ||
| 1043 |   } | ||
| 1044 | fseek(book_file, 0, SEEK_SET); | ||
| 1045 | /* | ||
| 1046 |  ************************************************************ | ||
| 1047 |  *                                                          * | ||
| 1048 |  *  Now, read in a series of moves (terminated by the "["   * | ||
| 1049 |  *  of the next title or by "end" for end of the file) and  * | ||
| 1050 |  *  make them.  After each MakeMove(), we can grab the hash * | ||
| 1051 |  *  key, and use it to access the book data file to add     * | ||
| 1052 |  *  this position.  Note that we have to check the last     * | ||
| 1053 |  *  character of a move for the special flags and set the   * | ||
| 1054 |  *  correct bit in the status for this position.  When we   * | ||
| 1055 |  *  reach the end of a book line, we back up to the         * | ||
| 1056 |  *  starting position and start over.                       * | ||
| 1057 |  *                                                          * | ||
| 1058 |  ************************************************************ | ||
| 1059 |  */ | ||
| 1060 | major = atoi(version); | ||
| 1061 | minor = atoi(strchr(version, '.') + 1); | ||
| 1062 | major = (major << 16) + minor; | ||
| 1063 | start = !strstr(output_filename, "book.bin"); | ||
| 1064 | printf("parsing pgn move file (100k moves/dot)\n"); | ||
| 1065 | start_elapsed_time = ReadClock(); | ||
| 1066 | if (book_file) { | ||
| 1067 | total_moves = 0; | ||
| 1068 | max_search_depth = 0; | ||
| 1069 | errors = 0; | ||
| 1070 | do { | ||
| 1071 | data_read = ReadPGN(book_input, 0); | ||
| 1072 | if (data_read == -1) | ||
| 1073 | Print(4095, "end-of-file reached\n"); | ||
| 1074 | } while (data_read == 0); | ||
| 1075 | do { | ||
| 1076 | if (data_read < 0) { | ||
| 1077 | Print(4095, "end-of-file reached\n"); | ||
| 1078 | break; | ||
| 1079 |       } | ||
| 1080 | if (data_read == 1) { | ||
| 1081 | if (strstr(buffer, "Site")) { | ||
| 1082 |           games_parsed++; | ||
| 1083 | result = 3; | ||
| 1084 | } else if (strstr(buffer, "esult")) { | ||
| 1085 | if (strstr(buffer, "1-0")) | ||
| 1086 | result = 2; | ||
| 1087 | else if (strstr(buffer, "0-1")) | ||
| 1088 | result = 1; | ||
| 1089 | else if (strstr(buffer, "1/2-1/2")) | ||
| 1090 | result = 0; | ||
| 1091 | else if (strstr(buffer, "*")) | ||
| 1092 | result = 3; | ||
| 1093 |         } | ||
| 1094 | data_read = ReadPGN(book_input, 0); | ||
| 1095 | } else | ||
| 1096 | do { | ||
| 1097 | wtm = 1; | ||
| 1098 | InitializeChessBoard(tree); | ||
| 1099 | tree->status[1] = tree->status[0]; | ||
| 1100 | move_num = 1; | ||
| 1101 | tree->status[2] = tree->status[1]; | ||
| 1102 | ply = 0; | ||
| 1103 | data_read = 0; | ||
| 1104 | #if defined(POSITIONS) | ||
| 1105 | output_pos = Random32(); | ||
| 1106 | output_pos = (output_pos | (output_pos >> 16)) & 65535; | ||
| 1107 | output_pos = output_pos % 20 + 8; | ||
| 1108 | output_wtm = Random32() & 1; | ||
| 1109 | #endif | ||
| 1110 | while (data_read == 0) { | ||
| 1111 | mask_word = 0; | ||
| 1112 | if ((ch = strpbrk(buffer, "?!"))) { | ||
| 1113 | mask_word = BookMask(ch); | ||
| 1114 | *ch = 0; | ||
| 1115 |             } | ||
| 1116 | if (!strchr(buffer, '$') && !strchr(buffer, '*')) { | ||
| 1117 | if (ply < max_ply) | ||
| 1118 | move = ReadNextMove(tree, buffer, 2, wtm); | ||
| 1119 | else { | ||
| 1120 | move = 0; | ||
| 1121 |                 discarded++; | ||
| 1122 |               } | ||
| 1123 | if (move) { | ||
| 1124 |                 ply++; | ||
| 1125 | max_search_depth = Max(max_search_depth, ply); | ||
| 1126 |                 total_moves++; | ||
| 1127 | common = HashKey & ((uint64_t) 65535 << 48); | ||
| 1128 | MakeMove(tree, 2, move, wtm); | ||
| 1129 | tree->status[2] = tree->status[3]; | ||
| 1130 | if (ply <= max_ply) { | ||
| 1131 | temp_hash_key = (wtm) ? HashKey : ~HashKey; | ||
| 1132 |                   temp_hash_key = | ||
| 1133 | (temp_hash_key & ~((uint64_t) 65535 << 48)) | common; | ||
| 1134 | memcpy(bbuffer[buffered].position, (char *) &temp_hash_key, | ||
| 1135 | 8); | ||
| 1136 | if (result & 1) | ||
| 1137 | mask_word |= 0x20; | ||
| 1138 | if (result == 0) | ||
| 1139 | mask_word |= 0x40; | ||
| 1140 | if (result & 2) | ||
| 1141 | mask_word |= 0x80; | ||
| 1142 | bbuffer[buffered].status = mask_word; | ||
| 1143 | bbuffer[buffered++].percent_play = | ||
| 1144 | pgn_suggested_percent + (wtm << 7); | ||
| 1145 | if (buffered >= SORT_BLOCK) { | ||
| 1146 | BookSort(bbuffer, buffered, ++files); | ||
| 1147 | buffered = 0; | ||
| 1148 | strcpy(schar, "S"); | ||
| 1149 |                   } | ||
| 1150 |                 } | ||
| 1151 | if (!(total_moves % 100000)) { | ||
| 1152 | printf("%s", schar); | ||
| 1153 | strcpy(schar, "."); | ||
| 1154 | if (!(total_moves % 6000000)) | ||
| 1155 | printf(" (%dk)\n", total_moves / 1000); | ||
| 1156 | fflush(stdout); | ||
| 1157 |                 } | ||
| 1158 | wtm = Flip(wtm); | ||
| 1159 | if (wtm) | ||
| 1160 |                   move_num++; | ||
| 1161 | #if defined(POSITIONS) | ||
| 1162 | if (wtm == output_wtm && move_num == output_pos) { | ||
| 1163 |                   SEARCH_POSITION temp_pos; | ||
| 1164 | int twtm; | ||
| 1165 | char t_initial_position[256]; | ||
| 1166 | |||
| 1167 | strcpy(t_initial_position, initial_position); | ||
| 1168 | temp_pos = tree->status[0]; | ||
| 1169 | tree->status[0] = tree->status[3]; | ||
| 1170 | if (Castle(0, white) < 0) | ||
| 1171 | Castle(0, white) = 0; | ||
| 1172 | if (Castle(0, black) < 0) | ||
| 1173 | Castle(0, black) = 0; | ||
| 1174 | strcpy(buffer, "savepos *"); | ||
| 1175 | twtm = game_wtm; | ||
| 1176 | game_wtm = wtm; | ||
| 1177 | (void) Option(tree); | ||
| 1178 | game_wtm = twtm; | ||
| 1179 | fprintf(pout, "%s\n", initial_position); | ||
| 1180 | strcpy(initial_position, t_initial_position); | ||
| 1181 | tree->status[0] = temp_pos; | ||
| 1182 |                 } | ||
| 1183 | #endif | ||
| 1184 | } else if (strspn(buffer, "0123456789/-.*") != strlen(buffer) | ||
| 1185 | && ply < max_ply) { | ||
| 1186 |                 errors++; | ||
| 1187 | Print(4095, "ERROR! move %d: %s is illegal (line %d)\n", | ||
| 1188 | move_num, buffer, ReadPGN(book_input, -2)); | ||
| 1189 | ReadPGN(book_input, -1); | ||
| 1190 | DisplayChessBoard(stdout, tree->position); | ||
| 1191 | do { | ||
| 1192 | data_read = ReadPGN(book_input, 0); | ||
| 1193 | if (data_read == -1) | ||
| 1194 | Print(4095, "end-of-file reached\n"); | ||
| 1195 | } while (data_read == 0); | ||
| 1196 | break; | ||
| 1197 |               } | ||
| 1198 |             } | ||
| 1199 | data_read = ReadPGN(book_input, 0); | ||
| 1200 |           } | ||
| 1201 | } while (0); | ||
| 1202 | } while (strcmp(buffer, "end") && data_read != -1); | ||
| 1203 | if (book_input != stdin) | ||
| 1204 | fclose(book_input); | ||
| 1205 | if (buffered) | ||
| 1206 | BookSort(bbuffer, buffered, ++files); | ||
| 1207 | free(bbuffer); | ||
| 1208 | printf("S <done>\n"); | ||
| 1209 | if (total_moves == 0) { | ||
| 1210 | Print(4095, "ERROR - empty input PGN file\n"); | ||
| 1211 | return; | ||
| 1212 |     } | ||
| 1213 | /* | ||
| 1214 |  ************************************************************ | ||
| 1215 |  *                                                          * | ||
| 1216 |  *  Now merge these "chunks" into book.bin, keeping all of  * | ||
| 1217 |  *  the "flags" as well as counting the number of times     * | ||
| 1218 |  *  that each move was played.                              * | ||
| 1219 |  *                                                          * | ||
| 1220 |  ************************************************************ | ||
| 1221 |  */ | ||
| 1222 | printf("merging sorted files (%d) (100k/dot)\n", files); | ||
| 1223 | counter = 0; | ||
| 1224 | index = (int *) malloc(32768 * sizeof(int)); | ||
| 1225 | if (!index) { | ||
| 1226 | Print(4095, "Unable to malloc() index block, aborting\n"); | ||
| 1227 | CraftyExit(1); | ||
| 1228 |     } | ||
| 1229 | for (i = 0; i < 32768; i++) | ||
| 1230 | index[i] = -1; | ||
| 1231 | temp = BookUpNextPosition(files, 1); | ||
| 1232 | memcpy((char *) ¤t.position, temp.position, 8); | ||
| 1233 | current.status_played = temp.status << 24; | ||
| 1234 | if (start) | ||
| 1235 | current.status_played += temp.percent_play & 127; | ||
| 1236 | current.learn = 0.0; | ||
| 1237 | played = 1; | ||
| 1238 | fclose(book_file); | ||
| 1239 | book_file = fopen(output_filename, "wb+"); | ||
| 1240 | fseek(book_file, sizeof(int) * 32768, SEEK_SET); | ||
| 1241 | last = current.position >> 49; | ||
| 1242 | index[last] = ftell(book_file); | ||
| 1243 | book_positions = 0; | ||
| 1244 | cluster = 0; | ||
| 1245 | cluster_seek = sizeof(int) * 32768; | ||
| 1246 | fseek(book_file, cluster_seek + sizeof(int), SEEK_SET); | ||
| 1247 | max_cluster = 0; | ||
| 1248 | wins = 0; | ||
| 1249 | losses = 0; | ||
| 1250 | if (temp.status & 128 && temp.percent_play & 128) | ||
| 1251 |       wins++; | ||
| 1252 | if (temp.status & 128 && !(temp.percent_play & 128)) | ||
| 1253 |       losses++; | ||
| 1254 | if (temp.status & 32 && !(temp.percent_play & 128)) | ||
| 1255 |       wins++; | ||
| 1256 | if (temp.status & 32 && temp.percent_play & 128) | ||
| 1257 |       losses++; | ||
| 1258 | while (1) { | ||
| 1259 | temp = BookUpNextPosition(files, 0); | ||
| 1260 | memcpy((char *) &next.position, temp.position, 8); | ||
| 1261 | next.status_played = temp.status << 24; | ||
| 1262 | if (start) | ||
| 1263 | next.status_played += temp.percent_play & 127; | ||
| 1264 | next.learn = 0.0; | ||
| 1265 |       counter++; | ||
| 1266 | if (counter % 100000 == 0) { | ||
| 1267 | printf("."); | ||
| 1268 | if (counter % 6000000 == 0) | ||
| 1269 | printf(" (%dk)\n", counter / 1000); | ||
| 1270 | fflush(stdout); | ||
| 1271 |       } | ||
| 1272 | if (current.position == next.position) { | ||
| 1273 | current.status_played = current.status_played | next.status_played; | ||
| 1274 |         played++; | ||
| 1275 | if (temp.status & 128 && temp.percent_play & 128) | ||
| 1276 |           wins++; | ||
| 1277 | if (temp.status & 128 && !(temp.percent_play & 128)) | ||
| 1278 |           losses++; | ||
| 1279 | if (temp.status & 32 && !(temp.percent_play & 128)) | ||
| 1280 |           wins++; | ||
| 1281 | if (temp.status & 32 && temp.percent_play & 128) | ||
| 1282 |           losses++; | ||
| 1283 | } else { | ||
| 1284 | if (played >= min_played && wins >= (losses * wl_percent)) { | ||
| 1285 |           book_positions++; | ||
| 1286 |           cluster++; | ||
| 1287 | max_cluster = Max(max_cluster, cluster); | ||
| 1288 | if (!start) | ||
| 1289 | current.status_played += played; | ||
| 1290 | current.learn = 0.0; | ||
| 1291 | memcpy((void *) &book_buffer_char[0].position, | ||
| 1292 | (void *) BookOut64(current.position), 8); | ||
| 1293 | memcpy((void *) &book_buffer_char[0].status_played, | ||
| 1294 | (void *) BookOut32(current.status_played), 4); | ||
| 1295 | memcpy((void *) &book_buffer_char[0].learn, | ||
| 1296 | (void *) BookOut32((int) current.learn), 4); // Pierre-Marie Baty -- added type cast | ||
| 1297 | stat = fwrite(book_buffer_char, BOOK_POSITION_SIZE, 1, book_file); | ||
| 1298 | if (stat != 1) | ||
| 1299 | Print(4095, "ERROR! write failed, disk probably full.\n"); | ||
| 1300 | } else if (played < min_played) | ||
| 1301 |           discarded_mp++; | ||
| 1302 |         else | ||
| 1303 |           discarded_lose++; | ||
| 1304 | if (last != (int) (next.position >> 49)) { | ||
| 1305 | next_cluster = ftell(book_file); | ||
| 1306 | fseek(book_file, cluster_seek, SEEK_SET); | ||
| 1307 | memcpy((void *) &cluster, BookOut32(cluster), 4); | ||
| 1308 | stat = fwrite(&cluster, sizeof(int), 1, book_file); | ||
| 1309 | if (stat != 1) | ||
| 1310 | Print(4095, "ERROR! write failed, disk probably full.\n"); | ||
| 1311 | if (next.position == 0) | ||
| 1312 | break; | ||
| 1313 | fseek(book_file, next_cluster + sizeof(int), SEEK_SET); | ||
| 1314 | cluster_seek = next_cluster; | ||
| 1315 | last = next.position >> 49; | ||
| 1316 | index[last] = next_cluster; | ||
| 1317 | cluster = 0; | ||
| 1318 |         } | ||
| 1319 | wins = 0; | ||
| 1320 | losses = 0; | ||
| 1321 | if (temp.status & 128 && temp.percent_play & 128) | ||
| 1322 |           wins++; | ||
| 1323 | if (temp.status & 128 && !(temp.percent_play & 128)) | ||
| 1324 |           losses++; | ||
| 1325 | if (temp.status & 32 && !(temp.percent_play & 128)) | ||
| 1326 |           wins++; | ||
| 1327 | if (temp.status & 32 && temp.percent_play & 128) | ||
| 1328 |           losses++; | ||
| 1329 | current = next; | ||
| 1330 | played = 1; | ||
| 1331 | if (next.position == 0) | ||
| 1332 | break; | ||
| 1333 |       } | ||
| 1334 |     } | ||
| 1335 | fseek(book_file, 0, SEEK_SET); | ||
| 1336 | for (i = 0; i < 32768; i++) { | ||
| 1337 | memcpy((void *) &cluster, (void *) BookOut32(index[i]), 4); | ||
| 1338 | fwrite(&cluster, 4, 1, book_file); | ||
| 1339 |     } | ||
| 1340 | fseek(book_file, 0, SEEK_END); | ||
| 1341 | memcpy((void *) &cluster, (void *) BookOut32(major), 4); | ||
| 1342 | fwrite(&cluster, 4, 1, book_file); | ||
| 1343 | /* | ||
| 1344 |  ************************************************************ | ||
| 1345 |  *                                                          * | ||
| 1346 |  *  Now clean up, remove the sort.n files, and print the    * | ||
| 1347 |  *  statistics for building the book.                       * | ||
| 1348 |  *                                                          * | ||
| 1349 |  ************************************************************ | ||
| 1350 |  */ | ||
| 1351 | for (i = 1; i <= files; i++) { | ||
| 1352 | sprintf(fname, "sort.%d", i); | ||
| 1353 | remove(fname); | ||
| 1354 |     } | ||
| 1355 | free(index); | ||
| 1356 | start_elapsed_time = ReadClock() - start_elapsed_time; | ||
| 1357 | Print(4095, "\n\nparsed %d moves (%d games).\n", total_moves, | ||
| 1358 | games_parsed); | ||
| 1359 | Print(4095, "found %d errors during parsing.\n", errors); | ||
| 1360 | Print(4095, "discarded %d moves (maxply=%d).\n", discarded, max_ply); | ||
| 1361 | Print(4095, "discarded %d moves (minplayed=%d).\n", discarded_mp, | ||
| 1362 | min_played); | ||
| 1363 | Print(4095, "discarded %d moves (win/lose=%.1f%%).\n", discarded_lose, | ||
| 1364 | wl_percent * 100); | ||
| 1365 | Print(4095, "book contains %d unique positions.\n", book_positions); | ||
| 1366 | Print(4095, "deepest book line was %d plies.\n", max_search_depth); | ||
| 1367 | Print(4095, "longest cluster of moves was %d.\n", max_cluster); | ||
| 1368 | Print(4095, "time used: %s elapsed.\n", DisplayTime(start_elapsed_time)); | ||
| 1369 |   } | ||
| 1370 | strcpy(initial_position, ""); | ||
| 1371 | InitializeChessBoard(tree); | ||
| 1372 | } | ||
| 1373 | |||
| 1374 | /* last modified 02/23/14 */ | ||
| 1375 | /* | ||
| 1376 |  ******************************************************************************* | ||
| 1377 |  *                                                                             * | ||
| 1378 |  *   BookMask() is used to convert the flags for a book move into an 8 bit     * | ||
| 1379 |  *   mask that is either kept in the file, or is set by the operator to select * | ||
| 1380 |  *   which opening(s) the program is allowed to play.                          * | ||
| 1381 |  *                                                                             * | ||
| 1382 |  ******************************************************************************* | ||
| 1383 |  */ | ||
| 1384 | int BookMask(char *flags) { | ||
| 1385 | int i, mask; | ||
| 1386 | |||
| 1387 | mask = 0; | ||
| 1388 | for (i = 0; i < (int) strlen(flags); i++) { | ||
| 1389 | if (flags[i] == '?') { | ||
| 1390 | if (flags[i + 1] == '?') { | ||
| 1391 | mask = mask | 1; | ||
| 1392 |         i++; | ||
| 1393 | } else | ||
| 1394 | mask = mask | 2; | ||
| 1395 | } else if (flags[i] == '=') { | ||
| 1396 | mask = mask | 4; | ||
| 1397 | } else if (flags[i] == '!') { | ||
| 1398 | if (flags[i + 1] == '!') { | ||
| 1399 | mask = mask | 16; | ||
| 1400 |         i++; | ||
| 1401 | } else | ||
| 1402 | mask = mask | 8; | ||
| 1403 |     } | ||
| 1404 |   } | ||
| 1405 | return mask; | ||
| 1406 | } | ||
| 1407 | |||
| 1408 | /* last modified 02/23/14 */ | ||
| 1409 | /* | ||
| 1410 |  ******************************************************************************* | ||
| 1411 |  *                                                                             * | ||
| 1412 |  *   BookSort() is called to sort a block of moves after they have been parsed * | ||
| 1413 |  *   and converted to hash keys.                                               * | ||
| 1414 |  *                                                                             * | ||
| 1415 |  ******************************************************************************* | ||
| 1416 |  */ | ||
| 1417 | void BookSort(BB_POSITION * buffer, int number, int fileno) { | ||
| 1418 | char fname[16]; | ||
| 1419 | FILE *output_file; | ||
| 1420 | int stat; | ||
| 1421 | |||
| 1422 | qsort((char *) buffer, number, sizeof(BB_POSITION), BookUpCompare); | ||
| 1423 | sprintf(fname, "sort.%d", fileno); | ||
| 1424 | if (!(output_file = fopen(fname, "wb+"))) | ||
| 1425 | printf("ERROR. unable to open sort output file\n"); | ||
| 1426 | stat = fwrite(buffer, sizeof(BB_POSITION), number, output_file); | ||
| 1427 | if (stat != number) | ||
| 1428 | Print(4095, "ERROR! write failed, disk probably full.\n"); | ||
| 1429 | fclose(output_file); | ||
| 1430 | } | ||
| 1431 | |||
| 1432 | /* last modified 02/23/14 */ | ||
| 1433 | /* | ||
| 1434 |  ******************************************************************************* | ||
| 1435 |  *                                                                             * | ||
| 1436 |  *   BookUpNextPosition() is the heart of the "merge" operation that is done   * | ||
| 1437 |  *   after the chunks of the parsed/hashed move file are sorted.  This code    * | ||
| 1438 |  *   opens the sort.n files, and returns the least (lexically) position key to * | ||
| 1439 |  *   counted/merged into the main book database.                               * | ||
| 1440 |  *                                                                             * | ||
| 1441 |  ******************************************************************************* | ||
| 1442 |  */ | ||
| 1443 | BB_POSITION BookUpNextPosition(int files, int init) { | ||
| 1444 | char fname[20]; | ||
| 1445 | static FILE *input_file[100]; | ||
| 1446 | static BB_POSITION *buffer[100]; | ||
| 1447 | static int data_read[100], next[100]; | ||
| 1448 | int i, used; | ||
| 1449 |   BB_POSITION least; | ||
| 1450 | |||
| 1451 | if (init) { | ||
| 1452 | for (i = 1; i <= files; i++) { | ||
| 1453 | sprintf(fname, "sort.%d", i); | ||
| 1454 | if (!(input_file[i] = fopen(fname, "rb"))) { | ||
| 1455 | printf("unable to open sort.%d file, may be too many files open.\n", | ||
| 1456 | i); | ||
| 1457 | CraftyExit(1); | ||
| 1458 |       } | ||
| 1459 | buffer[i] = (BB_POSITION *) malloc(sizeof(BB_POSITION) * MERGE_BLOCK); | ||
| 1460 | if (!buffer[i]) { | ||
| 1461 | printf("out of memory. aborting. \n"); | ||
| 1462 | CraftyExit(1); | ||
| 1463 |       } | ||
| 1464 | fseek(input_file[i], 0, SEEK_SET); | ||
| 1465 | data_read[i] = | ||
| 1466 | fread(buffer[i], sizeof(BB_POSITION), MERGE_BLOCK, input_file[i]); | ||
| 1467 | next[i] = 0; | ||
| 1468 |     } | ||
| 1469 |   } | ||
| 1470 | for (i = 0; i < 8; i++) | ||
| 1471 | least.position[i] = 0; | ||
| 1472 | least.status = 0; | ||
| 1473 | least.percent_play = 0; | ||
| 1474 | used = -1; | ||
| 1475 | for (i = 1; i <= files; i++) | ||
| 1476 | if (data_read[i]) { | ||
| 1477 | least = buffer[i][next[i]]; | ||
| 1478 | used = i; | ||
| 1479 | break; | ||
| 1480 |     } | ||
| 1481 | if (i > files) { | ||
| 1482 | for (i = 1; i <= files; i++) | ||
| 1483 | fclose(input_file[i]); | ||
| 1484 | return least; | ||
| 1485 |   } | ||
| 1486 | for (i++; i <= files; i++) { | ||
| 1487 | if (data_read[i]) { | ||
| 1488 | uint64_t p1, p2; | ||
| 1489 | |||
| 1490 | memcpy((char *) &p1, least.position, 8); | ||
| 1491 | memcpy((char *) &p2, buffer[i][next[i]].position, 8); | ||
| 1492 | if (p1 > p2) { | ||
| 1493 | least = buffer[i][next[i]]; | ||
| 1494 | used = i; | ||
| 1495 |       } | ||
| 1496 |     } | ||
| 1497 |   } | ||
| 1498 | if (--data_read[used] == 0) { | ||
| 1499 | data_read[used] = | ||
| 1500 | fread(buffer[used], sizeof(BB_POSITION), MERGE_BLOCK, | ||
| 1501 | input_file[used]); | ||
| 1502 | next[used] = 0; | ||
| 1503 | } else | ||
| 1504 | next[used]++; | ||
| 1505 | return least; | ||
| 1506 | } | ||
| 1507 | |||
| 1508 | int BookUpCompare(const void *pos1, const void *pos2) { | ||
| 1509 | static uint64_t p1, p2; | ||
| 1510 | |||
| 1511 | memcpy((char *) &p1, ((BB_POSITION *) pos1)->position, 8); | ||
| 1512 | memcpy((char *) &p2, ((BB_POSITION *) pos2)->position, 8); | ||
| 1513 | if (p1 < p2) | ||
| 1514 | return -1; | ||
| 1515 | if (p1 > p2) | ||
| 1516 | return +1; | ||
| 1517 | return 0; | ||
| 1518 | } |