Fixed sideeffects of enabling the gui to visualize the search tree.
[rattatechess.git] / search.cpp
blobecbba6d556d45c426de0684fb3a350966efa86c9
1 /***************************************************************************
2 search.cpp - description
3 -------------------
4 begin : Wed Mar 13 2002
5 copyright : (C) 2002-2005 by Monge Maurizio
6 email : monge@linuz.sns.it
7 ***************************************************************************/
9 /***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
18 #include "engine.h"
19 #include "utils.h"
20 #include "search_gui.h"
21 #include "search.h"
22 #include <string.h>
23 #include <stdlib.h>
27 #if 0
28 #define STAT(x)
29 #else
31 #define S_ALL \
32 S(max_quiesce_nodes); \
33 S(quiesce_nodes); \
34 S(quiesce_hash); \
35 S(quiesce_called); \
36 S(quiesce_best_can_be_first); \
37 S(quiesce_best_was_first); \
38 S(quiesce_cutoff); \
39 S(quiesce_cutoff_first); \
40 S(search_nodes); \
41 S(search_hash); \
42 S(search_best_can_be_first); \
43 S(search_best_was_first); \
44 S(search_cutoff); \
45 S(search_cutoff_first); \
46 S(null_tried); \
47 S(null_successful);
49 #define STAT(x) stat_##x++;
51 #define S(x) uint64_t stat_##x;
52 S_ALL
53 Board max_quiesce_board;
54 int16_t max_quiesce_alpha;
55 int16_t max_quiesce_beta;
56 #undef S
58 void reset_stats()
60 #define S(x) stat_##x = 0;
61 S_ALL
62 #undef S
65 void print_stats()
67 #define S(x) printf("# " #x " = %llu\n", (unsigned long long)stat_##x);
68 S_ALL
69 #undef S
72 #endif
75 /* enable the nega-scout pv-search */
76 #define NEGA_SCOUT 1
78 /* enable the null move */
79 #define NULL_MOVE 1
81 /* il null move > beta-margin, reduce (not a dangerous position). Set to 1 to disable */
82 #define NULL_SOLIDITY_MARGIN 1
84 /* before doing the alpha beta search check if any of the following positions
85 can give use an early cutoff thanks to the hashtable */
86 #define EARLY_TRANSP_CUTOFF 0
88 /* reduce nodes for moves that are not check/captures that are considered
89 bad from the heuristic */
90 #define LATE_MOVE_REDUCTION 0
92 /* futility pruning: */
93 #define FUTILITY 0
95 /* when the hashtable provides no "best" move, do a depth-2 search */
96 #define INTERNAL_ITERATIVE_DEEPENING 0
98 /* use the history sorting heuristic */
99 #define HISTORY_HEURISTIC 1
101 /* improve the sorting heuristic for pawn strikes */
102 #define PAWN_STRIKE 1
104 #define IGNORE_ALLBAD_NODES 0
107 void Engine::print_stat()
109 if(thinking && current_root)
111 output("stat01: %d %llu %d %d %d %s\n",
112 current_time() - start_think_time,
113 (unsigned long long)processed_nodes,
114 current_root->base_depth/PLY,
115 current_root->stack[0].num_moves - 1 - current_root->stack[0].curr_move,
116 current_root->stack[0].num_moves,
117 board.move_to_alg(TmpStr().data(),
118 current_root->stack[0].moves[current_root->stack[0].curr_move])
121 else
122 output("stat01: 0 0 0 0 0\n");
125 bool SearchRoot::null_move_ok()
127 int c = IS_WHITE(board.color_to_move) ? 5 : -1;
128 int numpawns = board.mat_tracking[c+PAWN].count;
129 int numpieces = board.mat_tracking[c+KNIGHT].count + board.mat_tracking[c+BISHOP].count
130 + board.mat_tracking[c+ROOK].count + board.mat_tracking[c+QUEEN].count;
131 if(numpieces >= 2)
132 return true;
133 if(numpieces >= 1 && numpawns >= 2)
134 return true;
135 return false;
138 void
139 SearchRoot::moves_heuristic(Move *mv, int num_mv, bool pv, int ply, int orig_depth,
140 Move best_mv_hash, bool quiesce, Move* prev, int* overall_extensions,
141 int p1, int p2)
143 #if PAWN_STRIKE
144 int escapes[2];
145 int num_escapes = 0;
146 #endif //PAWN_STRIKE
147 int hash_move = -1;
149 for(int i=0;i<num_mv;i++)
151 mv[i].val = 0;
152 mv[i].extend = 0;
154 /* give a high bonus to the move stored in the hash, if any.
155 mark only which is, don't continue, because some extensions
156 may be triggered ad added later (ie pawn strike, etc) */
157 if(mv[i].as_int == best_mv_hash.as_int)
158 hash_move = i;
160 #if PAWN_STRIKE
161 if(num_escapes<=2 && PIECE_OF(board.data[mv[i].from]) != PAWN &&
162 (mv[i].from == stack[ply-1].escape_from[1] || stack[ply-1].escape_from[2]) )
164 int x = board.move_see_val(mv[i]);
166 if(x >= 0)
168 if(num_escapes < 2)
169 escapes[num_escapes] = i;
170 num_escapes++;
173 #endif //PAWN_STRIKE
175 /* process strong pawn moves */
176 if(PIECE_OF(board.data[mv[i].from])==PAWN) /* pawn strike */
178 uint8_t x = X(mv[i].to);
179 uint8_t y = IS_WHITE(board.color_to_move) ? Y(mv[i].to) : 7-Y(mv[i].to);
181 if( mv[i].flags == PROMOTEQUEEN || y == 6)
183 int x = board.move_see_val(mv[i]);
185 if(x>=0)
187 mv[i].val += mv[i].flags ? 29600 : 29599; /* promote! */
188 mv[i].extend = PLY; /* extend search */
189 continue;
193 if(y >= 4)
195 int other = IS_WHITE(board.other_color);
196 uint8_t pos = 7-y;
197 if((!board.line_pawns[other][x].count || board.line_pawns[other][x].pos[0] > pos)
198 && (x==0 || !board.line_pawns[other][x-1].count || board.line_pawns[other][x-1].pos[0] >= pos)
199 && (x==7 || !board.line_pawns[other][x+1].count || board.line_pawns[other][x+1].pos[0] >= pos))
201 int x = board.move_see_val(mv[i]);
203 if(x>=0)
205 mv[i].val += y >= 5 ? 29550 : 29500; /* passed pawn advancing! */
206 if(y >= 5)
207 mv[i].extend = PLY; /* extend search */
208 continue;
213 /* pawn attack */
214 if(orig_depth >= 2*PLY)
216 /* pawn strike */
217 uint8_t up_right = Board::up_dir[IS_WHITE(board.color_to_move)] + RIGHT;
218 uint8_t up_left = Board::up_dir[IS_WHITE(board.color_to_move)] + LEFT;
219 uint8_t p1 = mv[i].to + up_right;
220 uint8_t p2 = mv[i].to + up_left;
221 uint8_t a = OUT_OF_BOARD(p1) ? 0 : board.data[p1];
222 uint8_t b = OUT_OF_BOARD(p2) ? 0 : board.data[p2];
223 if( (COLOR_OF(a)==board.other_color && PIECE_OF(a)!=PAWN)
224 || (COLOR_OF(b)==board.other_color && PIECE_OF(b)!=PAWN) )
226 int x = board.move_see_val(mv[i]);
228 if(x>=0)
230 mv[i].val = 27000; /* pawn strike! */
231 continue;
237 if(mv[i].capture)
239 int x = board.move_see_val(mv[i]);
241 /* recapture? */
242 if(prev && prev->capture &&
243 (mv[i].to == prev->to) && (x >= Board::simple_values[PIECE_OF(prev->capture)]) )
245 mv[i].val = 29900;
246 if( (x == Board::simple_values[PIECE_OF(prev->capture)]) )
247 mv[i].extend = PLY;
248 continue;
251 if(x>0)
253 if(orig_depth>-7*PLY && board.move_is_check(mv[i]) )
254 mv[i].val = 29800+x;
255 else
256 mv[i].val = 29000+x;
257 continue;
259 else if(x>=0 && orig_depth>-7*PLY && board.move_is_check(mv[i]) )
261 /* = capture but check */
262 mv[i].val = 29800;
263 continue;
266 else /* add a bonus for checks (but not too deep, or quiescence will get mad) */
267 if(board.move_is_check(mv[i]) )
269 if(board.move_see_val(mv[i])>=0)
270 mv[i].val = 28799;
271 else
272 mv[i].val = 28700;
273 continue;
276 /* null-move threat */
277 if(stack[ply-1].null_threat == mv[i])
279 mv[i].val = 28500;
280 continue;
283 #if 1
284 mv[i].val += (history_hit[HISTORY_INDEX(mv[i])] + 32) * 1024 / (3 * (history_tot[HISTORY_INDEX(mv[i])] + 64));
286 int mx = CAPT_INDEX(mv[i]);
287 if(p1 != -1)
289 int ix = FAST_PLUTO(p1, mx);
290 mv[i].val += (pluto_hit[ix] + 24) * 1024 / (3 * (pluto_tot[ix] + 48));
292 else
293 mv[i].val += 512/3;
294 if(p2 != -1)
296 int ix = FAST_PLUTO(p2, mx);
297 mv[i].val += (pluto2_hit[ix] + 16) * 1024 / (3 * (pluto2_tot[ix] + 32));
299 else
300 mv[i].val += 512/3;
301 #else
302 mv[i].val += (history_hit[HISTORY_INDEX(mv[i])] + 32) * 256*4 / (history_tot[HISTORY_INDEX(mv[i])] + 64);
303 #endif
306 #if PAWN_STRIKE
307 if((stack[ply-1].escape_from[1] != INVALID || stack[ply-1].escape_from[2] != INVALID) && num_escapes <= 2)
309 for(int i=0;i<num_escapes;i++)
310 mv[escapes[i]].extend = PLY;
312 #endif //PAWN_STRIKE
314 if(hash_move!=-1)
315 mv[hash_move].val = 30000;
319 void
320 SearchRoot::moves_quiescence_heuristic(Move *mv, int num_mv, const Move& best_mv_hash,
321 int static_eval, int alpha, int beta, int orig_depth, Move* prev)
323 int hash_move = -1;
324 for(int i=0;i<num_mv;i++)
326 mv[i].val = -10000;
327 mv[i].extend = 0;
329 /* give a high bonus to the move stored in the hash, if any.
330 mark only which is, don't continue, because some extensions
331 may be triggered ad added later (ie pawn strike, etc) */
332 if(mv[i].as_int == best_mv_hash.as_int)
333 hash_move = i;
335 /* process strong pawn moves */
336 if(PIECE_OF(board.data[mv[i].from])==PAWN)
338 if( ROW_OF(mv[i].to) == board.seventh_rank[IS_WHITE(board.color_to_move)] )
340 int x = board.move_see_val(mv[i]);
342 if(x>=0)
344 mv[i].val += 29499; /* 7th push */
345 mv[i].extend = PLY; /* extend search */
346 continue;
350 if( mv[i].flags == PROMOTEQUEEN )
352 int x = board.move_see_val(mv[i]);
354 if(x<0)
356 mv[i].val += 29500; /* promote! */
357 mv[i].extend = PLY; /* extend search */
358 continue;
363 if(mv[i].capture)
365 int x = board.move_see_val(mv[i]);
367 if(prev && prev->capture &&
368 (mv[i].to == prev->to) && (x >= Board::simple_values[PIECE_OF(prev->capture)]) )
370 mv[i].val = 29900 + x;
371 if( (x == Board::simple_values[PIECE_OF(prev->capture)]) )
372 mv[i].extend = PLY;
373 continue;
376 if( x>0 && ((orig_depth>-2*PLY || static_eval==-INF) ? true : x*100+static_eval+200>alpha) )
378 mv[i].val = 10000+x;
379 continue;
381 else if((x>=0 && orig_depth>-4*PLY) && board.move_is_check(mv[i]) )
383 /* = capture but check */
384 mv[i].val = 8000;
385 continue;
388 else /* add a bonus for checks (but not too deep, or quiescence will get mad) */
389 if(orig_depth>-2*PLY && board.move_is_check(mv[i]) && board.move_see_val(mv[i])>=0)
391 mv[i].val = 5000;
392 continue;
396 if(hash_move >= 0 && mv[hash_move].val > 9000)
397 mv[hash_move].val = 30000;
401 /*******************************************************************************
402 The main alpha-beta recursive search function.
403 It handles both normal search (with or without null move)
404 and quiescence search, because i have having 2 almost identical
405 function around.
406 *******************************************************************************/
407 int16_t SearchRoot::search(int ply, int base_depth, bool pv, int16_t orig_alpha, int16_t beta, bool expect_allbad)
409 SearchStack *s = &stack[ply];
410 int16_t best = -INF;
411 uint16_t cbest_mv_hash = 0; /* the compressed move from the hash */
412 Move best_mv_hash = Move::FromInt(0); /* the move from the hash */
413 int best_mv_found = -1; /* the index of the best move AFTER searching */
414 bool quiesce;
415 bool extended = false;
416 bool no_good_moves = false;
417 int16_t lower_bound = -INF;
418 #if FUTILITY
419 int16_t position_val;
420 #endif
421 int depth = base_depth;
422 int16_t alpha = orig_alpha;
423 int pluto1, pluto2;
425 #if IGNORE_ALLBAD_NODES
426 expect_allbad = false;
427 #endif
429 #if 0
430 if(ply >= 75)
432 for(int i=ply-1;i>=0;i--)
434 if(stack[i].curr_move == -1)
435 board.undo_null_move(stack[i].save_buf);
436 else if(stack[i].curr_move >= 0)
437 board.undo_move(stack[i].moves[stack[i].curr_move], stack[i].save_buf);
439 printf("[FEN \"%s\"]\n", board.to_fen(BigStr().data()) );
440 for(int i=0;i<ply;i++)
442 printf(" %s", stack[i].curr_move <= -1 ? "NULL" :
443 board.move_to_alg(TmpStr().data(), stack[i].moves[stack[i].curr_move]));
444 if(stack[i].curr_move == -1)
445 board.do_null_move(stack[i].save_buf);
446 else if(stack[i].curr_move >= 0)
447 board.do_move(stack[i].moves[stack[i].curr_move], stack[i].save_buf);
449 printf(" *\n");
450 exit(0);
452 #endif
454 // if(depth <= 0)
455 // STAT(quiesce_nodes)
457 s->num_moves = 0;
458 s->curr_move = -3;
459 s->best_move = -1;
460 s->null_threat = Move::FromInt(0);
461 s->null_threat_dangerous = false;
462 #if PAWN_STRIKE
463 stack[ply].escape_from[1] = stack[ply].escape_from[2] = INVALID;
464 #endif //PAWN_STRIKE
466 engine->prefetch_hash( board.hash );
468 /* check if time is running out */
469 engine->check_time();
471 /* check for a draw for repetition or 50mvs. Of course the draw for
472 repetition must be checked BEFORE probing the hash :)*/
473 if(check_draw(ply))
474 return (board.color_to_move == engine->st_computer_color) ? engine->v_eval_draw :
475 ((board.other_color == engine->st_computer_color) ? -engine->v_eval_draw : 0); /* be aggressive! */
477 /*******************************************************************************
478 Probe the hashtable.
479 If the probe is succesful the hashtable will return us value
480 that can be exact, a lower bound or an upper bound, and if the
481 depth of the hashed search is >= the current depth this value
482 will be used to improve alpha and beta and possibly return immediatly.
483 The hastable will also give us a "best" move that will be searched
484 first.
485 This is the move that caused the "final" cutoff when this position
486 was searched previously. This best move is actually the index of the best
487 move in the array of generated moves (it is supposed to be deterministic :)
488 *******************************************************************************/
490 HashEntry *h = engine->probe_hash( board.hash );
492 if(h){
493 GUI(notify_hash(ply, h->lo, h->up, h->depth, alpha, beta,
494 h->best_mv ? board.uncompress_move(h->best_mv) : Move::None(), false,
495 (((h->depth >= base_depth) && (h->lo>=beta || h->up==h->lo || h->up<=alpha))
496 ? SearchGui::Bold : 0) | SearchGui::Magenta) );
499 if(h && (h->depth >= base_depth))// || ABS(h->value)>INF-1000) )
501 int16_t l = h->lower();
502 int16_t u = h->upper();
504 if(l>=beta || u==l)
505 return l;
506 if(u<=alpha)
507 return u;
509 beta = MIN(beta, u);
510 best = alpha = MAX(alpha, l);
513 if(h)
514 cbest_mv_hash = h->best_mv;
516 /*******************************************************************************
517 Test if we are under check, and if so extend search
518 *******************************************************************************/
520 s->under_check = board.under_attack(board.king_pos[IS_WHITE(board.color_to_move)],
521 board.other_color);
523 /*******************************************************************************
524 If it is time to quiesce, evaluate and test if we can exit
525 immediately with a beta cut-off (try first a rough eval - delta)
526 *******************************************************************************/
527 quiesce = ((!s->under_check) && (depth<=0)) || (ply > 70);
528 if(quiesce)
529 STAT(quiesce_nodes);
531 #if 0 //PAPOPEPO
532 if(quiesce && depth>=-PLY)
534 int num_mv;
535 Move *mv = mv_stack + mv_stack_top;
536 board.do_null_move();
537 num_mv = board.find_moves(mv);
538 uint8_t pup = INVALID;
540 for(int i=0;i<num_mv;i++)
542 if(!mv[i].capture || PIECE_OF(mv[i].capture)==PAWN)
543 continue;
544 if(mv[i].to != pup && board.move_see_val(mv[i])>0)
545 if(pup == INVALID)
546 pup = mv[i].to;
547 else
549 quiesce = false;
550 break;
554 board.undo_null_move();
556 #endif
558 if(quiesce && (best == -INF || ply>70) )
560 best = board.evaluate(engine->st_computer_color, alpha, beta);
561 lower_bound = best; //we have at the very least "best" as lower bound now.
562 GUI(notify_eval(ply, best, alpha, beta, best>=beta ? SearchGui::Blue|SearchGui::Bold : SearchGui::Blue ));
564 alpha = MAX(alpha, best);
565 if(best >= beta)
566 goto search_done;
568 if(ply>70)
569 goto search_done;
572 if(quiesce && h && h->no_good_moves)
573 goto search_done;
575 #if NULL_MOVE
576 /*******************************************************************************
577 Try the null move.
578 *******************************************************************************/
579 if(!expect_allbad && !pv && !s->under_check && (stack[ply-1].curr_move != -1)
580 && depth >= 2*PLY && beta<WORST_MATE && null_move_ok())
582 int16_t val;
583 int sdepth = (depth >= 5*PLY) ? (depth-4*PLY) : depth-3*PLY;
585 s->curr_move = -1;
586 board.do_null_move(s->save_buf);
587 GUI1(notify(&board, Move::None(), ply, sdepth, -INF, beta-NULL_SOLIDITY_MARGIN, beta, SearchGui::Green ));
588 val = -search( ply+1, sdepth, false, -beta, -beta+NULL_SOLIDITY_MARGIN, false);
589 GUI2(notify_value(ply, val, DIFF_NODES, SearchGui::Bold ));
590 board.undo_null_move(s->save_buf);
592 /* null move cut! */
593 if(val >= beta)
594 return val;
596 #if NULL_SOLIDITY_MARGIN > 1
597 if(depth <= 3*PLY && val > beta - NULL_SOLIDITY_MARGIN)
598 depth -= PLY;
599 #endif
601 if(val < -WORST_MATE)
602 s->null_threat_dangerous = true;
604 #if 0
605 if(val<alpha-100 && /* we are facing a threat*/
606 stack[ply+1].best_move != -1) /* be sure we aren't reading random memory */
608 /* ok, officially the array stack[ply+1].moves has already
609 been deallocated, but who cares :) */
610 s->threat = stack[ply+1].moves[stack[ply+1].best_move];
612 #if 0
613 /* Botvinnik-Markoff extension!!! */
614 if(!extended && ply>=3 && (s->threat == stack[ply-2].threat) )
616 depth += 80;
617 extended = true;
619 #endif
621 #endif
623 #endif
627 /*******************************************************************************
628 Now generate the legal moves and look for a check/stalemate
629 *******************************************************************************/
631 /* generate all the legal moves */
632 s->moves = &mv_stack[mv_stack_top];
633 s->num_moves = board.find_moves(s->moves);
634 mv_stack_top += s->num_moves;
635 s->under_check = board.under_check;
637 pluto1 = stack[ply-1].best_move >=0 ? PLUTO1(stack[ply-1].moves[stack[ply-1].best_move]) : -1;
638 pluto2 = (ply>=2 && stack[ply-1].best_move >=0 && stack[ply-2].best_move >=0) ?
639 PLUTO2(stack[ply-2].moves[stack[ply-2].best_move], stack[ply-1].moves[stack[ply-1].best_move]) : -1;
641 if(s->under_check==2) /* double check */
643 depth += 2*PLY;
644 extended = true;
646 else if(s->under_check) /* simple check */
648 depth += PLY;
649 if(stack[ply-1].curr_move>=0 &&
650 !board.pins[stack[ply-1].moves[ /* last moved piece is not attacking the king */
651 stack[ply-1].curr_move].to]) /* so this is a discover check */
653 depth += PLY/2;
655 extended = true;
658 /* return now if the positon is terminal */
659 if(!s->num_moves)
661 if(s->under_check)
663 /* while mating, sacrify as much as possible :) */
664 int mt = IS_WHITE(board.other_color) ? 5 : -1;
665 int16_t matval = Board::simple_values[PAWN]*board.mat_tracking[mt+PAWN].count +
666 Board::simple_values[KNIGHT]*board.mat_tracking[mt+KNIGHT].count +
667 Board::simple_values[BISHOP]*board.mat_tracking[mt+BISHOP].count +
668 Board::simple_values[QUEEN]*board.mat_tracking[mt+QUEEN].count;
669 best = alpha = beta = -INF + ply*100 + matval;
671 else
672 best = alpha = beta = 0;
673 goto search_done;
676 /* single-reply extension */
677 if(s->num_moves == 1 && !extended)
679 depth += PLY;
680 extended = true;
682 else if(s->num_moves <= 3 && !extended)
684 depth += PLY/2;
685 extended = true;
688 /*******************************************************************************
689 Sort the moves.
690 First comes the move from the hashtable, if avalable.
691 The remaining moves are sorted with a heuristic that keeps in account
692 the history heuristic (ie the moves that previously caused an alpha
693 cutoff), a MVV/LVA bonus value and a small bonus for moves that go
694 toward the center.
695 *******************************************************************************/
697 /* convert the move we got from the hash to the move structure */
698 if(cbest_mv_hash)
700 best_mv_hash = board.uncompress_move(cbest_mv_hash);
701 /* if it happened that the move we got from the hashtable
702 is not valid, simply no move will get the high
703 heuristic bonus value */
705 #if INTERNAL_ITERATIVE_DEEPENING
706 else if(base_depth>3*PLY && pv) /* don't do it only on the pv, or it will be useless :) */
708 int val;
709 s->curr_move = -2;
710 GUI1(notify(&board, Move::None(), ply, base_depth-2*PLY, -INF, alpha, beta, SearchGui::Blue ));
711 val = search(ply+1, base_depth-2*PLY, true, alpha, beta, expect_allbad);
712 GUI2(notify_value(ply, val, DIFF_NODES, 0));
714 HashEntry *h2 = probe_hash( board.hash );
715 if(h2 && h2->best_mv)
717 cbest_mv_hash = h2->best_mv;
718 best_mv_hash = board.uncompress_move(cbest_mv_hash);
721 #endif //INTERNAL_ITERATIVE_DEEPENING
723 /* for each move calculate the heuristic goodness value */
724 #if DEBUG && TRACK_ATTACKS
725 board.check_attacks();
726 #endif
728 Move *prev = (stack[ply-1].curr_move>=0) ? &stack[ply-1].moves[stack[ply-1].curr_move] : NULL;
729 if(quiesce)
730 moves_quiescence_heuristic( s->moves, s->num_moves, best_mv_hash,
731 best, alpha, beta, base_depth, prev);
732 else
734 int overall_ext = 0;
735 moves_heuristic( s->moves, s->num_moves, pv, ply, base_depth,
736 best_mv_hash, quiesce, prev, &overall_ext, pluto1, pluto2 );
737 depth += overall_ext;
740 #if DEBUG && TRACK_ATTACKS
741 board.check_attacks();
742 #endif
744 /* if quiesce rip-off the "non-critical" moves */
745 if(quiesce)
747 int n = 0;
748 for(int i=0;i<s->num_moves;i++)
749 if(s->moves[i].val>0)
750 s->moves[n++] = s->moves[i];
751 mv_stack_top -= s->num_moves-n;
752 s->num_moves = n;
753 if(!n)
754 no_good_moves = true;
757 /* Don't do it now, do it incrementally */
758 //sort_moves( s->moves, s->num_moves );
761 #if EARLY_TRANSP_CUTOFF
762 /*******************************************************************************
763 Try to get an early beta cutoff using the hash table values
764 of the following moves, and improve alpha too.
765 Try on the first 6 value of the ordered moves (argh, looking into the
766 hashtable is very expensive because of the cache!!!!!!!!)
767 *******************************************************************************/
769 if(depth >= 3*PLY)
771 HashKey hk = board.move_hash(s->moves[0]);
772 for(int i=1;i<s->num_moves;i++)
774 engine->prefetch_hash(hk);
775 HashKey newhk = board.move_hash(s->moves[i]);
776 HashEntry *h2 = engine->probe_hash( hk );
777 hk = newhk;
779 if(h2 && h2->depth >= depth-PLY)
781 if(-h2->up >= beta)
783 best = -h2->up;
784 goto search_done;
786 alpha = MAX(alpha, (int16_t)-h2->up);
790 HashEntry *h2 = engine->probe_hash( hk );
791 if(h2 && h2->depth >= depth-PLY)
793 if(-h2->up >= beta)
795 best = -h2->up;
796 goto search_done;
798 alpha = MAX(alpha, (int16_t)-h2->up);
801 #endif //EARLY_TRANSP_CUTOFF
803 /*******************************************************************************
804 It is now time to loop all the successor moves and search recursively.
805 *******************************************************************************/
807 #if FUTILITY
808 /* calcluate the evaluation (required by fitility pruning) */
809 position_val = quiesce ? best : board.dummy_evaluate(); //evaluate( st_computer_color, -INF, INF);
810 #endif
812 for(int i=0;i<s->num_moves;i++)
814 int16_t val;
815 int sdepth = depth-100;
817 /* sort moves incrementally, in the hope of a beta cut */
818 engine->incremental_sort_moves(s->moves+i, s->num_moves-i);
820 /* extensions calculated during the heuristic sort */
821 sdepth += s->moves[i].extend;
823 #if FUTILITY
824 /* futility pruning, it is done only if we are not under check
825 and the move is not a "critical" move */
826 if(depth>0 && depth<=2*PLY && !s->under_check && s->moves[i].val < 28000)
828 static const int mavala[] = { 0, 500, 325, 975, 325, 100, 0 };
830 int16_t fmargin = (depth <= PLY ? 420 : 720);
831 int16_t fval = position_val + mavala[PIECE_OF(s->moves[i].capture)];
832 if(fval < alpha-fmargin)
833 continue;
835 #endif
837 if(!expect_allbad)
839 int mx = CAPT_INDEX(s->moves[i]);
841 /* collect history statistics */
842 if(!s->moves[i].capture &&
843 !(s->moves[i].flags>=PROMOTE_FIRST) )
845 int ix = HISTORY_INDEX(s->moves[i]);
846 if(history_tot[ix] > 1024)
848 history_tot[ix] = history_tot[ix]*7/8;
849 history_hit[ix] = history_hit[ix]*7/8;
851 history_tot[ix] += quiesce ? 8 : 16;
854 if(pluto1!=-1 && !s->moves[i].capture &&
855 !(s->moves[i].flags>=PROMOTE_FIRST))
857 int ix = FAST_PLUTO(pluto1, mx);
858 if(pluto_tot[ix] > 256)
860 pluto_tot[ix] = pluto_tot[ix]*7/8;
861 pluto_hit[ix] = pluto_hit[ix]*7/8;
863 pluto_tot[ix] += quiesce ? 8 : 16;
866 if(pluto2!=-1 && !s->moves[i].capture &&
867 !(s->moves[i].flags>=PROMOTE_FIRST))
869 int ix = FAST_PLUTO(pluto2, mx);
870 if(pluto2_tot[ix] > 128)
872 pluto2_tot[ix] = pluto2_tot[ix]*7/8;
873 pluto2_hit[ix] = pluto2_hit[ix]*7/8;
875 pluto2_tot[ix] += quiesce ? 8 : 16;
879 //Set data for pawn-strike
880 #if PAWN_STRIKE
881 if(!quiesce && PIECE_OF(board.data[s->moves[i].from]) == PAWN && s->moves[i].val == 27000) //FIXME, UGLY
883 stack[ply].escape_from[1] = s->moves[i].to + Board::up_dir[IS_WHITE(board.color_to_move)] + RIGHT;
884 if(OUT_OF_BOARD(stack[ply].escape_from[1]) || !IS_OF_COLOR(board.data[stack[ply].escape_from[1]], board.other_color)
885 || PIECE_OF(board.data[stack[ply].escape_from[1]])==PAWN)
886 stack[ply].escape_from[1] = INVALID;
887 stack[ply].escape_from[2] = s->moves[i].to + Board::up_dir[IS_WHITE(board.color_to_move)] + LEFT;
888 if(OUT_OF_BOARD(stack[ply].escape_from[2]) || !IS_OF_COLOR(board.data[stack[ply].escape_from[2]], board.other_color)
889 || PIECE_OF(board.data[stack[ply].escape_from[2]])==PAWN)
890 stack[ply].escape_from[2] = INVALID;
892 else
894 stack[ply].escape_from[1] = stack[ply].escape_from[2] = INVALID;
896 #endif //PAWN_STRIKE
897 s->curr_move = i;
898 board.do_move(s->moves[i], s->save_buf);
901 #if 0
902 uint64_t q;
903 if(base_depth > 0 && sdepth <= 0)
905 STAT(quiesce_called);
906 q = stat_quiesce_nodes;
908 #endif
910 #if NEGA_SCOUT /* use negascout, (null window search for nodes that are not in the pv) */
911 if(i == 0 && !expect_allbad)
913 if(depth>=0 && pv && mv_pv_top>=ply)
915 mv_pv[ply] = s->moves[i];
916 mv_pv_top = ply + 1;
918 #endif
919 GUI1(notify(&board, s->moves[i], ply, sdepth, s->moves[i].val, alpha, beta, SearchGui::Bold ));
920 val = -search( ply+1, sdepth, pv, -beta, -alpha, !pv);
921 GUI2(notify_value(ply, val, DIFF_NODES, 0));
922 #if NEGA_SCOUT
924 else
926 bool red_fuck = false;
927 #if LATE_MOVE_REDUCTION
928 /* history pruning, if this is not a "critical" move and the failhigh
929 stats are too low, try a reduced depth search (if it returns >alpha,
930 re-do the full depth search) */
931 // if((sdepth>0) && !s->under_check && depth<=3*PLY && s->moves[i].val<28000 &&
932 // (history_hit[HISTORY_INDEX(s->moves[i])]+1)*3
933 // < (history_tot[HISTORY_INDEX(s->moves[i])]+1))
934 if( (sdepth>0) && !s->under_check && !s->null_threat_dangerous
935 && s->moves[i].val<28000 && (s->moves[i].val<(sdepth<=PLY ? 650 : 450)) )
937 int rdepth = depth - 2*PLY;
938 //sdepth - PLY; //(s->moves[i].val<250 ? 2*PLY : PLY) - s->moves[i].extend;
939 GUI1(notify(&board, s->moves[i], ply, rdepth, s->moves[i].val, alpha, alpha+1, SearchGui::Italic|SearchGui::Gray ));
940 val = -search( ply+1, rdepth, false, -alpha-1, -alpha, false );
941 GUI2(notify_value(ply, val, DIFF_NODES, val>alpha ? SearchGui::Red : 0));
942 if(val <= alpha)
943 goto skip_search; /* reduced search was effective */
944 red_fuck = true;
946 #endif
947 GUI1(notify(&board, s->moves[i], ply, sdepth, s->moves[i].val, alpha, alpha+1, 0 ));
948 val = -search( ply+1, sdepth, false, -alpha-1, -alpha, red_fuck);
949 GUI2(notify_value(ply, val, DIFF_NODES, val>alpha ? SearchGui::Red : 0));
951 if((val>alpha) && pv)
953 if(depth>=0 && mv_pv_top>=ply)
955 mv_pv[ply] = s->moves[i];
956 mv_pv_top = ply + 1;
959 GUI1(notify(&board, s->moves[i], ply, sdepth, -INF, alpha, beta, SearchGui::Bold ));
960 val = -search( ply+1, sdepth, true, -beta, -alpha, false );
961 GUI2(notify_value(ply, val, DIFF_NODES, val>beta ? SearchGui::Red : 0));
964 #endif
966 #if 0
967 if(base_depth > 0 && sdepth <= 0)
969 q = stat_quiesce_nodes-q;
970 if(q > stat_max_quiesce_nodes)
972 stat_max_quiesce_nodes = q;
973 max_quiesce_board = board;
976 #endif
978 #if NEGA_SCOUT && LATE_MOVE_REDUCTION
979 skip_search:
980 #endif
981 board.undo_move(s->moves[i], s->save_buf);
983 /* update the current best value and check for and alpha cut */
984 if(val > best)
986 best = val;
988 best_mv_found = i;
989 s->best_move = i;
992 if(best > alpha)
994 /* alpha improvement! */
995 alpha = best;
998 /* beta cut! */
999 if(best >= beta)
1000 break;
1003 if(!expect_allbad || best >= beta)
1005 /* collect statistics for the history */
1006 if(/*best >= beta &&*/ (best_mv_found!=-1) &&
1007 !s->moves[best_mv_found].capture &&
1008 !(s->moves[best_mv_found].flags>=PROMOTE_FIRST) )
1009 history_hit[HISTORY_INDEX(s->moves[best_mv_found])] += quiesce ? 8 : 16;
1011 int mx = CAPT_INDEX(s->moves[best_mv_found]);
1012 if(pluto1!=-1 && !s->moves[best_mv_found].capture &&
1013 !(s->moves[best_mv_found].flags>=PROMOTE_FIRST))
1015 int ix = FAST_PLUTO(pluto1, mx);
1016 pluto_hit[ix] += quiesce ? 8 : 16;
1019 if(pluto2!=-1 && !s->moves[best_mv_found].capture &&
1020 !(s->moves[best_mv_found].flags>=PROMOTE_FIRST))
1022 int ix = FAST_PLUTO(pluto2, mx);
1023 pluto2_hit[ix] += quiesce ? 8 : 16;
1027 search_done:
1028 mv_stack_top -= s->num_moves; /* free the moves we allocated */
1030 /* this is a null move, save what the threat is */
1031 if(stack[ply-1].curr_move == -1 && best_mv_found >= 0)
1032 stack[ply-1].null_threat = s->moves[best_mv_found];
1034 /* if we found a best move searching, that move will be saved.
1035 if we did no search (ie quiescence), save the old hash value,
1036 or -1 if no hash entry had been found */
1037 int bestmv = cbest_mv_hash;
1038 if(best_mv_found >= 0)
1039 bestmv = board.compress_move(s->moves[best_mv_found]);
1041 /* write the value in the hash, with the index of the best move */
1042 engine->write_hash( board.hash,
1043 best > orig_alpha ? MIN(best, beta) : lower_bound,
1044 best < beta ? best : +INF,
1045 MAX(base_depth,-500), bestmv, no_good_moves);
1046 GUI(notify_hash(ply, best > orig_alpha ? MIN(best, beta) : lower_bound, best < beta ? best : +INF,
1047 MAX(base_depth,-500), alpha, beta, bestmv ? board.uncompress_move(bestmv) : Move::None(), true,
1048 SearchGui::Bold | SearchGui::Magenta) );
1050 return best;
1053 uint16_t SearchRoot::pluto_tot[PLUTO_SIZE];
1054 uint16_t SearchRoot::pluto_hit[PLUTO_SIZE];
1055 uint16_t SearchRoot::pluto2_tot[PLUTO_SIZE];
1056 uint16_t SearchRoot::pluto2_hit[PLUTO_SIZE];
1057 uint16_t SearchRoot::history_tot[HISTORY_SIZE];
1058 uint16_t SearchRoot::history_hit[HISTORY_SIZE];
1060 SearchRoot::SearchRoot(Engine* e, const Board& b)
1061 : mv_stack_top(0)
1062 , mv_pv_top(0)
1063 , search_gui(e->search_gui)
1064 , engine(e)
1065 , board(b)
1067 engine->current_root = this;
1070 SearchRoot::~SearchRoot()
1072 engine->current_root = NULL;
1075 Move Engine::find_best_move()
1077 ASSERT(!thinking);
1078 IFGUI(Engine *engine = this);
1079 int num_mate_hits = 0;
1080 SearchRoot root(this, board);
1081 SearchRoot::SearchStack *s = &root.stack[0];
1083 /* initialize the root node */
1084 root.base_depth = PLY;
1085 s->curr_move = -1;
1086 s->best_move = 0;
1087 s->null_threat = Move::FromInt(0);
1088 s->null_threat_dangerous = false;
1089 s->moves = root.mv_stack;
1090 s->num_moves = root.mv_stack_top = root.board.find_moves(s->moves);
1091 #if PAWN_STRIKE
1092 s->escape_from[1] = s->escape_from[2] = INVALID;
1093 #endif //PAWN_STRIKE
1095 ASSERT(s->moves);
1097 /* calculate how much time we will think*/
1098 start_think_time = current_time();
1099 if(analysis_limit == TIME_LIMIT)
1101 if(board.color_to_move == st_computer_color)
1102 calc_best_time();
1103 else /* pondering? analysing? */
1104 time_best_csec = 99999999;
1105 max_think_time = start_think_time + time_best_csec - 2;
1108 /* to print the analysis */
1109 if(post)
1110 output("\tply\tscore\ttime\tnodes\tpv\n");
1112 /* return immediatly if the move is forced. */
1113 if(s->num_moves==1)
1115 if(post)
1116 output("\t0\t0\t0\t0\t%s (only move)\n", board.move_to_alg(MoveStr().data(), s->moves[0]));
1117 return s->moves[0];
1120 /* probe the play lines */
1121 if(eng_status == PLAYING && st_computer_color == board.color_to_move)
1123 Move retv = probe_lines(&root.board);
1125 if(retv.valid())
1127 retv.val = 0;
1128 output("\t0\t0\t0\t0\t%s (selected line)\n", board.move_to_alg(MoveStr().data(), retv));
1129 return retv;
1133 /* probe the book */
1134 Move bookmove = probe_book(&root.board);
1135 if(bookmove.valid())
1137 bookmove.val = 0;
1138 for(int i=0;i<s->num_moves++;i++)
1139 if(bookmove == s->moves[i])
1140 return bookmove;
1141 output("Error!!! invalid move in the book!!!\n");
1144 reset_stats();
1145 reset_hash(); //Try to be deterministic
1146 processed_nodes = 0;
1147 thinking = true;
1148 make_old_hash();
1150 memset( root.history_tot, 0, HISTORY_SIZE*sizeof(uint16_t));
1151 memset( root.history_hit, 0, HISTORY_SIZE*sizeof(uint16_t));
1152 memset( root.pluto_tot, 0, PLUTO_SIZE*sizeof(uint16_t));
1153 memset( root.pluto_hit, 0, PLUTO_SIZE*sizeof(uint16_t));
1154 memset( root.pluto2_tot, 0, PLUTO_SIZE*sizeof(uint16_t));
1155 memset( root.pluto2_hit, 0, PLUTO_SIZE*sizeof(uint16_t));
1157 GUI(init_root());
1160 /* set the back jump for the quick thinking exit */
1161 if(setjmp(back))
1162 goto exit_thinking;
1165 /* do the iterative deepening thing. */
1166 while(1)
1168 int16_t alpha = num_mate_hits ? -INF : -WORST_MATE;
1169 int16_t beta = num_mate_hits ? INF : WORST_MATE;
1171 GUI(new_root_level(root.base_depth));
1173 /* for each move call the alpha-beta search function */
1174 for(int i=0;i<s->num_moves;i++)
1176 s->curr_move = i;
1177 root.board.do_move(s->moves[i], s->save_buf);
1178 #if NEGA_SCOUT
1179 if(i == 0)
1181 root.mv_pv[0] = s->moves[i];
1182 root.mv_pv_top = 1;
1183 #endif
1184 GUI1(notify(&root.board, s->moves[i], 0, root.base_depth-PLY, s->moves[i].val, alpha, beta, SearchGui::Bold));
1185 s->moves[i].val = -root.search( 1, root.base_depth-PLY, true, -beta, -alpha, false );
1186 GUI2(notify_value(0, s->moves[i].val, DIFF_NODES, 0));
1187 #if NEGA_SCOUT
1189 else
1191 GUI1(notify(&root.board, s->moves[i], 0, root.base_depth-PLY, s->moves[i].val, alpha, alpha+1, 0 ));
1192 s->moves[i].val = -root.search( 1, root.base_depth-PLY, false, -alpha-1, -alpha, false );
1193 GUI2(notify_value(0, s->moves[i].val, DIFF_NODES, s->moves[i].val>alpha ? SearchGui::Red : 0));
1195 if(s->moves[i].val > alpha)
1197 root.mv_pv[0] = s->moves[i];
1198 root.mv_pv_top = 1;
1200 GUI1(notify(&root.board, s->moves[i], 0, root.base_depth-PLY, -INF, alpha, beta, SearchGui::Bold));
1201 s->moves[i].val = -root.search( 1, root.base_depth-PLY, true, -beta, -alpha, false );
1202 GUI2(notify_value(0, s->moves[i].val, DIFF_NODES, 0));
1205 #endif
1206 root.board.undo_move(s->moves[i], s->save_buf);
1208 /* cut alpha */
1209 if(s->moves[i].val > alpha)
1211 alpha = s->moves[i].val;
1213 Move tmp = s->moves[i];
1214 for(int q=i-1;q>=0;q--)
1215 s->moves[q+1] = s->moves[q];
1216 s->moves[0] = tmp;
1218 /* this move caused an alpha cut, so print the new line */
1219 if( post /*&& processed_nodes>100000*/)
1221 output("\t%d\t%d\t%d\t%llu\t",
1222 root.base_depth/PLY,
1223 s->moves[0].val,
1224 current_time() - start_think_time,
1225 (unsigned long long)processed_nodes);
1226 print_moves(root.mv_pv, 1/*root.mv_pv_top*/, true, true);
1231 /* print the result of the analysis at this depth */
1232 if( post /*&& processed_nodes>100000*/)
1234 output("\t%d\t%d\t%d\t%llu\t",
1235 root.base_depth/PLY,
1236 s->moves[0].val,
1237 current_time() - start_think_time,
1238 (unsigned long long)processed_nodes);
1239 print_moves(root.mv_pv, 1/*root.mv_pv_top*/, true, true);
1242 /* max depth */
1243 if( root.base_depth >= 50*PLY )
1244 break;
1246 /* return in case of fixed depth search */
1247 if( eng_status == PLAYING && st_computer_color == board.color_to_move &&
1248 analysis_limit == DEPTH_LIMIT && root.base_depth == st_depth*PLY)
1249 break;
1251 /* return if 3/5 of time is gone (we can't search another ply anyway) */
1252 if( eng_status == PLAYING && st_computer_color == board.color_to_move &&
1253 analysis_limit == TIME_LIMIT &&
1254 (current_time()-start_think_time) >= (time_best_csec*3/5) )
1255 break;
1257 /* if a checkmate was detected return immediately */
1258 if( ABS(alpha) > WORST_MATE)
1260 num_mate_hits++;
1261 if(num_mate_hits >= 5)
1262 break;
1265 root.base_depth += PLY;
1268 exit_thinking:
1270 if(post)
1272 print_stats();
1273 output("#max quiesce board: %s [%d %d]\n", max_quiesce_board.to_fen(BigStr().data()),
1274 max_quiesce_alpha, max_quiesce_beta);
1277 thinking = false;
1278 return s->moves[0];