My Project
root.c
Go to the documentation of this file.
1
11#include "search.h"
12
13#include "bit.h"
14#include "options.h"
15#include "stats.h"
16#include "util.h"
17#include "ybwc.h"
18#include "settings.h"
19
20#include <stdlib.h>
21#include <assert.h>
22
23extern Log search_log[1];
24extern Log engine_log[1];
25
33void pv_debug(Search *search, const Move *bestmove, FILE *f)
34{
35 Board board[1];
36 Move move[1];
37 int x;
38 unsigned long long hash_code;
39 HashData hash_data[1];
40 char s[4];
41 int player = BLACK;
42
43 spin_lock(search->result);
44
45 *board = *search->board;
46 if (search->height == 1) { // hack to call this function from height 1 or 0
47 board_restore(board, bestmove);
48 }
49
50 x = bestmove->x;
51 fprintf(f, "pv = %s ", move_to_string(x, player, s));
52 hash_code = board_get_hash_code(board);
53 if (hash_get(search->pv_table, board, hash_code, hash_data)) {
54 fprintf(f, ":%02d@%d%%[%+03d,%+03d]; ", hash_data->depth, selectivity_table[hash_data->selectivity].percent, hash_data->lower, hash_data->upper);
55 }
56 while (x != NOMOVE) {
57 board_get_move(board, x, move);
58 board_update(board, move);
59 player ^= 1;
60
61 hash_code = board_get_hash_code(board);
62 if (hash_get(search->pv_table, board, hash_code, hash_data)) {
63 x = hash_data->move[0];
64 fprintf(f, "%s:%02d@%d%%[%+03d,%+03d]; ", move_to_string(x, player, s), hash_data->depth, selectivity_table[hash_data->selectivity].percent, hash_data->lower, hash_data->upper);
65 } else if (hash_get(search->hash_table, board, hash_code, hash_data)) {
66 x = hash_data->move[0];
67 fprintf(f, "{%s}:%2d@%d%%[%+03d,%+03d]; ", move_to_string(x, player, s), hash_data->depth, selectivity_table[hash_data->selectivity].percent, hash_data->lower, hash_data->upper);
68 } else x = NOMOVE;
69 }
70 fputc('\n', f);
71
72 spin_unlock(search->result);
73}
74
83bool is_pv_ok(Search *search, int bestmove, int search_depth)
84{
85 Board board[1];
86 Move move[1];
87 int x;
88 unsigned long long hash_code;
89 HashData hash_data[1];
90
91 *board = *search->board;
92
93 x = bestmove;
94 while (search_depth > 0 && x != NOMOVE) {
95 if (x != PASS) --search_depth;
96 board_get_move(board, x, move);
97 board_update(board, move);
98
99 hash_code = board_get_hash_code(board);
100 if (hash_get(search->pv_table, board, hash_code, hash_data)) {
101 x = hash_data->move[0];
102 } else if (hash_get(search->hash_table, board, hash_code, hash_data)) {
103 x = hash_data->move[0];
104 } else break;
105 if (hash_data->depth < search_depth || hash_data->selectivity < search->selectivity || hash_data->lower != hash_data->upper) return false;
106 if (x == NOMOVE && !board_is_game_over(board)) return false;
107 }
108 return true;
109}
110
111
123static int guess_move(Search *search, Board *board)
124{
125 HashData hash_data[1];
126 Board saved = *search->board;
127
128 *search->board = *board; search_setup(search);
129
130 PVS_shallow(search, SCORE_MIN, SCORE_MAX, MIN(search->n_empties, 6));
131 hash_get(search->shallow_table, board, board_get_hash_code(board), hash_data);
132
133 *search->board = saved; search_setup(search);
134
135 assert(hash_data->move[0] != NOMOVE || board_is_game_over(board));
136
137 return hash_data->move[0];
138}
139
150void record_best_move(Search *search, const Board *init_board, const Move *bestmove, const int alpha, const int beta, const int depth)
151{
152 Board board[1];
153 Move move[1];
154 Result *result = search->result;
155 int x;
156 unsigned long long hash_code;
157 HashData hash_data[1];
158 bool has_changed;
159 Bound *bound = result->bound + bestmove->x;
160 bool fail_low;
161 bool guess_pv;
162 int expected_depth, expected_selectivity, tmp;
163 Bound expected_bound;
164
165 *board = *init_board;
166
167 spin_lock(result);
168
169 has_changed = (result->move != bestmove->x || result->depth != depth || result->selectivity != search->selectivity);
170
171 result->move = bestmove->x;
172 result->score = bestmove->score;
173
174 assert(search->stability_bound.lower <= result->score && result->score <= search->stability_bound.upper);
175
176 if (result->score < beta && result->score < bound->upper) bound->upper = result->score;
177 if (result->score > alpha && result->score > bound->lower) bound->lower = result->score;
178 if (bound->lower > bound->upper) {
179 if (result->score < beta) bound->upper = result->score; else bound->upper = search->stability_bound.upper;
180 if (result->score > alpha) bound->lower = result->score; else bound->lower = search->stability_bound.lower;
181 }
182
183 expected_depth = result->depth = depth;
184 expected_selectivity = result->selectivity = search->selectivity;
185 expected_bound = *bound;
186
187 line_init(result->pv, search->player);
188 x = bestmove->x;
189
190 guess_pv = (search->options.guess_pv && depth == search->n_empties && (bestmove->score <= alpha || bestmove->score >= beta));
191 fail_low = (bestmove->score <= alpha);
192
193 while (x != NOMOVE) {
194 board_get_move(board, x, move);
195 if (board_check_move(board, move)) {
196 board_update(board, move);
197 --expected_depth;
198 tmp = expected_bound.upper; expected_bound.upper = -expected_bound.lower; expected_bound.lower = -tmp;
199 fail_low = !fail_low;
200 line_push(result->pv, move->x);
201
202 hash_code = board_get_hash_code(board);
203 if ((hash_get(search->pv_table, board, hash_code, hash_data) || hash_get(search->hash_table, board, hash_code, hash_data))
204 && (hash_data->depth >= expected_depth && hash_data->selectivity >= expected_selectivity)
205 && (hash_data->upper <= expected_bound.upper && hash_data->lower >= expected_bound.lower)) {
206 x = hash_data->move[0];
207 } else x = NOMOVE;
208 if (guess_pv && x == NOMOVE && fail_low) x = guess_move(search, board);
209 } else x = NOMOVE;
210 }
211
212 result->time = search_time(search);
213 result->n_nodes = search_count_nodes(search);
214
215 spin_unlock(result);
216
217 if (log_is_open(search_log)) {
218 lock(search_log);
219 log_print(search_log, "id = %d ; ", search->id);
220 log_print(search_log, "level = %2d@%2d%% ; ", result->depth, selectivity_table[result->selectivity].percent);
221 log_print(search_log, "ab = [%+03d, %+03d]:\n", alpha, beta);
222 log_print(search_log, "stability bounds = [%+03d, %+03d]:\n", search->stability_bound.lower, search->stability_bound.upper);
223 log_print(search_log, "%+03d < score = %+03d < %+03d; time = ", result->bound[result->move].lower, result->score, result->bound[result->move].upper);
224 time_print(result->time, false, search_log->f);
225 log_print(search_log, "; nodes = %lld N; ", result->n_nodes);
226 if (result->time > 0) {log_print(search_log, "speed = %9.0f Nps", 1000.0 * result->n_nodes / result->time);}
227 log_print(search_log, "\npv = ");
228 line_print(result->pv, 200, " ", search_log->f);
229 log_print(search_log, "\npv-debug = ");
230 pv_debug(search, bestmove, search_log->f);
231 log_print(search_log, "\n\n");
232 fflush(search_log->f);
233 unlock(search_log);
234 }
235
236 if (has_changed && options.noise <= depth && search->options.verbosity == 3) search->observer(search->result);
237}
238
239void show_current_move(FILE *f, Search *search, const Move *move, const int alpha, const int beta, const bool parallel) {
240 char s[4];
241
242 fprintf(f, "current move: %s [%+03d, %+03d], %s => %+03d; ", (parallel ? " // ":" -- "), alpha, beta, move_to_string(move->x, BLACK, s), move->score);
243 pv_debug(search, move, f);
244}
245
253int search_bound(const Search *search, int score)
254{
255 if (score < search->stability_bound.lower) score = search->stability_bound.lower;
256 if (score > search->stability_bound.upper) score = search->stability_bound.upper;
257 return score;
258}
259
270static int search_route_PVS(Search *search, int alpha, int beta, const int depth, Node *node)
271{
272 int score;
273
274 assert(alpha < beta);
275 assert(SCORE_MIN <= alpha && alpha <= SCORE_MAX);
276 assert(SCORE_MIN <= beta && beta <= SCORE_MAX);
277 assert(depth >= 0 && depth <= search->n_empties);
278
279 if (depth == search->n_empties) {
280 if (depth == 0) score = search_solve_0(search);
281 else score = PVS_midgame(search, alpha, beta, depth, node);
282 } else {
283 if (depth == 0) score = search_eval_0(search);
284 else if (depth == 1) score = search_eval_1(search, alpha, beta);
285 else if (depth == 2) score = search_eval_2(search, alpha, beta);
286 else score = PVS_midgame(search, alpha, beta, depth, node);
287 }
288
289 score = -search_bound(search, -score);
290 assert(SCORE_MIN <= score && score <= SCORE_MAX);
291
292 return score;
293}
294
304{
305 HashTable *hash_table = search->hash_table;
306 HashTable *pv_table = search->pv_table;
307 HashTable *shallow_table = search->shallow_table;
308 HashData hash_data[1];
309 const unsigned long long hash_code = board_get_hash_code(search->board);
310
311 if ((hash_get(pv_table, search->board, hash_code, hash_data) || hash_get(hash_table, search->board, hash_code, hash_data) || hash_get(shallow_table, search->board, hash_code, hash_data))) {
312 return writeable_level(hash_data);
313 }
314 return 0;
315}
316
330int PVS_root(Search *search, const int alpha, const int beta, const int depth)
331{
332 HashData hash_data[1];
333 MoveList *movelist = search->movelist;
334 Board *board = search->board;
335 Move *move;
336 Node node[1];
337 long long cost = -search_count_nodes(search);
338 unsigned long long hash_code;
339 assert(alpha < beta);
340 assert(SCORE_MIN <= alpha && alpha <= SCORE_MAX);
341 assert(SCORE_MIN <= beta && beta <= SCORE_MAX);
342 assert(depth > 0 && depth <= search->n_empties);
343
344 search->probcut_level = 0;
345 search->result->n_moves_left = search->result->n_moves;
346
347 cassio_debug("PVS_root [%d, %d], %d@%d%%\n", alpha, beta, depth, selectivity_table[search->selectivity].percent);
348 if (search->options.verbosity == 4) printf("PVS_root [%d, %d], %d@%d%%\n", alpha, beta, depth, selectivity_table[search->selectivity].percent);
351
352 // transposition cutoff
353 hash_code = board_get_hash_code(board);
354
355 node_init(node, search, alpha, beta, depth, movelist->n_moves, NULL);
356 node->pv_node = true;
357 search->node_type[0] = PV_NODE;
358 search->time.can_update = false;
359
360 // special cases: pass or game over
361 if (movelist_is_empty(movelist)) {
362 move = movelist->move->next = movelist->move + 1;
363 move->flipped = 0;
364 if (can_move(board->opponent, board->player)) {
366 node->bestscore = move->score = -search_route_PVS(search, -node->beta, -node->alpha, depth, node);
368 node->bestmove = move->x = PASS;
369 } else { // game over
370 node->bestscore = move->score = search_solve(search);
371 move->x = NOMOVE;
372 }
373 } else {
374
375 // first move
376 if ((move = node_first_move(node, movelist))) {
377 assert(board_check_move(board, move));
378 search_update_midgame(search, move); search->node_type[search->height] = PV_NODE;
379 move->score = -search_route_PVS(search, -beta, -alpha, depth - 1, node);
380 move->cost = search_get_pv_cost(search);
381 assert(SCORE_MIN <= move->score && move->score <= SCORE_MAX);
382 assert(search->stability_bound.lower <= move->score && move->score <= search->stability_bound.upper);
383 search_restore_midgame(search, move);
384 if (log_is_open(search_log)) show_current_move(search_log->f, search, move, alpha, beta, false);
385 node_update(node, move);
386 if (search->options.verbosity == 4) pv_debug(search, move, stdout);
387
388 search->time.can_update = true;
389
390 // other moves : try to refute the first/best one
391 while ((move = node_next_move(node))) {
392 const int alpha = depth > search->options.multipv_depth ? node->alpha : SCORE_MIN;
393
394 assert(board_check_move(board, move));
395 if (depth > search->options.multipv_depth && node_split(node, move)) {
396 } else {
397 search_update_midgame(search, move);
398 move->score = -search_route_PVS(search, -alpha - 1, -alpha, depth - 1, node);
399 if (alpha < move->score && move->score < beta) {
400 search->node_type[search->height] = PV_NODE;
401 move->score = -search_route_PVS(search, -beta, -alpha, depth - 1, node);
402 }
403 move->cost = search_get_pv_cost(search);
404 assert(SCORE_MIN <= move->score && move->score <= SCORE_MAX);
405 search_restore_midgame(search, move);
406 if (log_is_open(search_log)) show_current_move(search_log->f, search, move, alpha, beta, false);
407 node_update(node, move);
408 assert(SCORE_MIN <= node->bestscore && node->bestscore <= SCORE_MAX);
409 }
410 if (search->options.verbosity == 4) pv_debug(search, move, stdout);
411 if (search_time(search) > search->time.maxi && node->bestscore > alpha) search->stop = STOP_TIMEOUT;
412 }
413 node_wait_slaves(node);
414 }
415 }
416
417
418 if (!search->stop) {
419 hash_get(search->pv_table, board, hash_code, hash_data);
420 if (depth < search->options.multipv_depth) movelist_sort(movelist);
421 else movelist_sort_cost(movelist, hash_data);
422 movelist_sort_bestmove(movelist, node->bestmove);
423 record_best_move(search, board, movelist_first(movelist), alpha, beta, depth);
424
425 if (movelist->n_moves == get_mobility(board->player, board->opponent)) {
426 cost += search_count_nodes(search);
427 hash_store(search->hash_table, board, hash_code, depth, search->selectivity, last_bit(cost), alpha, beta, node->bestscore, node->bestmove);
428 if (search->options.guess_pv) hash_force(search->pv_table, board, hash_code, depth, search->selectivity, last_bit(cost), alpha, beta, node->bestscore, node->bestmove);
429 else hash_store(search->pv_table, board, hash_code, depth, search->selectivity, last_bit(cost), alpha, beta, node->bestscore, node->bestmove);
430 }
431
432 assert(SCORE_MIN <= node->bestscore && node->bestscore <= SCORE_MAX);
433
434 }
435
436 node_free(node);
437
438 return node->bestscore;
439}
440
451int aspiration_search(Search *search, int alpha, int beta, const int depth, int score)
452{
453 int low, high, left, right;
454 int width;
455 int i;
456 int old_score;
457 Move *move;
458 extern Log xboard_log[1];
459
460 assert(alpha < beta);
461 assert(SCORE_MIN <= alpha && alpha <= SCORE_MAX);
462 assert(SCORE_MIN <= beta && beta <= SCORE_MAX);
463 assert(SCORE_MIN <= score && score <= SCORE_MAX);
464 assert(depth >= 0 && depth <= search->n_empties);
465
466 log_print(xboard_log, "edax (search)> search [%d, %d] %d (%d)\n", alpha, beta, depth, score);
467
468 if (is_depth_solving(depth, search->n_empties)) {
469 if (alpha & 1) --alpha;
470 if (beta & 1) ++beta;
471 }
472
473 // at shallow depths always use a large window, for better move ordering
474 if (depth <= search->options.multipv_depth) {
475 alpha = SCORE_MIN;
476 beta = SCORE_MAX;
477 }
478
479
480 high = MIN(SCORE_MAX, search->stability_bound.upper + 2);
481 low = MAX(SCORE_MIN, search->stability_bound.lower - 2);
482 if (alpha < low) alpha = low;
483 if (beta > high) beta = high;
484 if (score < low) score = low; else if (score > high) score = high;
485 if (score < alpha) score = alpha; else if (score > beta) score = beta;
486 log_print(search_log, "initial bound = [%+03d, %+03d]\n", low, high);
487
488 foreach_move(move, search->movelist) {
489 search->result->bound[move->x].lower = low;
490 search->result->bound[move->x].upper = high;
491 }
492
493 width = 10 - depth; if (width < 1) width = 1;
494 if ((width & 1) && depth == search->n_empties) ++width;
495
496 for (i = 0; i < 10; ++i) {
497 old_score = score;
498
499 // if in multipv mode or the alphabeta window is already small, search directly
500 if (depth <= search->options.multipv_depth || beta - alpha <= 2 * width) {
501 log_print(search_log, "direct root_PVS [%d, %d]:\n", low, high);
502 score = PVS_root(search, alpha, beta, depth);
503 } else { // otherwise iterate search with small windows until the score is bounded by the window, or a cut
504 left = right = (i <= 0 ? 1 : i) * width;
505 for (;;) {
506 low = score - left; if (low < alpha) low = alpha;
507 high = score + right; if (high > beta) high = beta;
508 if (low >= high) break;
509 if (low >= SCORE_MAX) low = SCORE_MAX - 1;
510 if (high <= SCORE_MIN) high = SCORE_MIN + 1;
511 log_print(search_log, "aspiration search [%d, %d]:\n", low, high);
512
513 score = PVS_root(search, low, high, depth);
514
515 if (search->stop) break;
516
517 if (score <= low && score > alpha && left > 0) {
518 left *= 2;
519 right = 0;
520 } else if (score >= high && score < beta && right > 0) {
521 left = 0;
522 right *= 2;
523 } else break;
524 }
525 }
526 // emergency stop
527 if (search->stop) break;
528
529 // check PV if alpha < score < beta
530 if (is_depth_solving(depth, search->n_empties)
531 && ((alpha < score && score < beta) || (score == alpha && score == options.alpha) || (score == beta && score == options.beta))
532 && !is_pv_ok(search, search->result->move, depth)) {
533 log_print(search_log, "*** WRONG PV => re-research id %d ***\n", search->id);
534 if (log_is_open(search_log)) {
535 pv_debug(search, movelist_first(search->movelist), search_log->f);
536 putc('\n', search_log->f); fflush(search_log->f);
537 }
538 if (options.debug_cassio) {
539 printf("DEBUG: Wrong PV: "); pv_debug(search, movelist_first(search->movelist), stdout); putchar('\n'); fflush(stdout);
540 if (log_is_open(engine_log)) {
541 fprintf(engine_log->f, "DEBUG: Wrong PV: "); pv_debug(search, movelist_first(search->movelist), engine_log->f);
542 putc('\n', engine_log->f); fflush(engine_log->f);
543 }
544 }
545 continue;
546 }
547 if (is_depth_solving(depth, search->n_empties) && (score & 1)) {
548 log_print(search_log, "*** UNEXPECTED ODD SCORE (score=%+d) => re-research id %d ***\n", score, search->id);
549 cassio_debug("wrong odd score => re-research.\n");
550 continue;
551 }
552 if (score == old_score) break;
553 }
554
555 if (!search->stop) record_best_move(search, search->board, movelist_first(search->movelist), alpha, beta, depth);
556 search->result->time = search_time(search);
557 search->result->n_nodes = search_count_nodes(search);
558 if (options.noise <= depth && search->options.verbosity >= 2) {
559 search->observer(search->result);
560 }
561
562 return score;
563}
564
572static bool get_last_level(Search *search, int *depth, int *selectivity)
573{
574 int i, d, s, x;
575 Board board[1];
576 Move move[1];
577 unsigned long long hash_code;
578 HashData hash_data[1];
579
580
581 *board = *search->board;
582
583 *depth = *selectivity = -1;
584
585 for (i = 0; i < 4; ++i) {
586 hash_code = board_get_hash_code(board);
587 if (hash_get(search->pv_table, board, hash_code, hash_data)) {
588 x = hash_data->move[0];
589 } else if (hash_get(search->hash_table, board, hash_code, hash_data)) {
590 x = hash_data->move[0];
591 } else break;
592
593 d = hash_data->depth + i;
594 s = hash_data->selectivity;
595
596 if (d > *depth) *depth = d;
597 if (s > *selectivity) *selectivity = s;
598
599 board_get_move(board, x, move);
600 board_update(board, move);
601
602 if (x == PASS) --i;
603 }
604
605 return *depth > -1 && *selectivity > -1;
606}
607
608
609
621void iterative_deepening(Search *search, int alpha, int beta)
622{
623 Result *result = search->result;
624 MoveList *movelist = search->movelist;
625 Board *board = search->board;
626 Move *bestmove, *move;
627 HashData hash_data[1];
628 int score, end, start;
629 long long t;
630 bool has_time;
631 int old_depth, old_selectivity, tmp_selectivity;
632
633 assert(alpha < beta);
634 assert(SCORE_MIN <= alpha && alpha <= SCORE_MAX);
635 assert(SCORE_MIN <= beta && beta <= SCORE_MAX);
636
637 // initialize result
638 result->move = NOMOVE;
639 result->score = -SCORE_INF;
640 result->depth = -1;
641 result->selectivity = 0;
642 result->time = 0;
643 result->n_nodes = 0;
644 line_init(result->pv, search->player);
645
646 // special case: game over...
647 if (movelist_is_empty(movelist) && !can_move(board->opponent, board->player)) {
648 result->move = NOMOVE;
649 result->score = search_solve(search);
650 result->depth = search->n_empties;
651 result->selectivity = NO_SELECTIVITY;
652 result->time = search_time(search);
653 result->n_nodes = search_count_nodes(search);
654 result->bound[NOMOVE].lower = result->bound[NOMOVE].upper = result->score;
655 line_init(result->pv, search->player);
656 return;
657 }
658
659 score = search_bound(search, search_eval_0(search));
660 end = search->options.depth;
661 if (end >= search->n_empties) {
662 end = search->n_empties - ITERATIVE_MIN_EMPTIES + 2;
663 if (end <= 0) end = 2 - (search->n_empties & 1);
664 }
665 start = 6 - (end & 1);
666 if (start > end - 2) start = end - 2;
667 if (start <= 0) start = 2 - (end & 1);
668
669 if (USE_PROBCUT && search->options.depth > 10) search->selectivity = 0;
670 else search->selectivity = NO_SELECTIVITY;
671
672 old_depth = 0; old_selectivity = search->selectivity;
673
674 if (log_is_open(search_log)) {
675 lock(search_log);
676 log_print(search_log, "\n\n*** Search: id: %d ***\n", search->id);
677 }
678
679 // reuse last search ?
680 if (hash_get(search->pv_table, board, board_get_hash_code(board), hash_data)) {
681 char s[2][3];
682 if (search->options.verbosity >= 2) {
683 info("<hash: value = [%+02d, %+02d] ; bestmove = %s, %s ; level = %d@%d%% ; date = %d ; cost = %d>\n",
684 hash_data->lower, hash_data->upper,
685 move_to_string(hash_data->move[0], search->player, s[0]),
686 move_to_string(hash_data->move[1], search->player, s[1]),
687 hash_data->depth, selectivity_table[hash_data->selectivity].percent,
688 hash_data->date, hash_data->cost);
689 }
690 if (log_is_open(search_log)) {
691 log_print(search_log, "--- Next Search ---: ");
692 hash_print(hash_data, search_log->f);
693 }
694 old_depth = hash_data->depth;
695 old_selectivity = hash_data->selectivity;
696
698 if (hash_data->lower == hash_data->upper) {
699 if (get_last_level(search, &old_depth, &old_selectivity)) {
700 start = old_depth;
701 search->selectivity = old_selectivity;
702 }
703 score = hash_data->lower;
704 } else {
705 search_adjust_time(search, true);
706 log_print(search_log, "--- New Search (inexact score) ---:\n");
707 }
708 }
709
710 } else {
711 search_adjust_time(search, false);
712 log_print(search_log, "--- New Search ---:\n");
713 }
714
715 if (search->selectivity > search->options.selectivity) search->selectivity = search->options.selectivity;
716
717 if (start > search->options.depth) start = search->options.depth;
718 if (start > search->n_empties) start = search->n_empties;
719 if (start < search->n_empties) {
720 if ((start & 1) != (end & 1)) ++start;
721 if (start <= 0) start = 2 - (end & 1);
722 if (start > end) start = end;
723 }
724
725 if (log_is_open(search_log)) {
726 log_print(search_log,"date: pv = %d, main = %d %s\n", search->pv_table->date, search->hash_table->date, search->options.keep_date ? "(keep)":"");
727 log_print(search_log,"iterating from level %d@%d\n", start, selectivity_table[search->selectivity].percent);
728 log_print(search_log, "alloted time: mini=%.1fs maxi=%.1fs extra=%.1fs\n", 0.001 * search->time.mini, 0.001 * search->time.maxi, 0.001 * search->time.extra);
729 unlock(search_log);
730 }
731
732 // sort moves & display initial value
733 tmp_selectivity = search->selectivity; search->selectivity = old_selectivity;
734 if (!movelist_is_empty(movelist)) {
735 if (end == 0) { // shuffle the movelist
736 foreach_move(move, movelist) move->score = (random_get(search->random) & 0x7fffffff);
737 // randomness is not perfect as several moves may share the same value, but this should be rare enough not to care about it.
738 } else { // sort the moves
739 movelist_evaluate(movelist, search, hash_data, alpha, start);
740 }
741 movelist_sort(movelist);
742 bestmove = movelist_first(movelist); bestmove->score = score;
743 record_best_move(search, board, bestmove, alpha, beta, old_depth);
744 assert(SCORE_MIN <= result->score && result->score <= SCORE_MAX);
745 } else {
746 Move pass = MOVE_PASS;
747 bestmove = &pass; bestmove->score = score;
748 record_best_move(search, board, bestmove, alpha, beta, old_depth);
749 assert(SCORE_MIN <= result->score && result->score <= SCORE_MAX);
750 }
751 search->selectivity = tmp_selectivity;
752
753 // display current search status
754 if (options.noise <= start && search->options.verbosity >= 2) {
755 search->result->time = search_time(search);
756 search->result->n_nodes = search_count_nodes(search);
757 search->observer(search->result);
758 }
759
760 // special case : level 0
761 if (end == 0) {
762 return;
763 }
764
765 // midgame : iterative depth
766 for (search->depth = start; search->depth < end; search->depth += 2) {
767 search->depth_pv_extension = get_pv_extension(search->depth, search->n_empties);
768 score = aspiration_search(search, alpha, beta, search->depth, score);
769 if (!search_continue(search)) return;
770 if (abs(score) >= SCORE_MAX - 1 && search->depth > end - ITERATIVE_MIN_EMPTIES && search->options.depth >= search->n_empties) break;
771 }
772 search->depth = end;
773
774 // switch to endgame
775 if (search->options.depth >= search->n_empties) search->depth = search->n_empties;
776
777 // iterative selectivity
778 t = (search->options.time - search_time(search));
779 has_time = (solvable_depth(t / 10, search_count_tasks(search)) > search->depth);
780 for (; search->selectivity <= search->options.selectivity; ++search->selectivity) {
781 if (search->depth == search->n_empties &&
782 ( (search->depth < 21 && search->selectivity >= 1)
783 || (search->depth < 24 && search->selectivity >= 2)
784 || (search->depth < 27 && search->selectivity >= 3)
785 || (search->depth < 30 && search->selectivity >= 4)
786 || (has_time && search->depth < 30 && search->selectivity >= 2)
787 || (abs(score) >= SCORE_MAX))) { // jump to exact endgame (to solve faster) ?
788 search->selectivity = search->options.selectivity;
789 }
790 if (search->selectivity == search->options.selectivity) search_adjust_time(search, true);
791 score = aspiration_search(search, alpha, beta, search->depth, score);
792 if (!search_continue(search)) return;
793 }
794 if (search->selectivity > search->options.selectivity) search->selectivity = search->options.selectivity;
795}
796
810void* search_run(void *v)
811{
812 Search *search = (Search*) v;
813 Move *move;
814
815 search->stop = RUNNING;
816
817 //initialisations
818 search->n_nodes = 0;
819 search->child_nodes = 0;
820 search->time.spent = -search_clock(search);
821 search_time_init(search);
822 if (!search->options.keep_date) {
823 hash_clear(search->hash_table);
824 hash_clear(search->pv_table);
825 hash_clear(search->shallow_table);
826 }
827 search->height = 0;
828 search->node_type[search->height] = PV_NODE;
829 search->depth_pv_extension = get_pv_extension(0, search->n_empties);
830 search->stability_bound.upper = SCORE_MAX - 2 * get_stability(search->board->opponent, search->board->player);
831 search->stability_bound.lower = 2 * get_stability(search->board->player, search->board->opponent) - SCORE_MAX;
832 search->result->score = search_bound(search, search_eval_0(search));
833 search->result->n_moves_left = search->result->n_moves = search->movelist->n_moves;
834 search->result->book_move = false;
835
836 if (!movelist_is_empty(search->movelist)) {
837 foreach_move(move, search->movelist) {
838 search->result->bound[move->x].lower = SCORE_MIN;
839 search->result->bound[move->x].upper = SCORE_MAX;
840 }
841 } else {
842 search->result->bound[PASS].lower = SCORE_MIN;
843 search->result->bound[PASS].upper = SCORE_MAX;
844 }
845
846 // search using iterative deepening (& widening).
848
849 // finalizations
850 search->result->n_nodes = search_count_nodes(search);
851 if (search->options.verbosity) {
852 if (search->options.verbosity == 1 || options.noise > search->result->depth) search->observer(search->result);
853 if (search->stop == STOP_TIMEOUT) {info("[Search out of time]\n");}
854 else if (search->stop == STOP_ON_DEMAND) {info("[Search stopped on user demand]\n");}
855 else if (search->stop == STOP_PONDERING) {info("[Pondering stopped]\n");}
856 else if (search->stop == RUNNING) {info("[Search completed]\n");}
857 }
858
859 if (log_is_open(search_log)) {
860 lock(search_log);
861 log_print(search_log, "\n*** Search id: %d ", search->id);
862 if (search->stop == STOP_TIMEOUT) log_print(search_log, "out of time");
863 else if (search->stop == STOP_ON_DEMAND) log_print(search_log, "stopped on user demand");
864 else if (search->stop == STOP_PONDERING) log_print(search_log, "stop pondering");
865 else if (search->stop == STOP_PARALLEL_SEARCH) log_print(search_log, "### BUG: stop parallel search reached root! ###");
866 else if (search->stop == RUNNING) log_print(search_log, "completed");
867 else log_print(search_log, "### BUG: unkwown stop condition %d ###", search->stop);
868 log_print(search_log, " ***\n\n");
869 unlock(search_log);
870 }
871
872 if (search->stop == RUNNING) search->stop = STOP_END;
873 search->time.spent += search_clock(search);
874 search->result->time = search->time.spent;
875
876 statistics_sum_nodes(search);
877 if (search->options.verbosity >= 3) statistics_print(stdout);
878
879 assert(search->height == 0);
880
881 return search->result;
882}
883
DLL_API int last_bit(unsigned long long b)
Search the last bit set (same as log2()).
Definition bit.c:264
bool board_check_move(const Board *board, Move *move)
Check if a move is legal.
Definition board.c:452
unsigned long long board_get_hash_code(const Board *board)
Compute a hash code.
Definition board.c:1134
void board_restore(Board *board, const Move *move)
Restore a board.
Definition board.c:487
bool board_is_game_over(const Board *board)
Check if the game is over.
Definition board.c:1203
void board_update(Board *board, const Move *move)
Update a board.
Definition board.c:469
unsigned long long board_get_move(const Board *board, const int x, Move *move)
Compute a move.
Definition board.c:438
int get_stability(const unsigned long long P, const unsigned long long O)
Estimate the stability.
Definition board.c:1067
int get_mobility(const unsigned long long P, const unsigned long long O)
Count legal moves.
Definition board.c:833
DLL_API bool can_move(const unsigned long long P, const unsigned long long O)
Check if a player can move.
Definition board.c:797
#define SCORE_MAX
Definition const.h:58
#define SCORE_INF
Definition const.h:52
@ PASS
Definition const.h:37
@ NOMOVE
Definition const.h:37
@ BLACK
Definition const.h:42
@ RUNNING
Definition const.h:71
@ STOP_ON_DEMAND
Definition const.h:75
@ STOP_TIMEOUT
Definition const.h:74
@ STOP_PARALLEL_SEARCH
Definition const.h:72
@ STOP_END
Definition const.h:76
@ STOP_PONDERING
Definition const.h:73
@ PV_NODE
Definition const.h:81
#define SCORE_MIN
Definition const.h:55
int search_solve(const Search *search)
Get the final score.
Definition endgame.c:52
int search_solve_0(const Search *search)
Get the final score.
Definition endgame.c:80
bool hash_get(HashTable *hash_table, const unsigned long long hash_code, HashData *data)
Find an hash table entry according to the evaluated board hash codes.
Definition hash-lock-free.c:600
void hash_clear(HashTable *hash_table)
Clear the hashtable.
Definition hash-lock-free.c:179
unsigned int writeable_level(HashData *data)
make a level from date, cost, depth & selectivity.
Definition hash-lock-free.c:208
void hash_print(const HashData *data, FILE *f)
print HashData content.
Definition hash-lock-free.c:670
void hash_store(HashTable *hash_table, const unsigned long long hash_code, const int depth, const int selectivity, const int cost, const int alpha, const int beta, const int score, const int move)
Store an hashtable item.
Definition hash-lock-free.c:543
void hash_force(HashTable *hash_table, const unsigned long long hash_code, const int depth, const int selectivity, const int cost, const int alpha, const int beta, const int score, const int move)
Store an hashtable item.
Definition hash-lock-free.c:575
int search_eval_2(Search *search, int alpha, const int beta)
Evaluate a position at depth 2.
Definition midgame.c:147
int search_eval_0(Search *search)
evaluate a midgame position with the evaluation function.
Definition midgame.c:32
int PVS_midgame(Search *search, const int alpha, const int beta, int depth, Node *parent)
Evaluate a position with a deep Principal Variation Search algorithm.
Definition midgame.c:585
int PVS_shallow(Search *search, int alpha, int beta, int depth)
Evaluate a midgame position at shallow depth.
Definition midgame.c:381
int search_eval_1(Search *search, const int alpha, int beta)
Evaluate a position at depth 1.
Definition midgame.c:74
void line_print(const Line *line, int width, const char *separator, FILE *f)
Print a move sequence.
Definition move.c:610
bool movelist_is_empty(const MoveList *movelist)
Check if the list is empty.
Definition move.c:537
void line_push(Line *line, const int x)
Add a move to the sequence.
Definition move.c:561
const Move MOVE_PASS
Definition move.c:26
void movelist_sort(MoveList *movelist)
Sort all moves.
Definition move.c:505
Move * movelist_first(MoveList *movelist)
Return the first move of the list.
Definition move.c:422
void line_init(Line *line, const int player)
Initialize a sequence of moves.
Definition move.c:549
void movelist_sort_cost(MoveList *movelist, const HashData *hash_data)
Sort all moves except the first, based on move cost & hash_table storage.
Definition move.c:489
void movelist_evaluate(MoveList *movelist, Search *search, const HashData *hash_data, const int alpha, const int depth)
Evaluate a list of move in order to sort it.
Definition move.c:436
char * move_to_string(const int x, const int player, char *s)
Print out a move.
Definition move.c:76
Move * movelist_sort_bestmove(MoveList *movelist, const int move)
Sort a move as best.
Definition move.c:468
#define foreach_move(iter, movelist)
Definition move.h:78
Options options
Definition options.c:22
void iterative_deepening(Search *search, int alpha, int beta)
Iterative deepening.
Definition root.c:621
int PVS_root(Search *search, const int alpha, const int beta, const int depth)
Principal Variation Search algorithm at the root of the tree.
Definition root.c:330
int search_get_pv_cost(Search *search)
Compute a cost as a combination of node count, depth, etc. from hash_table.
Definition root.c:303
Log engine_log[1]
Definition cassio.c:35
static int guess_move(Search *search, Board *board)
Guess a move.
Definition root.c:123
Log search_log[1]
Definition search.c:74
static bool get_last_level(Search *search, int *depth, int *selectivity)
Retrieve the last level of the search.
Definition root.c:572
void pv_debug(Search *search, const Move *bestmove, FILE *f)
Debug PV.
Definition root.c:33
void show_current_move(FILE *f, Search *search, const Move *move, const int alpha, const int beta, const bool parallel)
Definition root.c:239
bool is_pv_ok(Search *search, int bestmove, int search_depth)
Check if PV is ok.
Definition root.c:83
static int search_route_PVS(Search *search, int alpha, int beta, const int depth, Node *node)
Reroute the PVS between midgame,endgame or terminal PVS.
Definition root.c:270
int search_bound(const Search *search, int score)
bound root scores according to stable squares
Definition root.c:253
int aspiration_search(Search *search, int alpha, int beta, const int depth, int score)
Aspiration window.
Definition root.c:451
void record_best_move(Search *search, const Board *init_board, const Move *bestmove, const int alpha, const int beta, const int depth)
Record best move.
Definition root.c:150
void * search_run(void *v)
Search the bestmove of a given board.
Definition root.c:810
void search_time_init(Search *search)
Initialize the alloted time.
Definition search.c:697
const int NO_SELECTIVITY
Definition search.c:94
bool search_continue(Search *search)
Check if it can iterate more...
Definition search.c:790
const Selectivity selectivity_table[]
Definition search.c:97
long long search_clock(Search *search)
Return the time spent by the search.
Definition search.c:1049
void search_restore_pass_midgame(Search *search)
Update the search state after a passing move.
Definition search.c:998
unsigned long long search_count_nodes(Search *search)
Return the number of nodes searched.
Definition search.c:1073
int get_pv_extension(const int depth, const int n_empties)
Compute the pv_extension.
Definition search.c:1012
void search_adjust_time(Search *search, const bool once)
Give more time.
Definition search.c:771
bool is_depth_solving(const int depth, const int n_empties)
Check if final score use pv_extension or is solved.
Definition search.c:1032
void search_restore_midgame(Search *search, const Move *move)
Restore the search state as before a move.
Definition search.c:964
void search_update_midgame(Search *search, const Move *move)
Update the search state after a move.
Definition search.c:942
int solvable_depth(const long long limit, int n_tasks)
Compute the deepest level that can be solved given a limited time...
Definition search.c:650
int search_count_tasks(const Search *search)
Count the number of tasks used in parallel search.
Definition search.c:1324
void search_setup(Search *search)
Set up various structure once the board has been set.
Definition search.c:466
void search_update_pass_midgame(Search *search)
Update the search state after a passing move.
Definition search.c:983
long long search_time(Search *search)
Return the time spent by the search.
Definition search.c:1061
#define ITERATIVE_MIN_EMPTIES
Definition settings.h:89
#define USE_PROBCUT
Definition settings.h:36
#define USE_PREVIOUS_SEARCH
Definition settings.h:62
void statistics_sum_nodes(Search *search)
Cumulate node counts from the last search.
Definition stats.c:97
Statistics statistics
Definition stats.c:21
void statistics_print(FILE *f)
Print statistics.
Definition stats.c:112
Statistics header.
#define SEARCH_UPDATE_INTERNAL_NODES()
Definition stats.h:40
#define SEARCH_STATS(x)
Definition stats.h:27
Definition board.h:26
unsigned long long player
Definition board.h:27
unsigned long long opponent
Definition board.h:27
Definition search.h:35
int lower
Definition search.h:36
int upper
Definition search.h:37
Definition hash.h:24
unsigned char move[2]
Definition hash.h:31
signed char lower
Definition hash.h:29
unsigned char cost
Definition hash.h:27
unsigned char date
Definition hash.h:28
signed char upper
Definition hash.h:30
unsigned char selectivity
Definition hash.h:26
unsigned char depth
Definition hash.h:25
Definition hash.h:47
unsigned char date
Definition hash.h:55
LogFile.
Definition util.h:423
FILE * f
Definition util.h:424
Definition move.h:29
int n_moves
Definition move.h:31
Move move[MAX_MOVE+2]
Definition move.h:30
Definition move.h:20
unsigned int cost
Definition move.h:24
int score
Definition move.h:23
struct Move * next
Definition move.h:25
unsigned long long flipped
Definition move.h:21
int x
Definition move.h:22
Definition ybwc.h:48
int beta
Definition ybwc.h:52
bool pv_node
Definition ybwc.h:53
volatile int alpha
Definition ybwc.h:51
volatile int bestscore
Definition ybwc.h:50
volatile int bestmove
Definition ybwc.h:49
int alpha
Definition options.h:52
int noise
Definition options.h:33
int beta
Definition options.h:53
bool debug_cassio
Definition options.h:37
Definition search.h:41
bool book_move
Definition search.h:50
int move
Definition search.h:44
unsigned long long n_nodes
Definition search.h:49
int score
Definition search.h:45
long long time
Definition search.h:48
int n_moves_left
Definition search.h:52
int selectivity
Definition search.h:43
Line pv[1]
Definition search.h:47
int depth
Definition search.h:42
int n_moves
Definition search.h:51
Bound bound[BOARD_SIZE+2]
Definition search.h:46
Definition search.h:95
struct Search::@24 time
long long maxi
Definition search.h:130
volatile long long spent
Definition search.h:126
int n_empties
Definition search.h:99
bool keep_date
Definition search.h:143
void(* observer)(Result *)
Definition search.h:153
long long extra
Definition search.h:125
bool guess_pv
Definition search.h:146
volatile unsigned long long child_nodes
Definition search.h:156
MoveList movelist[1]
Definition search.h:132
bool can_update
Definition search.h:128
Result * result
Definition search.h:151
int depth_pv_extension
Definition search.h:121
int height
Definition search.h:133
int id
Definition search.h:101
int player
Definition search.h:100
struct Search::@25 options
Random random[1]
Definition search.h:107
int verbosity
Definition search.h:142
int depth
Definition search.h:117
int probcut_level
Definition search.h:119
Bound stability_bound
Definition search.h:135
long long mini
Definition search.h:129
HashTable pv_table[1]
Definition search.h:104
HashTable hash_table[1]
Definition search.h:103
volatile Stop stop
Definition search.h:122
volatile unsigned long long n_nodes
Definition search.h:155
HashTable shallow_table[1]
Definition search.h:105
int selectivity
Definition search.h:118
NodeType node_type[GAME_SIZE]
Definition search.h:134
Board board[1]
Definition search.h:96
int multipv_depth
Definition search.h:147
int percent
Definition search.h:28
unsigned long long n_PVS_root
Definition stats.h:76
void time_print(long long t, bool justified, FILE *f)
Print time as "D:HH:MM:SS.CC".
Definition util.c:131
unsigned long long random_get(Random *random)
Pseudo-random number generator.
Definition util.c:1043
Miscellaneous utilities header.
#define MIN(a, b)
Definition util.h:101
#define log_print(l,...)
Print into the log file.
Definition util.h:442
#define info(...)
Display a message.
Definition util.h:382
#define cassio_debug(...)
Display a message in cassio's "fenetre de rapport" .
Definition util.h:389
#define log_is_open(l)
Check if the log stream can be used.
Definition util.h:448
#define MAX(a, b)
Definition util.h:98
Log xboard_log[1]
Definition xboard.c:26
Move * node_next_move(Node *node)
Get the next move of the move list.
Definition ybwc.c:345
void node_wait_slaves(Node *node)
Wait for slaves termination.
Definition ybwc.c:212
bool node_split(Node *node, Move *move)
Node split.
Definition ybwc.c:167
void node_free(Node *node)
Free Resources allocated by a node.
Definition ybwc.c:95
Move * node_first_move(Node *node, MoveList *movelist)
Get the first move of the move list.
Definition ybwc.c:297
void node_update(Node *node, Move *move)
Update a node.
Definition ybwc.c:261
void node_init(Node *node, Search *search, const int alpha, const int beta, const int depth, const int n_moves, Node *parent)
Initialize a node.
Definition ybwc.c:59
Parallel search header.