Fixed another bad typo in the eval.
[rattatechess.git] / search.cpp
blob8ccbca4f3bdde8f607dc507496fd9566588a36eb
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 0
110 if(eng_status != ANALYZING)
112 printf("Thinking: ");
113 for(int i=0; i<current_ply; i++)
115 if(stack[i].curr_move == -2)
116 continue; //Internal iterative deepening
117 else if(stack[i].curr_move == -1)
119 printf("<NULL>");
120 board.do_null_move();
122 else
124 char buf[32];
125 printf("%s", move_to_alg(buf, &(stack[i].moves[stack[i].curr_move]) ) );
126 board.do_move(stack[i].moves[stack[i].curr_move]);
128 printf((i != current_ply-1) ? " " : "\n");
130 rewind_thinking();
131 return;
134 if(thinking)
136 char buf[32];
137 output("stat01: %d %llu %d %d %d %s\n",
138 current_time() - start_think_time,
139 (unsigned long long)processed_nodes,
140 stack[0].base_depth/100,
141 stack[0].num_moves-1-stack[0].curr_move,
142 stack[0].num_moves,
143 move_to_alg(buf, &(stack[0].moves[stack[0].curr_move]) ) );
145 else
146 output("stat01: 0 0 0 0 0\n");
147 #endif
150 bool SearchRoot::null_move_ok()
152 int c = IS_WHITE(board.color_to_move) ? 5 : -1;
153 int numpawns = board.mat_tracking[c+PAWN].count;
154 int numpieces = board.mat_tracking[c+KNIGHT].count + board.mat_tracking[c+BISHOP].count
155 + board.mat_tracking[c+ROOK].count + board.mat_tracking[c+QUEEN].count;
156 if(numpieces >= 2)
157 return true;
158 if(numpieces >= 1 && numpawns >= 2)
159 return true;
160 return false;
163 void
164 SearchRoot::moves_heuristic(Move *mv, int num_mv, bool pv, int ply, int orig_depth,
165 Move best_mv_hash, bool quiesce, Move* prev, int* overall_extensions,
166 int p1, int p2)
168 #if PAWN_STRIKE
169 int escapes[2];
170 int num_escapes = 0;
171 #endif //PAWN_STRIKE
172 int hash_move = -1;
174 for(int i=0;i<num_mv;i++)
176 mv[i].val = 0;
177 mv[i].extend = 0;
179 /* give a high bonus to the move stored in the hash, if any.
180 mark only which is, don't continue, because some extensions
181 may be triggered ad added later (ie pawn strike, etc) */
182 if(mv[i].as_int == best_mv_hash.as_int)
183 hash_move = i;
185 #if PAWN_STRIKE
186 if(num_escapes<=2 && PIECE_OF(board.data[mv[i].from]) != PAWN &&
187 (mv[i].from == stack[ply-1].escape_from[1] || stack[ply-1].escape_from[2]) )
189 int x = board.move_see_val(mv[i]);
191 if(x >= 0)
193 if(num_escapes < 2)
194 escapes[num_escapes] = i;
195 num_escapes++;
198 #endif //PAWN_STRIKE
200 /* process strong pawn moves */
201 if(PIECE_OF(board.data[mv[i].from])==PAWN) /* pawn strike */
203 uint8_t x = X(mv[i].to);
204 uint8_t y = IS_WHITE(board.color_to_move) ? Y(mv[i].to) : 7-Y(mv[i].to);
206 if( mv[i].flags == PROMOTEQUEEN || y == 6)
208 int x = board.move_see_val(mv[i]);
210 if(x>=0)
212 mv[i].val += mv[i].flags ? 29600 : 29599; /* promote! */
213 mv[i].extend = PLY; /* extend search */
214 continue;
218 if(y >= 4)
220 int other = IS_WHITE(board.other_color);
221 uint8_t pos = 7-y;
222 if((!board.line_pawns[other][x].count || board.line_pawns[other][x].pos[0] > pos)
223 && (x==0 || !board.line_pawns[other][x-1].count || board.line_pawns[other][x-1].pos[0] >= pos)
224 && (x==7 || !board.line_pawns[other][x+1].count || board.line_pawns[other][x+1].pos[0] >= pos))
226 int x = board.move_see_val(mv[i]);
228 if(x>=0)
230 mv[i].val += y >= 5 ? 29550 : 29500; /* passed pawn advancing! */
231 if(y >= 5)
232 mv[i].extend = PLY; /* extend search */
233 continue;
238 /* pawn attack */
239 if(orig_depth >= 2*PLY)
241 /* pawn strike */
242 uint8_t up_right = Board::up_dir[IS_WHITE(board.color_to_move)] + RIGHT;
243 uint8_t up_left = Board::up_dir[IS_WHITE(board.color_to_move)] + LEFT;
244 uint8_t p1 = mv[i].to + up_right;
245 uint8_t p2 = mv[i].to + up_left;
246 uint8_t a = OUT_OF_BOARD(p1) ? 0 : board.data[p1];
247 uint8_t b = OUT_OF_BOARD(p2) ? 0 : board.data[p2];
248 if( (COLOR_OF(a)==board.other_color && PIECE_OF(a)!=PAWN)
249 || (COLOR_OF(b)==board.other_color && PIECE_OF(b)!=PAWN) )
251 int x = board.move_see_val(mv[i]);
253 if(x>=0)
255 mv[i].val = 27000; /* pawn strike! */
256 continue;
262 if(mv[i].capture)
264 int x = board.move_see_val(mv[i]);
266 /* recapture? */
267 if(prev && prev->capture &&
268 (mv[i].to == prev->to) && (x >= Board::simple_values[PIECE_OF(prev->capture)]) )
270 mv[i].val = 29900;
271 if( (x == Board::simple_values[PIECE_OF(prev->capture)]) )
272 mv[i].extend = PLY;
273 continue;
276 if(x>0)
278 if(orig_depth>-7*PLY && board.move_is_check(mv[i]) )
279 mv[i].val = 29800+x;
280 else
281 mv[i].val = 29000+x;
282 continue;
284 else if(x>=0 && orig_depth>-7*PLY && board.move_is_check(mv[i]) )
286 /* = capture but check */
287 mv[i].val = 29800;
288 continue;
291 else /* add a bonus for checks (but not too deep, or quiescence will get mad) */
292 if(board.move_is_check(mv[i]) )
294 if(board.move_see_val(mv[i])>=0)
295 mv[i].val = 28799;
296 else
297 mv[i].val = 28700;
298 continue;
301 /* null-move threat */
302 if(stack[ply-1].null_threat == mv[i])
304 mv[i].val = 28500;
305 continue;
308 #if 1
309 mv[i].val += (history_hit[HISTORY_INDEX(mv[i])] + 32) * 1024 / (3 * (history_tot[HISTORY_INDEX(mv[i])] + 64));
311 int mx = CAPT_INDEX(mv[i]);
312 if(p1 != -1)
314 int ix = FAST_PLUTO(p1, mx);
315 mv[i].val += (pluto_hit[ix] + 24) * 1024 / (3 * (pluto_tot[ix] + 48));
317 else
318 mv[i].val += 512/3;
319 if(p2 != -1)
321 int ix = FAST_PLUTO(p2, mx);
322 mv[i].val += (pluto2_hit[ix] + 16) * 1024 / (3 * (pluto2_tot[ix] + 32));
324 else
325 mv[i].val += 512/3;
326 #else
327 mv[i].val += (history_hit[HISTORY_INDEX(mv[i])] + 32) * 256*4 / (history_tot[HISTORY_INDEX(mv[i])] + 64);
328 #endif
331 #if PAWN_STRIKE
332 if((stack[ply-1].escape_from[1] != INVALID || stack[ply-1].escape_from[2] != INVALID) && num_escapes <= 2)
334 for(int i=0;i<num_escapes;i++)
335 mv[escapes[i]].extend = PLY;
337 #endif //PAWN_STRIKE
339 if(hash_move!=-1)
340 mv[hash_move].val = 30000;
344 void
345 SearchRoot::moves_quiescence_heuristic(Move *mv, int num_mv, const Move& best_mv_hash,
346 int static_eval, int alpha, int beta, int orig_depth, Move* prev)
348 int hash_move = -1;
349 for(int i=0;i<num_mv;i++)
351 mv[i].val = -10000;
352 mv[i].extend = 0;
354 /* give a high bonus to the move stored in the hash, if any.
355 mark only which is, don't continue, because some extensions
356 may be triggered ad added later (ie pawn strike, etc) */
357 if(mv[i].as_int == best_mv_hash.as_int)
358 hash_move = i;
360 /* process strong pawn moves */
361 if(PIECE_OF(board.data[mv[i].from])==PAWN)
363 if( ROW_OF(mv[i].to) == board.seventh_rank[IS_WHITE(board.color_to_move)] )
365 int x = board.move_see_val(mv[i]);
367 if(x>=0)
369 mv[i].val += 29499; /* 7th push */
370 mv[i].extend = PLY; /* extend search */
371 continue;
375 if( mv[i].flags == PROMOTEQUEEN )
377 int x = board.move_see_val(mv[i]);
379 if(x<0)
381 mv[i].val += 29500; /* promote! */
382 mv[i].extend = PLY; /* extend search */
383 continue;
388 if(mv[i].capture)
390 int x = board.move_see_val(mv[i]);
392 if(prev && prev->capture &&
393 (mv[i].to == prev->to) && (x >= Board::simple_values[PIECE_OF(prev->capture)]) )
395 mv[i].val = 29900 + x;
396 if( (x == Board::simple_values[PIECE_OF(prev->capture)]) )
397 mv[i].extend = PLY;
398 continue;
401 if( x>0 && ((orig_depth>-2*PLY || static_eval==-INF) ? true : x*100+static_eval+200>alpha) )
403 mv[i].val = 10000+x;
404 continue;
406 else if((x>=0 && orig_depth>-4*PLY) && board.move_is_check(mv[i]) )
408 /* = capture but check */
409 mv[i].val = 8000;
410 continue;
413 else /* add a bonus for checks (but not too deep, or quiescence will get mad) */
414 if(orig_depth>-2*PLY && board.move_is_check(mv[i]) && board.move_see_val(mv[i])>=0)
416 mv[i].val = 5000;
417 continue;
421 if(hash_move >= 0 && mv[hash_move].val > 9000)
422 mv[hash_move].val = 30000;
426 /*******************************************************************************
427 The main alpha-beta recursive search function.
428 It handles both normal search (with or without null move)
429 and quiescence search, because i have having 2 almost identical
430 function around.
431 *******************************************************************************/
432 int16_t SearchRoot::search(int ply, int base_depth, bool pv, int16_t orig_alpha, int16_t beta, bool expect_allbad)
434 SearchStack *s = &stack[ply];
435 int16_t best = -INF;
436 uint16_t cbest_mv_hash = 0; /* the compressed move from the hash */
437 Move best_mv_hash = Move::FromInt(0); /* the move from the hash */
438 int best_mv_found = -1; /* the index of the best move AFTER searching */
439 bool quiesce;
440 bool extended = false;
441 bool no_good_moves = false;
442 int16_t lower_bound = -INF;
443 int16_t position_val;
444 int depth = base_depth;
445 int16_t alpha = orig_alpha;
446 int pluto1, pluto2;
448 #if IGNORE_ALLBAD_NODES
449 expect_allbad = false;
450 #endif
452 #if 0
453 if(ply >= 75)
455 for(int i=ply-1;i>=0;i--)
457 if(stack[i].curr_move == -1)
458 board.undo_null_move(stack[i].save_buf);
459 else if(stack[i].curr_move >= 0)
460 board.undo_move(stack[i].moves[stack[i].curr_move], stack[i].save_buf);
462 printf("[FEN \"%s\"]\n", board.to_fen(BigStr().data()) );
463 for(int i=0;i<ply;i++)
465 printf(" %s", stack[i].curr_move <= -1 ? "NULL" :
466 board.move_to_alg(TmpStr().data(), stack[i].moves[stack[i].curr_move]));
467 if(stack[i].curr_move == -1)
468 board.do_null_move(stack[i].save_buf);
469 else if(stack[i].curr_move >= 0)
470 board.do_move(stack[i].moves[stack[i].curr_move], stack[i].save_buf);
472 printf(" *\n");
473 exit(0);
475 #endif
477 // if(depth <= 0)
478 // STAT(quiesce_nodes)
480 s->num_moves = 0;
481 s->curr_move = -3;
482 s->best_move = -1;
483 s->null_threat = Move::FromInt(0);
484 s->null_threat_dangerous = false;
485 #if PAWN_STRIKE
486 stack[ply].escape_from[1] = stack[ply].escape_from[2] = INVALID;
487 #endif //PAWN_STRIKE
489 engine->prefetch_hash( board.hash );
491 /* check if time is running out */
492 engine->check_time();
494 /* check for a draw for repetition or 50mvs. Of course the draw for
495 repetition must be checked BEFORE probing the hash :)*/
496 if(check_draw(ply))
497 return (board.color_to_move == engine->st_computer_color) ? engine->v_eval_draw :
498 ((board.other_color == engine->st_computer_color) ? -engine->v_eval_draw : 0); /* be aggressive! */
500 /*******************************************************************************
501 Probe the hashtable.
502 If the probe is succesful the hashtable will return us value
503 that can be exact, a lower bound or an upper bound, and if the
504 depth of the hashed search is >= the current depth this value
505 will be used to improve alpha and beta and possibly return immediatly.
506 The hastable will also give us a "best" move that will be searched
507 first.
508 This is the move that caused the "final" cutoff when this position
509 was searched previously. This best move is actually the index of the best
510 move in the array of generated moves (it is supposed to be deterministic :)
511 *******************************************************************************/
513 HashEntry *h = engine->probe_hash( board.hash );
515 if(h){
516 GUI(notify_hash(ply, h->lo, h->up, h->depth, alpha, beta,
517 h->best_mv ? board.uncompress_move(h->best_mv) : Move::None(), false,
518 (((h->depth >= base_depth) && (h->lo>=beta || h->up==h->lo || h->up<=alpha))
519 ? SearchGui::Bold : 0) | SearchGui::Magenta) );
522 if(h && (h->depth >= base_depth))// || ABS(h->value)>INF-1000) )
524 int16_t l = h->lower();
525 int16_t u = h->upper();
527 if(l>=beta || u==l)
528 return l;
529 if(u<=alpha)
530 return u;
532 beta = MIN(beta, u);
533 best = alpha = MAX(alpha, l);
536 if(h)
537 cbest_mv_hash = h->best_mv;
539 /*******************************************************************************
540 Test if we are under check, and if so extend search
541 *******************************************************************************/
543 s->under_check = board.under_attack(board.king_pos[IS_WHITE(board.color_to_move)],
544 board.other_color);
546 /*******************************************************************************
547 If it is time to quiesce, evaluate and test if we can exit
548 immediately with a beta cut-off (try first a rough eval - delta)
549 *******************************************************************************/
550 quiesce = ((!s->under_check) && (depth<=0)) || (ply > 70);
551 if(quiesce)
552 STAT(quiesce_nodes);
554 #if 0 //PAPOPEPO
555 if(quiesce && depth>=-PLY)
557 int num_mv;
558 Move *mv = mv_stack + mv_stack_top;
559 board.do_null_move();
560 num_mv = board.find_moves(mv);
561 uint8_t pup = INVALID;
563 for(int i=0;i<num_mv;i++)
565 if(!mv[i].capture || PIECE_OF(mv[i].capture)==PAWN)
566 continue;
567 if(mv[i].to != pup && board.move_see_val(mv[i])>0)
568 if(pup == INVALID)
569 pup = mv[i].to;
570 else
572 quiesce = false;
573 break;
577 board.undo_null_move();
579 #endif
581 if(quiesce && (best == -INF || ply>70) )
583 best = board.evaluate(engine->st_computer_color, alpha, beta);
584 lower_bound = best; //we have at the very least "best" as lower bound now.
585 GUI(notify_eval(ply, best, alpha, beta, best>=beta ? SearchGui::Blue|SearchGui::Bold : SearchGui::Blue ));
587 alpha = MAX(alpha, best);
588 if(best >= beta)
589 goto search_done;
591 if(ply>70)
592 goto search_done;
595 if(quiesce && h && h->no_good_moves)
596 goto search_done;
598 #if NULL_MOVE
599 /*******************************************************************************
600 Try the null move.
601 *******************************************************************************/
602 if(!expect_allbad && !pv && !s->under_check && (stack[ply-1].curr_move != -1)
603 && depth >= 2*PLY && beta<WORST_MATE && null_move_ok())
605 int16_t val;
606 int sdepth = (depth >= 5*PLY) ? (depth-4*PLY) : depth-3*PLY;
608 s->curr_move = -1;
609 board.do_null_move(s->save_buf);
610 GUI1(notify(&board, Move::None(), ply, sdepth, -INF, beta-1, beta, SearchGui::Green ));
611 val = -search( ply+1, sdepth, false, -beta, -beta+NULL_SOLIDITY_MARGIN, false);
612 GUI2(notify_value(ply, val, DIFF_NODES, SearchGui::Bold ));
613 board.undo_null_move(s->save_buf);
615 /* null move cut! */
616 if(val >= beta)
617 return val;
619 #if NULL_SOLIDITY_MARGIN > 1
620 if(depth <= 3*PLY && val > beta - NULL_SOLIDITY_MARGIN)
621 depth -= PLY;
622 #endif
624 if(val < -WORST_MATE)
625 s->null_threat_dangerous = true;
627 #if 0
628 if(val<alpha-100 && /* we are facing a threat*/
629 stack[ply+1].best_move != -1) /* be sure we aren't reading random memory */
631 /* ok, officially the array stack[ply+1].moves has already
632 been deallocated, but who cares :) */
633 s->threat = stack[ply+1].moves[stack[ply+1].best_move];
635 #if 0
636 /* Botvinnik-Markoff extension!!! */
637 if(!extended && ply>=3 && (s->threat == stack[ply-2].threat) )
639 depth += 80;
640 extended = true;
642 #endif
644 #endif
646 #endif
650 /*******************************************************************************
651 Now generate the legal moves and look for a check/stalemate
652 *******************************************************************************/
654 /* generate all the legal moves */
655 s->moves = &mv_stack[mv_stack_top];
656 s->num_moves = board.find_moves(s->moves);
657 mv_stack_top += s->num_moves;
658 s->under_check = board.under_check;
660 pluto1 = stack[ply-1].best_move >=0 ? PLUTO1(stack[ply-1].moves[stack[ply-1].best_move]) : -1;
661 pluto2 = (ply>=2 && stack[ply-1].best_move >=0 && stack[ply-2].best_move >=0) ?
662 PLUTO2(stack[ply-2].moves[stack[ply-2].best_move], stack[ply-1].moves[stack[ply-1].best_move]) : -1;
664 if(s->under_check==2) /* double check */
666 depth += 2*PLY;
667 extended = true;
669 else if(s->under_check) /* simple check */
671 depth += PLY;
672 if(stack[ply-1].curr_move>=0 &&
673 !board.pins[stack[ply-1].moves[ /* last moved piece is not attacking the king */
674 stack[ply-1].curr_move].to]) /* so this is a discover check */
676 depth += PLY/2;
678 extended = true;
681 /* return now if the positon is terminal */
682 if(!s->num_moves)
684 if(s->under_check)
686 /* while mating, sacrify as much as possible :) */
687 int mt = IS_WHITE(board.other_color) ? 5 : -1;
688 int16_t matval = Board::simple_values[PAWN]*board.mat_tracking[mt+PAWN].count +
689 Board::simple_values[KNIGHT]*board.mat_tracking[mt+KNIGHT].count +
690 Board::simple_values[BISHOP]*board.mat_tracking[mt+BISHOP].count +
691 Board::simple_values[QUEEN]*board.mat_tracking[mt+QUEEN].count;
692 best = alpha = beta = -INF + ply*100 + matval;
694 else
695 best = alpha = beta = 0;
696 goto search_done;
699 /* single-reply extension */
700 if(s->num_moves == 1 && !extended)
702 depth += PLY;
703 extended = true;
705 else if(s->num_moves <= 3 && !extended)
707 depth += PLY/2;
708 extended = true;
711 /*******************************************************************************
712 Sort the moves.
713 First comes the move from the hashtable, if avalable.
714 The remaining moves are sorted with a heuristic that keeps in account
715 the history heuristic (ie the moves that previously caused an alpha
716 cutoff), a MVV/LVA bonus value and a small bonus for moves that go
717 toward the center.
718 *******************************************************************************/
720 /* convert the move we got from the hash to the move structure */
721 if(cbest_mv_hash)
723 best_mv_hash = board.uncompress_move(cbest_mv_hash);
724 /* if it happened that the move we got from the hashtable
725 is not valid, simply no move will get the high
726 heuristic bonus value */
728 #if INTERNAL_ITERATIVE_DEEPENING
729 else if(base_depth>3*PLY && pv) /* don't do it only on the pv, or it will be useless :) */
731 s->curr_move = -2;
732 GUI1(notify(&board, Move::None(), ply, base_depth-2*PLY, -INF, alpha, beta, SearchGui::Blue ));
733 int val = search(ply+1, base_depth-2*PLY, true, alpha, beta, expect_allbad);
734 GUI2(search_gui->notify_value(ply, val, DIFF_NODES, 0));
736 HashEntry *h2 = probe_hash( board.hash );
737 if(h2 && h2->best_mv)
739 cbest_mv_hash = h2->best_mv;
740 best_mv_hash = board.uncompress_move(cbest_mv_hash);
743 #endif //INTERNAL_ITERATIVE_DEEPENING
745 /* for each move calculate the heuristic goodness value */
746 #if DEBUG && TRACK_ATTACKS
747 board.check_attacks();
748 #endif
750 Move *prev = (stack[ply-1].curr_move>=0) ? &stack[ply-1].moves[stack[ply-1].curr_move] : NULL;
751 if(quiesce)
752 moves_quiescence_heuristic( s->moves, s->num_moves, best_mv_hash,
753 best, alpha, beta, base_depth, prev);
754 else
756 int overall_ext = 0;
757 moves_heuristic( s->moves, s->num_moves, pv, ply, base_depth,
758 best_mv_hash, quiesce, prev, &overall_ext, pluto1, pluto2 );
759 depth += overall_ext;
762 #if DEBUG && TRACK_ATTACKS
763 board.check_attacks();
764 #endif
766 /* if quiesce rip-off the "non-critical" moves */
767 if(quiesce)
769 int n = 0;
770 for(int i=0;i<s->num_moves;i++)
771 if(s->moves[i].val>0)
772 s->moves[n++] = s->moves[i];
773 mv_stack_top -= s->num_moves-n;
774 s->num_moves = n;
775 if(!n)
776 no_good_moves = true;
779 /* Don't do it now, do it incrementally */
780 //sort_moves( s->moves, s->num_moves );
783 #if EARLY_TRANSP_CUTOFF
784 /*******************************************************************************
785 Try to get an early beta cutoff using the hash table values
786 of the following moves, and improve alpha too.
787 Try on the first 6 value of the ordered moves (argh, looking into the
788 hashtable is very expensive because of the cache!!!!!!!!)
789 *******************************************************************************/
791 if(depth >= 3*PLY)
793 HashKey hk = board.move_hash(s->moves[0]);
794 for(int i=1;i<s->num_moves;i++)
796 engine->prefetch_hash(hk);
797 HashKey newhk = board.move_hash(s->moves[i]);
798 HashEntry *h2 = engine->probe_hash( hk );
799 hk = newhk;
801 if(h2 && h2->depth >= depth-PLY)
803 if(-h2->up >= beta)
805 best = -h2->up;
806 goto search_done;
808 alpha = MAX(alpha, (int16_t)-h2->up);
812 HashEntry *h2 = engine->probe_hash( hk );
813 if(h2 && h2->depth >= depth-PLY)
815 if(-h2->up >= beta)
817 best = -h2->up;
818 goto search_done;
820 alpha = MAX(alpha, (int16_t)-h2->up);
823 #endif //EARLY_TRANSP_CUTOFF
825 /*******************************************************************************
826 It is now time to loop all the successor moves and search recursively.
827 *******************************************************************************/
829 #if FUTILITY
830 /* calcluate the evaluation (required by fitility pruning) */
831 position_val = quiesce ? best : board.dummy_evaluate(); //evaluate( st_computer_color, -INF, INF);
832 #endif
834 for(int i=0;i<s->num_moves;i++)
836 int16_t val;
837 int sdepth = depth-100;
839 /* sort moves incrementally, in the hope of a beta cut */
840 engine->incremental_sort_moves(s->moves+i, s->num_moves-i);
842 /* extensions calculated during the heuristic sort */
843 sdepth += s->moves[i].extend;
845 #if FUTILITY
846 /* futility pruning, it is done only if we are not under check
847 and the move is not a "critical" move */
848 if(depth>0 && depth<=2*PLY && !s->under_check && s->moves[i].val < 28000)
850 static const int mavala[] = { 0, 500, 325, 975, 325, 100, 0 };
852 int16_t fmargin = (depth <= PLY ? 420 : 720);
853 int16_t fval = position_val + mavala[PIECE_OF(s->moves[i].capture)];
854 if(fval < alpha-fmargin)
855 continue;
857 #endif
859 if(!expect_allbad)
861 int mx = CAPT_INDEX(s->moves[i]);
863 /* collect history statistics */
864 if(!s->moves[i].capture &&
865 !(s->moves[i].flags>=PROMOTE_FIRST) )
867 int ix = HISTORY_INDEX(s->moves[i]);
868 if(history_tot[ix] > 1024)
870 history_tot[ix] = history_tot[ix]*7/8;
871 history_hit[ix] = history_hit[ix]*7/8;
873 history_tot[ix] += quiesce ? 8 : 16;
876 if(pluto1!=-1 && !s->moves[i].capture &&
877 !(s->moves[i].flags>=PROMOTE_FIRST))
879 int ix = FAST_PLUTO(pluto1, mx);
880 if(pluto_tot[ix] > 256)
882 pluto_tot[ix] = pluto_tot[ix]*7/8;
883 pluto_hit[ix] = pluto_hit[ix]*7/8;
885 pluto_tot[ix] += quiesce ? 8 : 16;
888 if(pluto2!=-1 && !s->moves[i].capture &&
889 !(s->moves[i].flags>=PROMOTE_FIRST))
891 int ix = FAST_PLUTO(pluto2, mx);
892 if(pluto2_tot[ix] > 128)
894 pluto2_tot[ix] = pluto2_tot[ix]*7/8;
895 pluto2_hit[ix] = pluto2_hit[ix]*7/8;
897 pluto2_tot[ix] += quiesce ? 8 : 16;
901 //Set data for pawn-strike
902 #if PAWN_STRIKE
903 if(!quiesce && PIECE_OF(board.data[s->moves[i].from]) == PAWN && s->moves[i].val == 27000) //FIXME, UGLY
905 stack[ply].escape_from[1] = s->moves[i].to + Board::up_dir[IS_WHITE(board.color_to_move)] + RIGHT;
906 if(OUT_OF_BOARD(stack[ply].escape_from[1]) || !IS_OF_COLOR(board.data[stack[ply].escape_from[1]], board.other_color)
907 || PIECE_OF(board.data[stack[ply].escape_from[1]])==PAWN)
908 stack[ply].escape_from[1] = INVALID;
909 stack[ply].escape_from[2] = s->moves[i].to + Board::up_dir[IS_WHITE(board.color_to_move)] + LEFT;
910 if(OUT_OF_BOARD(stack[ply].escape_from[2]) || !IS_OF_COLOR(board.data[stack[ply].escape_from[2]], board.other_color)
911 || PIECE_OF(board.data[stack[ply].escape_from[2]])==PAWN)
912 stack[ply].escape_from[2] = INVALID;
914 else
916 stack[ply].escape_from[1] = stack[ply].escape_from[2] = INVALID;
918 #endif //PAWN_STRIKE
919 s->curr_move = i;
920 board.do_move(s->moves[i], s->save_buf);
923 #if 0
924 uint64_t q;
925 if(base_depth > 0 && sdepth <= 0)
927 STAT(quiesce_called);
928 q = stat_quiesce_nodes;
930 #endif
932 #if NEGA_SCOUT /* use negascout, (null window search for nodes that are not in the pv) */
933 if(i == 0 && !expect_allbad)
935 if(depth>=0 && pv && mv_pv_top>=ply)
937 mv_pv[ply] = s->moves[i];
938 mv_pv_top = ply + 1;
940 #endif
941 GUI1(notify(&board, s->moves[i], ply, sdepth, s->moves[i].val, alpha, beta, SearchGui::Bold ));
942 val = -search( ply+1, sdepth, pv, -beta, -alpha, !pv);
943 GUI2(notify_value(ply, val, DIFF_NODES, 0));
944 #if NEGA_SCOUT
946 else
948 bool red_fuck = false;
949 #if LATE_MOVE_REDUCTION
950 /* history pruning, if this is not a "critical" move and the failhigh
951 stats are too low, try a reduced depth search (if it returns >alpha,
952 re-do the full depth search) */
953 // if((sdepth>0) && !s->under_check && depth<=3*PLY && s->moves[i].val<28000 &&
954 // (history_hit[HISTORY_INDEX(s->moves[i])]+1)*3
955 // < (history_tot[HISTORY_INDEX(s->moves[i])]+1))
956 if( (sdepth>0) && !s->under_check && !s->null_threat_dangerous
957 && s->moves[i].val<28000 && (s->moves[i].val<(sdepth<=PLY ? 650 : 450)) )
959 int rdepth = depth - 2*PLY;
960 //sdepth - PLY; //(s->moves[i].val<250 ? 2*PLY : PLY) - s->moves[i].extend;
961 GUI1(notify(&board, s->moves[i], ply, rdepth, s->moves[i].val, alpha, alpha+1, SearchGui::Italic|SearchGui::Gray ));
962 val = -search( ply+1, rdepth, false, -alpha-1, -alpha, false );
963 GUI2(notify_value(ply, val, DIFF_NODES, val>alpha ? SearchGui::Red : 0));
964 if(val <= alpha)
965 goto skip_search; /* reduced search was effective */
966 red_fuck = true;
968 #endif
969 GUI1(notify(&board, s->moves[i], ply, sdepth, s->moves[i].val, alpha, alpha+1, 0 ));
970 val = -search( ply+1, sdepth, false, -alpha-1, -alpha, red_fuck);
971 GUI2(notify_value(ply, val, DIFF_NODES, val>alpha ? SearchGui::Red : 0));
973 if((val>alpha) && pv)
975 if(depth>=0 && mv_pv_top>=ply)
977 mv_pv[ply] = s->moves[i];
978 mv_pv_top = ply + 1;
981 GUI1(notify(&board, s->moves[i], ply, sdepth, -INF, alpha, beta, SearchGui::Bold ));
982 val = -search( ply+1, sdepth, true, -beta, -alpha, false );
983 GUI2(notify_value(ply, val, DIFF_NODES, val>beta ? SearchGui::Red : 0));
986 #endif
988 #if 0
989 if(base_depth > 0 && sdepth <= 0)
991 q = stat_quiesce_nodes-q;
992 if(q > stat_max_quiesce_nodes)
994 stat_max_quiesce_nodes = q;
995 max_quiesce_board = board;
998 #endif
1000 skip_search:
1001 board.undo_move(s->moves[i], s->save_buf);
1003 /* update the current best value and check for and alpha cut */
1004 if(val > best)
1006 best = val;
1008 best_mv_found = i;
1009 s->best_move = i;
1012 if(best > alpha)
1014 /* alpha improvement! */
1015 alpha = best;
1018 /* beta cut! */
1019 if(best >= beta)
1020 break;
1023 if(!expect_allbad || best >= beta)
1025 /* collect statistics for the history */
1026 if(/*best >= beta &&*/ (best_mv_found!=-1) &&
1027 !s->moves[best_mv_found].capture &&
1028 !(s->moves[best_mv_found].flags>=PROMOTE_FIRST) )
1029 history_hit[HISTORY_INDEX(s->moves[best_mv_found])] += quiesce ? 8 : 16;
1031 int mx = CAPT_INDEX(s->moves[best_mv_found]);
1032 if(pluto1!=-1 && !s->moves[best_mv_found].capture &&
1033 !(s->moves[best_mv_found].flags>=PROMOTE_FIRST))
1035 int ix = FAST_PLUTO(pluto1, mx);
1036 pluto_hit[ix] += quiesce ? 8 : 16;
1039 if(pluto2!=-1 && !s->moves[best_mv_found].capture &&
1040 !(s->moves[best_mv_found].flags>=PROMOTE_FIRST))
1042 int ix = FAST_PLUTO(pluto2, mx);
1043 pluto2_hit[ix] += quiesce ? 8 : 16;
1047 search_done:
1048 mv_stack_top -= s->num_moves; /* free the moves we allocated */
1050 /* this is a null move, save what the threat is */
1051 if(stack[ply-1].curr_move == -1 && best_mv_found >= 0)
1052 stack[ply-1].null_threat = s->moves[best_mv_found];
1054 /* if we found a best move searching, that move will be saved.
1055 if we did no search (ie quiescence), save the old hash value,
1056 or -1 if no hash entry had been found */
1057 int bestmv = cbest_mv_hash;
1058 if(best_mv_found >= 0)
1059 bestmv = board.compress_move(s->moves[best_mv_found]);
1061 /* write the value in the hash, with the index of the best move */
1062 engine->write_hash( board.hash,
1063 best > orig_alpha ? MIN(best, beta) : lower_bound,
1064 best < beta ? best : +INF,
1065 MAX(base_depth,-500), bestmv, no_good_moves);
1066 GUI(notify_hash(ply, best > orig_alpha ? MIN(best, beta) : lower_bound, best < beta ? best : +INF,
1067 MAX(base_depth,-500), alpha, beta, bestmv ? board.uncompress_move(bestmv) : Move::None(), true,
1068 SearchGui::Bold | SearchGui::Magenta) );
1070 return best;
1073 uint16_t SearchRoot::pluto_tot[PLUTO_SIZE];
1074 uint16_t SearchRoot::pluto_hit[PLUTO_SIZE];
1075 uint16_t SearchRoot::pluto2_tot[PLUTO_SIZE];
1076 uint16_t SearchRoot::pluto2_hit[PLUTO_SIZE];
1077 uint16_t SearchRoot::history_tot[HISTORY_SIZE];
1078 uint16_t SearchRoot::history_hit[HISTORY_SIZE];
1080 SearchRoot::SearchRoot(Engine* e, const Board& b)
1081 : mv_stack_top(0)
1082 , mv_pv_top(0)
1083 , search_gui(e->search_gui)
1084 , engine(e)
1085 , board(b)
1089 SearchRoot::~SearchRoot()
1093 Move Engine::find_best_move()
1095 ASSERT(!thinking);
1096 Engine *engine = this;
1097 int base_depth = PLY;
1098 int num_mate_hits = 0;
1099 SearchRoot root(this, board);
1100 SearchRoot::SearchStack *s = &root.stack[0];
1102 /* initialize the root node */
1103 s->curr_move = -1;
1104 s->best_move = 0;
1105 s->null_threat = Move::FromInt(0);
1106 s->null_threat_dangerous = false;
1107 s->moves = root.mv_stack;
1108 s->num_moves = root.mv_stack_top = root.board.find_moves(s->moves);
1109 #if PAWN_STRIKE
1110 s->escape_from[1] = s->escape_from[2] = INVALID;
1111 #endif //PAWN_STRIKE
1113 ASSERT(s->moves);
1115 /* calculate how much time we will think*/
1116 start_think_time = current_time();
1117 if(analysis_limit == TIME_LIMIT)
1119 if(board.color_to_move == st_computer_color)
1120 calc_best_time();
1121 else /* pondering? analysing? */
1122 time_best_csec = 99999999;
1123 max_think_time = start_think_time + time_best_csec - 2;
1126 /* to print the analysis */
1127 if(post)
1128 output("\tply\tscore\ttime\tnodes\tpv\n");
1130 /* return immediatly if the move is forced. */
1131 if(s->num_moves==1)
1133 if(post)
1134 output("\t0\t0\t0\t0\t%s (only move)\n", board.move_to_alg(MoveStr().data(), s->moves[0]));
1135 return s->moves[0];
1138 /* probe the play lines */
1139 if(eng_status == PLAYING && st_computer_color == board.color_to_move)
1141 Move retv = probe_lines(&root.board);
1143 if(retv.valid())
1145 retv.val = 0;
1146 output("\t0\t0\t0\t0\t%s (selected line)\n", board.move_to_alg(MoveStr().data(), retv));
1147 return retv;
1151 /* probe the book */
1152 Move bookmove = probe_book(&root.board);
1153 if(bookmove.valid())
1155 bookmove.val = 0;
1156 for(int i=0;i<s->num_moves++;i++)
1157 if(bookmove == s->moves[i])
1158 return bookmove;
1159 output("Error!!! invalid move in the book!!!\n");
1162 reset_stats();
1163 IFGUI(reset_hash()); //Try to be deterministic
1164 processed_nodes = 0;
1165 thinking = true;
1166 make_old_hash();
1168 memset( root.history_tot, 0, HISTORY_SIZE*sizeof(uint16_t));
1169 memset( root.history_hit, 0, HISTORY_SIZE*sizeof(uint16_t));
1170 memset( root.pluto_tot, 0, PLUTO_SIZE*sizeof(uint16_t));
1171 memset( root.pluto_hit, 0, PLUTO_SIZE*sizeof(uint16_t));
1172 memset( root.pluto2_tot, 0, PLUTO_SIZE*sizeof(uint16_t));
1173 memset( root.pluto2_hit, 0, PLUTO_SIZE*sizeof(uint16_t));
1175 GUI(init_root());
1178 /* set the back jump for the quick thinking exit */
1179 if(setjmp(back))
1180 goto exit_thinking;
1183 /* do the iterative deepening thing. */
1184 while(1)
1186 int16_t alpha = num_mate_hits ? -INF : -WORST_MATE;
1187 int16_t beta = num_mate_hits ? INF : WORST_MATE;
1189 GUI(new_root_level(base_depth));
1191 /* for each move call the alpha-beta search function */
1192 for(int i=0;i<s->num_moves;i++)
1194 s->curr_move = i;
1195 root.board.do_move(s->moves[i], s->save_buf);
1196 #if NEGA_SCOUT
1197 if(i == 0)
1199 root.mv_pv[0] = s->moves[i];
1200 root.mv_pv_top = 1;
1201 #endif
1202 GUI1(notify(&root.board, s->moves[i], 0, base_depth-PLY, s->moves[i].val, alpha, beta, SearchGui::Bold));
1203 s->moves[i].val = -root.search( 1, base_depth-PLY, true, -beta, -alpha, false );
1204 GUI2(notify_value(0, s->moves[i].val, DIFF_NODES, 0));
1205 #if NEGA_SCOUT
1207 else
1209 GUI1(notify(&root.board, s->moves[i], 0, base_depth-PLY, s->moves[i].val, alpha, alpha+1, 0 ));
1210 s->moves[i].val = -root.search( 1, base_depth-PLY, false, -alpha-1, -alpha, false );
1211 GUI2(notify_value(0, s->moves[i].val, DIFF_NODES, s->moves[i].val>alpha ? SearchGui::Red : 0));
1213 if(s->moves[i].val > alpha)
1215 root.mv_pv[0] = s->moves[i];
1216 root.mv_pv_top = 1;
1218 GUI1(notify(&root.board, s->moves[i], 0, base_depth-PLY, -INF, alpha, beta, SearchGui::Bold));
1219 s->moves[i].val = -root.search( 1, base_depth-PLY, true, -beta, -alpha, false );
1220 GUI2(notify_value(0, s->moves[i].val, DIFF_NODES, 0));
1223 #endif
1224 root.board.undo_move(s->moves[i], s->save_buf);
1226 /* cut alpha */
1227 if(s->moves[i].val > alpha)
1229 alpha = s->moves[i].val;
1231 Move tmp = s->moves[i];
1232 for(int q=i-1;q>=0;q--)
1233 s->moves[q+1] = s->moves[q];
1234 s->moves[0] = tmp;
1236 /* this move caused an alpha cut, so print the new line */
1237 if( post /*&& processed_nodes>100000*/)
1239 output("\t%d\t%d\t%d\t%llu\t",
1240 base_depth/PLY,
1241 s->moves[0].val,
1242 current_time() - start_think_time,
1243 (unsigned long long)processed_nodes);
1244 print_moves(root.mv_pv, 1/*root.mv_pv_top*/, true, true);
1249 /* print the result of the analysis at this depth */
1250 if( post /*&& processed_nodes>100000*/)
1252 output("\t%d\t%d\t%d\t%llu\t",
1253 base_depth/PLY,
1254 s->moves[0].val,
1255 current_time() - start_think_time,
1256 (unsigned long long)processed_nodes);
1257 print_moves(root.mv_pv, 1/*root.mv_pv_top*/, true, true);
1260 /* max depth */
1261 if( base_depth >= 40*PLY )
1262 break;
1264 /* return in case of fixed depth search */
1265 if( eng_status == PLAYING && st_computer_color == board.color_to_move &&
1266 analysis_limit == DEPTH_LIMIT && base_depth == st_depth*PLY)
1267 break;
1269 /* return if 3/5 of time is gone (we can't search another ply anyway) */
1270 if( eng_status == PLAYING && st_computer_color == board.color_to_move &&
1271 analysis_limit == TIME_LIMIT &&
1272 (current_time()-start_think_time) >= (time_best_csec*3/5) )
1273 break;
1275 /* if a checkmate was detected return immediately */
1276 if( ABS(alpha) > WORST_MATE)
1278 num_mate_hits++;
1279 if(num_mate_hits >= 5)
1280 break;
1283 base_depth += PLY;
1286 exit_thinking:
1288 if(post)
1290 print_stats();
1291 output("#max quiesce board: %s [%d %d]\n", max_quiesce_board.to_fen(BigStr().data()),
1292 max_quiesce_alpha, max_quiesce_beta);
1295 thinking = false;
1296 return s->moves[0];