Fixed quiescence-futility pruning. Code changes because of tests.
[rattatechess.git] / search.cpp
blobd936abc3dcf5f6c04dbcb234a42a64f58e72e8ae
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 /* improve the sorting heuristic for moves to the center */
105 #define CENTER_HEURISTIC 0
108 #define IGNORE_ALLBAD_NODES 0
111 void Engine::print_stat()
113 #if 0
114 if(eng_status != ANALYZING)
116 printf("Thinking: ");
117 for(int i=0; i<current_ply; i++)
119 if(stack[i].curr_move == -2)
120 continue; //Internal iterative deepening
121 else if(stack[i].curr_move == -1)
123 printf("<NULL>");
124 board.do_null_move();
126 else
128 char buf[32];
129 printf("%s", move_to_alg(buf, &(stack[i].moves[stack[i].curr_move]) ) );
130 board.do_move(stack[i].moves[stack[i].curr_move]);
132 printf((i != current_ply-1) ? " " : "\n");
134 rewind_thinking();
135 return;
138 if(thinking)
140 char buf[32];
141 output("stat01: %d %llu %d %d %d %s\n",
142 current_time() - start_think_time,
143 (unsigned long long)processed_nodes,
144 stack[0].base_depth/100,
145 stack[0].num_moves-1-stack[0].curr_move,
146 stack[0].num_moves,
147 move_to_alg(buf, &(stack[0].moves[stack[0].curr_move]) ) );
149 else
150 output("stat01: 0 0 0 0 0\n");
151 #endif
154 bool SearchRoot::null_move_ok()
156 int c = IS_WHITE(board.color_to_move) ? 5 : -1;
157 int numpawns = board.mat_tracking[c+PAWN].count;
158 int numpieces = board.mat_tracking[c+KNIGHT].count + board.mat_tracking[c+BISHOP].count
159 + board.mat_tracking[c+ROOK].count + board.mat_tracking[c+QUEEN].count;
160 if(numpieces >= 2)
161 return true;
162 if(numpieces >= 1 && numpawns >= 2)
163 return true;
164 return false;
167 void
168 SearchRoot::moves_heuristic(Move *mv, int num_mv, bool pv, int ply, int orig_depth,
169 Move best_mv_hash, bool quiesce, Move* prev, int* overall_extensions,
170 int p1, int p2)
172 #if PAWN_STRIKE
173 int escapes[2];
174 int num_escapes = 0;
175 #endif //PAWN_STRIKE
176 int hash_move = -1;
178 for(int i=0;i<num_mv;i++)
180 mv[i].val = 0;
181 mv[i].extend = 0;
183 /* give a high bonus to the move stored in the hash, if any.
184 mark only which is, don't continue, because some extensions
185 may be triggered ad added later (ie pawn strike, etc) */
186 if(mv[i].as_int == best_mv_hash.as_int)
187 hash_move = i;
189 #if PAWN_STRIKE
190 if(num_escapes<=2 && PIECE_OF(board.data[mv[i].from]) != PAWN &&
191 (mv[i].from == stack[ply-1].escape_from[1] || stack[ply-1].escape_from[2]) )
193 int x = board.move_see_val(mv[i]);
195 if(x >= 0)
197 if(num_escapes < 2)
198 escapes[num_escapes] = i;
199 num_escapes++;
202 #endif //PAWN_STRIKE
204 /* process strong pawn moves */
205 if(PIECE_OF(board.data[mv[i].from])==PAWN) /* pawn strike */
207 uint8_t x = X(mv[i].to);
208 uint8_t y = IS_WHITE(board.color_to_move) ? Y(mv[i].to) : 7-Y(mv[i].to);
210 if( mv[i].flags == PROMOTEQUEEN || y == 6)
212 int x = board.move_see_val(mv[i]);
214 if(x>=0)
216 mv[i].val += mv[i].flags ? 29600 : 29599; /* promote! */
217 mv[i].extend = PLY; /* extend search */
218 continue;
222 if(y >= 4)
224 int other = IS_WHITE(board.other_color);
225 uint8_t pos = 7-y;
226 if((!board.line_pawns[other][x].count || board.line_pawns[other][x].pos[0] > pos)
227 && (x==0 || !board.line_pawns[other][x-1].count || board.line_pawns[other][x-1].pos[0] >= pos)
228 && (x==7 || !board.line_pawns[other][x+1].count || board.line_pawns[other][x+1].pos[0] >= pos))
230 int x = board.move_see_val(mv[i]);
232 if(x>=0)
234 mv[i].val += y >= 5 ? 29550 : 29500; /* passed pawn advancing! */
235 if(y >= 5)
236 mv[i].extend = PLY; /* extend search */
237 continue;
242 /* pawn attack */
243 if(orig_depth >= 2*PLY)
245 /* pawn strike */
246 uint8_t up_right = Board::up_dir[IS_WHITE(board.color_to_move)] + RIGHT;
247 uint8_t up_left = Board::up_dir[IS_WHITE(board.color_to_move)] + LEFT;
248 uint8_t p1 = mv[i].to + up_right;
249 uint8_t p2 = mv[i].to + up_left;
250 uint8_t a = OUT_OF_BOARD(p1) ? 0 : board.data[p1];
251 uint8_t b = OUT_OF_BOARD(p2) ? 0 : board.data[p2];
252 if( (COLOR_OF(a)==board.other_color && PIECE_OF(a)!=PAWN)
253 || (COLOR_OF(b)==board.other_color && PIECE_OF(b)!=PAWN) )
255 int x = board.move_see_val(mv[i]);
257 if(x>=0)
259 mv[i].val = 27000; /* pawn strike! */
260 continue;
266 if(mv[i].capture)
268 int x = board.move_see_val(mv[i]);
270 /* recapture? */
271 if(prev && prev->capture &&
272 (mv[i].to == prev->to) && (x >= Board::simple_values[PIECE_OF(prev->capture)]) )
274 mv[i].val = 29900;
275 if( (x == Board::simple_values[PIECE_OF(prev->capture)]) )
276 mv[i].extend = PLY;
277 continue;
280 if(x>0)
282 if(orig_depth>-7*PLY && board.move_is_check(mv[i]) )
283 mv[i].val = 29800+x;
284 else
285 mv[i].val = 29000+x;
286 continue;
288 else if(x>=0 && orig_depth>-7*PLY && board.move_is_check(mv[i]) )
290 /* = capture but check */
291 mv[i].val = 29800;
292 continue;
295 else /* add a bonus for checks (but not too deep, or quiescence will get mad) */
296 if(board.move_is_check(mv[i]) )
298 if(board.move_see_val(mv[i])>=0)
299 mv[i].val = 28799;
300 else
301 mv[i].val = 28700;
302 continue;
305 /* null-move threat */
306 if(stack[ply-1].null_threat == mv[i])
308 mv[i].val = 28500;
309 continue;
312 #if 1
313 mv[i].val += (history_hit[HISTORY_INDEX(mv[i])] + 32) * (1024/3) / (history_tot[HISTORY_INDEX(mv[i])] + 64);
315 int mx = CAPT_INDEX(mv[i]);
316 if(p1 != -1)
318 int ix = FAST_PLUTO(p1, mx);
319 mv[i].val += (pluto_hit[ix] + 24) * (1024/3) / (pluto_tot[ix] + 48);
321 else
322 mv[i].val += 512/3;
323 if(p2 != -1)
325 int ix = FAST_PLUTO(p2, mx);
326 mv[i].val += (pluto2_hit[ix] + 16) * (1024/3) / (pluto2_tot[ix] + 32);
328 else
329 mv[i].val += 512/3;
330 #else
331 mv[i].val += (history_hit[HISTORY_INDEX(mv[i])] + 32) * 256*4 / (history_tot[HISTORY_INDEX(mv[i])] + 64);
332 #endif
334 #if CENTER_HEURISTIC
335 static int bof[128] = {
336 0,0,1,2,2,1,0,0,ENDL,
337 0,1,2,3,3,2,1,0,ENDL,
338 0,2,4,5,5,4,2,0,ENDL,
339 1,2,5,6,6,5,2,1,ENDL,
340 1,2,5,6,6,5,2,1,ENDL,
341 0,2,4,5,5,4,2,0,ENDL,
342 0,1,2,3,3,2,1,0,ENDL,
343 0,0,1,2,2,1,0,0,ENDL
346 /* add a bonus for moves towards the center */
347 mv[i].val += bof[mv[i].to];
348 //mv[i].val += (bof[mv[i].to] - bof[mv[i].from]);
349 #endif //CENTER_HEURISTIC
352 #if PAWN_STRIKE
353 if((stack[ply-1].escape_from[1] != INVALID || stack[ply-1].escape_from[2] != INVALID) && num_escapes <= 2)
355 for(int i=0;i<num_escapes;i++)
356 mv[escapes[i]].extend = PLY;
358 #endif //PAWN_STRIKE
360 if(hash_move!=-1)
361 mv[hash_move].val = 30000;
365 void
366 SearchRoot::moves_quiescence_heuristic(Move *mv, int num_mv, const Move& best_mv_hash,
367 int static_eval, int alpha, int beta, int orig_depth, Move* prev)
369 int hash_move = -1;
370 for(int i=0;i<num_mv;i++)
372 mv[i].val = -10000;
373 mv[i].extend = 0;
375 /* give a high bonus to the move stored in the hash, if any.
376 mark only which is, don't continue, because some extensions
377 may be triggered ad added later (ie pawn strike, etc) */
378 if(mv[i].as_int == best_mv_hash.as_int)
379 hash_move = i;
381 /* process strong pawn moves */
382 if(PIECE_OF(board.data[mv[i].from])==PAWN)
384 if( ROW_OF(mv[i].to) == board.seventh_rank[IS_WHITE(board.color_to_move)] )
386 int x = board.move_see_val(mv[i]);
388 if(x>=0)
390 mv[i].val += 29499; /* 7th push */
391 mv[i].extend = PLY; /* extend search */
392 continue;
396 if( mv[i].flags == PROMOTEQUEEN )
398 int x = board.move_see_val(mv[i]);
400 if(x<0)
402 mv[i].val += 29500; /* promote! */
403 mv[i].extend = PLY; /* extend search */
404 continue;
409 if(mv[i].capture)
411 int x = board.move_see_val(mv[i]);
413 if(prev && prev->capture &&
414 (mv[i].to == prev->to) && (x >= Board::simple_values[PIECE_OF(prev->capture)]) )
416 mv[i].val = 29900 + x;
417 if( (x == Board::simple_values[PIECE_OF(prev->capture)]) )
418 mv[i].extend = PLY;
419 continue;
422 if( x>0 && ((orig_depth>-2*PLY || static_eval==-INF) ? true : x*100+static_eval+200>alpha) )
424 mv[i].val = 10000+x;
425 continue;
427 else if((x>=0 && orig_depth>-4*PLY) && board.move_is_check(mv[i]) )
429 /* = capture but check */
430 mv[i].val = 8000;
431 continue;
434 else /* add a bonus for checks (but not too deep, or quiescence will get mad) */
435 if(orig_depth>-2*PLY && board.move_is_check(mv[i]) && board.move_see_val(mv[i])>=0)
437 mv[i].val = 5000;
438 continue;
442 if(hash_move >= 0 && mv[hash_move].val > 9000)
443 mv[hash_move].val = 30000;
447 /*******************************************************************************
448 The main alpha-beta recursive search function.
449 It handles both normal search (with or without null move)
450 and quiescence search, because i have having 2 almost identical
451 function around.
452 *******************************************************************************/
453 int16_t SearchRoot::search(int ply, int base_depth, bool pv, int16_t orig_alpha, int16_t beta, bool expect_allbad)
455 SearchStack *s = &stack[ply];
456 int16_t best = -INF;
457 uint16_t cbest_mv_hash = 0; /* the compressed move from the hash */
458 Move best_mv_hash = Move::FromInt(0); /* the move from the hash */
459 int best_mv_found = -1; /* the index of the best move AFTER searching */
460 bool quiesce;
461 bool extended = false;
462 bool no_good_moves = false;
463 int16_t lower_bound = -INF;
464 int16_t position_val;
465 int depth = base_depth;
466 int16_t alpha = orig_alpha;
467 int pluto1, pluto2;
469 #if IGNORE_ALLBAD_NODES
470 expect_allbad = false;
471 #endif
473 #if 0
474 if(ply >= 75)
476 for(int i=ply-1;i>=0;i--)
478 if(stack[i].curr_move == -1)
479 board.undo_null_move(stack[i].save_buf);
480 else if(stack[i].curr_move >= 0)
481 board.undo_move(stack[i].moves[stack[i].curr_move], stack[i].save_buf);
483 printf("[FEN \"%s\"]\n", board.to_fen(BigStr().data()) );
484 for(int i=0;i<ply;i++)
486 printf(" %s", stack[i].curr_move <= -1 ? "NULL" :
487 board.move_to_alg(TmpStr().data(), stack[i].moves[stack[i].curr_move]));
488 if(stack[i].curr_move == -1)
489 board.do_null_move(stack[i].save_buf);
490 else if(stack[i].curr_move >= 0)
491 board.do_move(stack[i].moves[stack[i].curr_move], stack[i].save_buf);
493 printf(" *\n");
494 exit(0);
496 #endif
498 // if(depth <= 0)
499 // STAT(quiesce_nodes)
501 s->num_moves = 0;
502 s->curr_move = -3;
503 s->best_move = -1;
504 s->null_threat = Move::FromInt(0);
505 s->null_threat_dangerous = false;
506 #if PAWN_STRIKE
507 stack[ply].escape_from[1] = stack[ply].escape_from[2] = INVALID;
508 #endif //PAWN_STRIKE
510 /* check if time is running out */
511 engine->check_time();
513 /* check for a draw for repetition or 50mvs. Of course the draw for
514 repetition must be checked BEFORE probing the hash :)*/
515 if(check_draw(ply))
516 return (board.color_to_move == engine->st_computer_color) ? engine->v_eval_draw :
517 ((board.other_color == engine->st_computer_color) ? -engine->v_eval_draw : 0); /* be aggressive! */
519 /*******************************************************************************
520 Probe the hashtable.
521 If the probe is succesful the hashtable will return us value
522 that can be exact, a lower bound or an upper bound, and if the
523 depth of the hashed search is >= the current depth this value
524 will be used to improve alpha and beta and possibly return immediatly.
525 The hastable will also give us a "best" move that will be searched
526 first.
527 This is the move that caused the "final" cutoff when this position
528 was searched previously. This best move is actually the index of the best
529 move in the array of generated moves (it is supposed to be deterministic :)
530 *******************************************************************************/
532 HashEntry *h = engine->probe_hash( board.hash );
534 if(h){
535 GUI(notify_hash(ply, h->lo, h->up, h->depth, alpha, beta,
536 h->best_mv ? board.uncompress_move(h->best_mv) : Move::None(), false,
537 (((h->depth >= base_depth) && (h->lo>=beta || h->up==h->lo || h->up<=alpha))
538 ? SearchGui::Bold : 0) | SearchGui::Magenta) );
541 if(h && (h->depth >= base_depth))// || ABS(h->value)>INF-1000) )
543 int16_t l = h->lower();
544 int16_t u = h->upper();
546 if(l>=beta || u==l)
547 return l;
548 if(u<=alpha)
549 return u;
551 beta = MIN(beta, u);
552 best = alpha = MAX(alpha, l);
555 if(h)
556 cbest_mv_hash = h->best_mv;
558 /*******************************************************************************
559 Test if we are under check, and if so extend search
560 *******************************************************************************/
562 s->under_check = board.under_attack(board.king_pos[IS_WHITE(board.color_to_move)],
563 board.other_color);
565 /*******************************************************************************
566 If it is time to quiesce, evaluate and test if we can exit
567 immediately with a beta cut-off (try first a rough eval - delta)
568 *******************************************************************************/
569 quiesce = ((!s->under_check) && (depth<=0)) || (ply > 70);
570 if(quiesce)
571 STAT(quiesce_nodes);
573 #if 0 //PAPOPEPO
574 if(quiesce && depth>=-PLY)
576 int num_mv;
577 Move *mv = mv_stack + mv_stack_top;
578 board.do_null_move();
579 num_mv = board.find_moves(mv);
580 uint8_t pup = INVALID;
582 for(int i=0;i<num_mv;i++)
584 if(!mv[i].capture || PIECE_OF(mv[i].capture)==PAWN)
585 continue;
586 if(mv[i].to != pup && board.move_see_val(mv[i])>0)
587 if(pup == INVALID)
588 pup = mv[i].to;
589 else
591 quiesce = false;
592 break;
596 board.undo_null_move();
598 #endif
600 if(quiesce && (best == -INF || ply>70) )
602 best = board.evaluate(engine->st_computer_color, alpha, beta);
603 lower_bound = best; //we have at the very least "best" as lower bound now.
604 GUI(notify_eval(ply, best, alpha, beta, best>=beta ? SearchGui::Blue|SearchGui::Bold : SearchGui::Blue ));
606 alpha = MAX(alpha, best);
607 if(best >= beta)
608 goto search_done;
610 if(ply>70)
611 goto search_done;
614 if(quiesce && h && h->no_good_moves)
615 goto search_done;
617 #if NULL_MOVE
618 /*******************************************************************************
619 Try the null move.
620 *******************************************************************************/
621 if(!expect_allbad && !pv && !s->under_check && (stack[ply-1].curr_move != -1)
622 && depth >= 2*PLY && beta<WORST_MATE && null_move_ok())
624 int16_t val;
625 int sdepth = (depth >= 5*PLY) ? (depth-4*PLY) : depth-3*PLY;
627 s->curr_move = -1;
628 board.do_null_move(s->save_buf);
629 GUI1(notify(&board, Move::None(), ply, sdepth, -INF, beta-1, beta, SearchGui::Green ));
630 val = -search( ply+1, sdepth, false, -beta, -beta+NULL_SOLIDITY_MARGIN, false);
631 GUI2(notify_value(ply, val, DIFF_NODES, SearchGui::Bold ));
632 board.undo_null_move(s->save_buf);
634 /* null move cut! */
635 if(val >= beta)
636 return val;
638 #if NULL_SOLIDITY_MARGIN > 1
639 if(depth <= 3*PLY && val > beta - NULL_SOLIDITY_MARGIN)
640 depth -= PLY;
641 #endif
643 if(val < -WORST_MATE)
644 s->null_threat_dangerous = true;
646 #if 0
647 if(val<alpha-100 && /* we are facing a threat*/
648 stack[ply+1].best_move != -1) /* be sure we aren't reading random memory */
650 /* ok, officially the array stack[ply+1].moves has already
651 been deallocated, but who cares :) */
652 s->threat = stack[ply+1].moves[stack[ply+1].best_move];
654 #if 0
655 /* Botvinnik-Markoff extension!!! */
656 if(!extended && ply>=3 && (s->threat == stack[ply-2].threat) )
658 depth += 80;
659 extended = true;
661 #endif
663 #endif
665 #endif
669 /*******************************************************************************
670 Now generate the legal moves and look for a check/stalemate
671 *******************************************************************************/
673 /* generate all the legal moves */
674 s->moves = &mv_stack[mv_stack_top];
675 s->num_moves = board.find_moves(s->moves);
676 mv_stack_top += s->num_moves;
677 s->under_check = board.under_check;
679 pluto1 = stack[ply-1].best_move >=0 ? PLUTO1(stack[ply-1].moves[stack[ply-1].best_move]) : -1;
680 pluto2 = (ply>=2 && stack[ply-1].best_move >=0 && stack[ply-2].best_move >=0) ?
681 PLUTO2(stack[ply-2].moves[stack[ply-2].best_move], stack[ply-1].moves[stack[ply-1].best_move]) : -1;
683 if(s->under_check==2) /* double check */
685 depth += 2*PLY;
686 extended = true;
688 else if(s->under_check) /* simple check */
690 depth += PLY;
691 if(stack[ply-1].curr_move>=0 &&
692 !board.pins[stack[ply-1].moves[ /* last moved piece is not attacking the king */
693 stack[ply-1].curr_move].to]) /* so this is a discover check */
695 depth += PLY/2;
697 extended = true;
700 /* return now if the positon is terminal */
701 if(!s->num_moves)
703 if(s->under_check)
705 /* while mating, sacrify as much as possible :) */
706 int mt = IS_WHITE(board.other_color) ? 5 : -1;
707 int16_t matval = Board::simple_values[PAWN]*board.mat_tracking[mt+PAWN].count +
708 Board::simple_values[KNIGHT]*board.mat_tracking[mt+KNIGHT].count +
709 Board::simple_values[BISHOP]*board.mat_tracking[mt+BISHOP].count +
710 Board::simple_values[QUEEN]*board.mat_tracking[mt+QUEEN].count;
711 best = alpha = beta = -INF + ply*100 + matval;
713 else
714 best = alpha = beta = 0;
715 goto search_done;
718 /* single-reply extension */
719 if(s->num_moves == 1 && !extended)
721 depth += PLY;
722 extended = true;
724 else if(s->num_moves <= 3 && !extended)
726 depth += PLY/2;
727 extended = true;
730 /*******************************************************************************
731 Sort the moves.
732 First comes the move from the hashtable, if avalable.
733 The remaining moves are sorted with a heuristic that keeps in account
734 the history heuristic (ie the moves that previously caused an alpha
735 cutoff), a MVV/LVA bonus value and a small bonus for moves that go
736 toward the center.
737 *******************************************************************************/
739 /* convert the move we got from the hash to the move structure */
740 if(cbest_mv_hash)
742 best_mv_hash = board.uncompress_move(cbest_mv_hash);
743 /* if it happened that the move we got from the hashtable
744 is not valid, simply no move will get the high
745 heuristic bonus value */
747 #if INTERNAL_ITERATIVE_DEEPENING
748 else if(base_depth>3*PLY && pv) /* don't do it only on the pv, or it will be useless :) */
750 s->curr_move = -2;
751 GUI1(notify(&board, Move::None(), ply, base_depth-2*PLY, -INF, alpha, beta, SearchGui::Blue ));
752 int val = search(ply+1, base_depth-2*PLY, true, alpha, beta, expect_allbad);
753 GUI2(search_gui->notify_value(ply, val, DIFF_NODES, 0));
755 HashEntry *h2 = probe_hash( board.hash );
756 if(h2 && h2->best_mv)
758 cbest_mv_hash = h2->best_mv;
759 best_mv_hash = board.uncompress_move(cbest_mv_hash);
762 #endif //INTERNAL_ITERATIVE_DEEPENING
764 /* for each move calculate the heuristic goodness value */
766 Move *prev = (stack[ply-1].curr_move>=0) ? &stack[ply-1].moves[stack[ply-1].curr_move] : NULL;
767 if(quiesce)
768 moves_quiescence_heuristic( s->moves, s->num_moves, best_mv_hash,
769 best, alpha, beta, base_depth, prev);
770 else
772 int overall_ext = 0;
773 moves_heuristic( s->moves, s->num_moves, pv, ply, base_depth,
774 best_mv_hash, quiesce, prev, &overall_ext, pluto1, pluto2 );
775 depth += overall_ext;
779 /* if quiesce rip-off the "non-critical" moves */
780 if(quiesce)
782 int n = 0;
783 for(int i=0;i<s->num_moves;i++)
784 if(s->moves[i].val>0)
785 s->moves[n++] = s->moves[i];
786 mv_stack_top -= s->num_moves-n;
787 s->num_moves = n;
788 if(!n)
789 no_good_moves = true;
792 /* Don't do it now, do it incrementally */
793 //sort_moves( s->moves, s->num_moves );
796 #if EARLY_TRANSP_CUTOFF
797 /*******************************************************************************
798 Try to get an early beta cutoff using the hash table values
799 of the following moves, and improve alpha too.
800 Try on the first 6 value of the ordered moves (argh, looking into the
801 hashtable is very expensive because of the cache!!!!!!!!)
802 *******************************************************************************/
804 if(depth >= 3*PLY)
806 HashKey hk = board.move_hash(s->moves[0]);
807 for(int i=1;i<s->num_moves;i++)
809 engine->prefetch_hash(hk);
810 HashKey newhk = board.move_hash(s->moves[i]);
811 HashEntry *h2 = engine->probe_hash( hk );
812 hk = newhk;
814 if(h2 && h2->depth >= depth-PLY)
816 if(-h2->up >= beta)
818 best = -h2->up;
819 goto search_done;
821 alpha = MAX(alpha, (int16_t)-h2->up);
825 HashEntry *h2 = engine->probe_hash( hk );
826 if(h2 && h2->depth >= depth-PLY)
828 if(-h2->up >= beta)
830 best = -h2->up;
831 goto search_done;
833 alpha = MAX(alpha, (int16_t)-h2->up);
836 #endif //EARLY_TRANSP_CUTOFF
838 /*******************************************************************************
839 It is now time to loop all the successor moves and search recursively.
840 *******************************************************************************/
842 #if FUTILITY
843 /* calcluate the evaluation (required by fitility pruning) */
844 position_val = quiesce ? best : board.dummy_evaluate(); //evaluate( st_computer_color, -INF, INF);
845 #endif
847 for(int i=0;i<s->num_moves;i++)
849 int16_t val;
850 int sdepth = depth-100;
852 /* sort moves incrementally, in the hope of a beta cut */
853 engine->incremental_sort_moves(s->moves+i, s->num_moves-i);
855 /* extensions calculated during the heuristic sort */
856 sdepth += s->moves[i].extend;
858 #if FUTILITY
859 /* futility pruning, it is done only if we are not under check
860 and the move is not a "critical" move */
861 if(depth>0 && depth<=2*PLY && !s->under_check && s->moves[i].val < 28000)
863 static const int mavala[] = { 0, 500, 325, 975, 325, 100, 0 };
865 int16_t fmargin = (depth <= PLY ? 420 : 720);
866 int16_t fval = position_val + mavala[PIECE_OF(s->moves[i].capture)];
867 if(fval < alpha-fmargin)
868 continue;
870 #endif
872 if(!expect_allbad)
874 int mx = CAPT_INDEX(s->moves[i]);
876 /* collect history statistics */
877 if(!s->moves[i].capture &&
878 !(s->moves[i].flags>=PROMOTE_FIRST) )
880 int ix = HISTORY_INDEX(s->moves[i]);
881 if(history_tot[ix] > 1024)
883 history_tot[ix] = history_tot[ix]*7/8;
884 history_hit[ix] = history_hit[ix]*7/8;
886 history_tot[ix] += quiesce ? 8 : 16;
889 if(pluto1!=-1 && !s->moves[i].capture &&
890 !(s->moves[i].flags>=PROMOTE_FIRST))
892 int ix = FAST_PLUTO(pluto1, mx);
893 if(pluto_tot[ix] > 256)
895 pluto_tot[ix] = pluto_tot[ix]*7/8;
896 pluto_hit[ix] = pluto_hit[ix]*7/8;
898 pluto_tot[ix] += quiesce ? 8 : 16;
901 if(pluto2!=-1 && !s->moves[i].capture &&
902 !(s->moves[i].flags>=PROMOTE_FIRST))
904 int ix = FAST_PLUTO(pluto2, mx);
905 if(pluto2_tot[ix] > 128)
907 pluto2_tot[ix] = pluto2_tot[ix]*7/8;
908 pluto2_hit[ix] = pluto2_hit[ix]*7/8;
910 pluto2_tot[ix] += quiesce ? 8 : 16;
914 //Set data for pawn-strike
915 #if PAWN_STRIKE
916 if(!quiesce && PIECE_OF(board.data[s->moves[i].from]) == PAWN && s->moves[i].val == 27000) //FIXME, UGLY
918 stack[ply].escape_from[1] = s->moves[i].to + Board::up_dir[IS_WHITE(board.color_to_move)] + RIGHT;
919 if(OUT_OF_BOARD(stack[ply].escape_from[1]) || !IS_OF_COLOR(board.data[stack[ply].escape_from[1]], board.other_color)
920 || PIECE_OF(board.data[stack[ply].escape_from[1]])==PAWN)
921 stack[ply].escape_from[1] = INVALID;
922 stack[ply].escape_from[2] = s->moves[i].to + Board::up_dir[IS_WHITE(board.color_to_move)] + LEFT;
923 if(OUT_OF_BOARD(stack[ply].escape_from[2]) || !IS_OF_COLOR(board.data[stack[ply].escape_from[2]], board.other_color)
924 || PIECE_OF(board.data[stack[ply].escape_from[2]])==PAWN)
925 stack[ply].escape_from[2] = INVALID;
927 else
929 stack[ply].escape_from[1] = stack[ply].escape_from[2] = INVALID;
931 #endif //PAWN_STRIKE
932 s->curr_move = i;
933 board.do_move(s->moves[i], s->save_buf);
936 #if 0
937 uint64_t q;
938 if(base_depth > 0 && sdepth <= 0)
940 STAT(quiesce_called);
941 q = stat_quiesce_nodes;
943 #endif
945 #if NEGA_SCOUT /* use negascout, (null window search for nodes that are not in the pv) */
946 if(i == 0 && !expect_allbad)
948 if(depth>=0 && pv && mv_pv_top>=ply)
950 mv_pv[ply] = s->moves[i];
951 mv_pv_top = ply + 1;
953 #endif
954 GUI1(notify(&board, s->moves[i], ply, sdepth, s->moves[i].val, alpha, beta, SearchGui::Bold ));
955 val = -search( ply+1, sdepth, pv, -beta, -alpha, !pv);
956 GUI2(notify_value(ply, val, DIFF_NODES, 0));
957 #if NEGA_SCOUT
959 else
961 bool red_fuck = false;
962 #if LATE_MOVE_REDUCTION
963 /* history pruning, if this is not a "critical" move and the failhigh
964 stats are too low, try a reduced depth search (if it returns >alpha,
965 re-do the full depth search) */
966 // if((sdepth>0) && !s->under_check && depth<=3*PLY && s->moves[i].val<28000 &&
967 // (history_hit[HISTORY_INDEX(s->moves[i])]+1)*3
968 // < (history_tot[HISTORY_INDEX(s->moves[i])]+1))
969 if( (sdepth>0) && !s->under_check && !s->null_threat_dangerous
970 && s->moves[i].val<28000 && (s->moves[i].val<(sdepth<=PLY ? 650 : 450)) )
972 int rdepth = depth - 2*PLY;
973 //sdepth - PLY; //(s->moves[i].val<250 ? 2*PLY : PLY) - s->moves[i].extend;
974 GUI1(notify(&board, s->moves[i], ply, rdepth, s->moves[i].val, alpha, alpha+1, SearchGui::Italic|SearchGui::Gray ));
975 val = -search( ply+1, rdepth, false, -alpha-1, -alpha, false );
976 GUI2(notify_value(ply, val, DIFF_NODES, val>alpha ? SearchGui::Red : 0));
977 if(val <= alpha)
978 goto skip_search; /* reduced search was effective */
979 red_fuck = true;
981 #endif
982 GUI1(notify(&board, s->moves[i], ply, sdepth, s->moves[i].val, alpha, alpha+1, 0 ));
983 val = -search( ply+1, sdepth, false, -alpha-1, -alpha, red_fuck);
984 GUI2(notify_value(ply, val, DIFF_NODES, val>alpha ? SearchGui::Red : 0));
986 if((val>alpha) && pv)
988 if(depth>=0 && mv_pv_top>=ply)
990 mv_pv[ply] = s->moves[i];
991 mv_pv_top = ply + 1;
994 GUI1(notify(&board, s->moves[i], ply, sdepth, -INF, alpha, beta, SearchGui::Bold ));
995 val = -search( ply+1, sdepth, true, -beta, -alpha, false );
996 GUI2(notify_value(ply, val, DIFF_NODES, val>beta ? SearchGui::Red : 0));
999 #endif
1001 #if 0
1002 if(base_depth > 0 && sdepth <= 0)
1004 q = stat_quiesce_nodes-q;
1005 if(q > stat_max_quiesce_nodes)
1007 stat_max_quiesce_nodes = q;
1008 max_quiesce_board = board;
1011 #endif
1013 skip_search:
1014 board.undo_move(s->moves[i], s->save_buf);
1016 /* update the current best value and check for and alpha cut */
1017 if(val > best)
1019 best = val;
1021 best_mv_found = i;
1022 s->best_move = i;
1025 if(best > alpha)
1027 /* alpha improvement! */
1028 alpha = best;
1031 /* beta cut! */
1032 if(best >= beta)
1033 break;
1036 if(!expect_allbad || best >= beta)
1038 /* collect statistics for the history */
1039 if(/*best >= beta &&*/ (best_mv_found!=-1) &&
1040 !s->moves[best_mv_found].capture &&
1041 !(s->moves[best_mv_found].flags>=PROMOTE_FIRST) )
1042 history_hit[HISTORY_INDEX(s->moves[best_mv_found])] += quiesce ? 8 : 16;
1044 int mx = CAPT_INDEX(s->moves[best_mv_found]);
1045 if(pluto1!=-1 && !s->moves[best_mv_found].capture &&
1046 !(s->moves[best_mv_found].flags>=PROMOTE_FIRST))
1048 int ix = FAST_PLUTO(pluto1, mx);
1049 pluto_hit[ix] += quiesce ? 8 : 16;
1052 if(pluto2!=-1 && !s->moves[best_mv_found].capture &&
1053 !(s->moves[best_mv_found].flags>=PROMOTE_FIRST))
1055 int ix = FAST_PLUTO(pluto2, mx);
1056 pluto2_hit[ix] += quiesce ? 8 : 16;
1060 search_done:
1061 mv_stack_top -= s->num_moves; /* free the moves we allocated */
1063 /* this is a null move, save what the threat is */
1064 if(stack[ply-1].curr_move == -1 && best_mv_found >= 0)
1065 stack[ply-1].null_threat = s->moves[best_mv_found];
1067 /* if we found a best move searching, that move will be saved.
1068 if we did no search (ie quiescence), save the old hash value,
1069 or -1 if no hash entry had been found */
1070 int bestmv = cbest_mv_hash;
1071 if(best_mv_found >= 0)
1072 bestmv = board.compress_move(s->moves[best_mv_found]);
1074 /* write the value in the hash, with the index of the best move */
1075 engine->write_hash( board.hash,
1076 best > orig_alpha ? MIN(best, beta) : lower_bound,
1077 best < beta ? best : +INF,
1078 MAX(base_depth,-500), bestmv, no_good_moves);
1079 GUI(notify_hash(ply, best > orig_alpha ? MIN(best, beta) : lower_bound, best < beta ? best : +INF,
1080 MAX(base_depth,-500), alpha, beta, bestmv ? board.uncompress_move(bestmv) : Move::None(), true,
1081 SearchGui::Bold | SearchGui::Magenta) );
1083 return best;
1087 SearchRoot::SearchRoot(Engine* e, const Board& b)
1088 : mv_stack_top(0)
1089 , mv_pv_top(0)
1090 , search_gui(e->search_gui)
1091 , engine(e)
1092 , board(b)
1096 SearchRoot::~SearchRoot()
1100 Move Engine::find_best_move()
1102 ASSERT(!thinking);
1103 Engine *engine = this;
1104 int base_depth = PLY;
1105 int num_mate_hits = 0;
1106 SearchRoot root(this, board);
1107 SearchRoot::SearchStack *s = &root.stack[0];
1109 /* initialize the root node */
1110 s->curr_move = -1;
1111 s->best_move = 0;
1112 s->null_threat = Move::FromInt(0);
1113 s->null_threat_dangerous = false;
1114 s->moves = root.mv_stack;
1115 s->num_moves = root.mv_stack_top = root.board.find_moves(s->moves);
1116 #if PAWN_STRIKE
1117 s->escape_from[1] = s->escape_from[2] = INVALID;
1118 #endif //PAWN_STRIKE
1120 ASSERT(s->moves);
1122 /* calculate how much time we will think*/
1123 start_think_time = current_time();
1124 if(analysis_limit == TIME_LIMIT)
1126 if(board.color_to_move == st_computer_color)
1127 calc_best_time();
1128 else /* pondering? analysing? */
1129 time_best_csec = 99999999;
1130 max_think_time = start_think_time + time_best_csec - 2;
1133 /* to print the analysis */
1134 if(post)
1135 output("\tply\tscore\ttime\tnodes\tpv\n");
1137 /* return immediatly if the move is forced. */
1138 if(s->num_moves==1)
1140 if(post)
1141 output("\t0\t0\t0\t0\t%s (only move)\n", board.move_to_alg(MoveStr().data(), s->moves[0]));
1142 return s->moves[0];
1145 /* probe the play lines */
1146 if(eng_status == PLAYING && st_computer_color == board.color_to_move)
1148 Move retv = probe_lines(&root.board);
1150 if(retv.valid())
1152 retv.val = 0;
1153 output("\t0\t0\t0\t0\t%s (selected line)\n", board.move_to_alg(MoveStr().data(), retv));
1154 return retv;
1158 /* probe the book */
1159 Move bookmove = probe_book(&root.board);
1160 if(bookmove.valid())
1162 bookmove.val = 0;
1163 for(int i=0;i<s->num_moves++;i++)
1164 if(bookmove == s->moves[i])
1165 return bookmove;
1166 output("Error!!! invalid move in the book!!!\n");
1169 reset_stats();
1170 IFGUI(reset_hash()); //Try to be deterministic
1171 processed_nodes = 0;
1172 thinking = true;
1173 make_old_hash();
1175 memset( root.history_tot, 0, HISTORY_SIZE*sizeof(uint16_t));
1176 memset( root.history_hit, 0, HISTORY_SIZE*sizeof(uint16_t));
1177 memset( root.pluto_tot, 0, PLUTO_SIZE*sizeof(uint16_t));
1178 memset( root.pluto_hit, 0, PLUTO_SIZE*sizeof(uint16_t));
1179 memset( root.pluto2_tot, 0, PLUTO_SIZE*sizeof(uint16_t));
1180 memset( root.pluto2_hit, 0, PLUTO_SIZE*sizeof(uint16_t));
1182 GUI(init_root());
1185 /* set the back jump for the quick thinking exit */
1186 if(setjmp(back))
1187 goto exit_thinking;
1190 /* do the iterative deepening thing. */
1191 while(1)
1193 int16_t alpha = num_mate_hits ? -INF : -WORST_MATE;
1194 int16_t beta = num_mate_hits ? INF : WORST_MATE;
1196 GUI(new_root_level(base_depth));
1198 /* for each move call the alpha-beta search function */
1199 for(int i=0;i<s->num_moves;i++)
1201 s->curr_move = i;
1202 root.board.do_move(s->moves[i], s->save_buf);
1203 #if NEGA_SCOUT
1204 if(i == 0)
1206 root.mv_pv[0] = s->moves[i];
1207 root.mv_pv_top = 1;
1208 #endif
1209 GUI1(notify(&root.board, s->moves[i], 0, base_depth-PLY, s->moves[i].val, alpha, beta, SearchGui::Bold));
1210 s->moves[i].val = -root.search( 1, base_depth-PLY, true, -beta, -alpha, false );
1211 GUI2(notify_value(0, s->moves[i].val, DIFF_NODES, 0));
1212 #if NEGA_SCOUT
1214 else
1216 GUI1(notify(&root.board, s->moves[i], 0, base_depth-PLY, s->moves[i].val, alpha, alpha+1, 0 ));
1217 s->moves[i].val = -root.search( 1, base_depth-PLY, false, -alpha-1, -alpha, false );
1218 GUI2(notify_value(0, s->moves[i].val, DIFF_NODES, s->moves[i].val>alpha ? SearchGui::Red : 0));
1220 if(s->moves[i].val > alpha)
1222 root.mv_pv[0] = s->moves[i];
1223 root.mv_pv_top = 1;
1225 GUI1(notify(&root.board, s->moves[i], 0, base_depth-PLY, -INF, alpha, beta, SearchGui::Bold));
1226 s->moves[i].val = -root.search( 1, base_depth-PLY, true, -beta, -alpha, false );
1227 GUI2(notify_value(0, s->moves[i].val, DIFF_NODES, 0));
1230 #endif
1231 root.board.undo_move(s->moves[i], s->save_buf);
1233 /* cut alpha */
1234 if(s->moves[i].val > alpha)
1236 alpha = s->moves[i].val;
1238 Move tmp = s->moves[i];
1239 for(int q=i-1;q>=0;q--)
1240 s->moves[q+1] = s->moves[q];
1241 s->moves[0] = tmp;
1243 /* this move caused an alpha cut, so print the new line */
1244 if( post /*&& processed_nodes>100000*/)
1246 output("\t%d\t%d\t%d\t%llu\t",
1247 base_depth/PLY,
1248 s->moves[0].val,
1249 current_time() - start_think_time,
1250 (unsigned long long)processed_nodes);
1251 print_moves(root.mv_pv, 1/*root.mv_pv_top*/, true, true);
1256 /* print the result of the analysis at this depth */
1257 if( post /*&& processed_nodes>100000*/)
1259 output("\t%d\t%d\t%d\t%llu\t",
1260 base_depth/PLY,
1261 s->moves[0].val,
1262 current_time() - start_think_time,
1263 (unsigned long long)processed_nodes);
1264 print_moves(root.mv_pv, 1/*root.mv_pv_top*/, true, true);
1267 /* max depth */
1268 if( base_depth >= 40*PLY )
1269 break;
1271 /* return in case of fixed depth search */
1272 if( eng_status == PLAYING && st_computer_color == board.color_to_move &&
1273 analysis_limit == DEPTH_LIMIT && base_depth == st_depth*PLY)
1274 break;
1276 /* return if 3/5 of time is gone (we can't search another ply anyway) */
1277 if( eng_status == PLAYING && st_computer_color == board.color_to_move &&
1278 analysis_limit == TIME_LIMIT &&
1279 (current_time()-start_think_time) >= (time_best_csec*3/5) )
1280 break;
1282 /* if a checkmate was detected return immediately */
1283 if( ABS(alpha) > WORST_MATE)
1285 num_mate_hits++;
1286 if(num_mate_hits >= 5)
1287 break;
1290 base_depth += PLY;
1293 exit_thinking:
1295 if(post)
1297 print_stats();
1298 output("#max quiesce board: %s [%d %d]\n", max_quiesce_board.to_fen(BigStr().data()),
1299 max_quiesce_alpha, max_quiesce_beta);
1302 thinking = false;
1303 return s->moves[0];