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