Tuned late move reduction.
[rattatechess.git] / search.cpp
blob021e1c9b1d3c6a37a508772c50caa028b28fdeb2
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 <string.h>
21 #include <stdlib.h>
23 #define MAX_PV 80
24 #define PLY 100
26 Move null_threat[MAX_PV];
27 bool null_threat_dangerous[MAX_PV];
28 uint8_t escape_from_1[MAX_PV];
29 uint8_t escape_from_2[MAX_PV];
31 #define HISTORY_SIZE (64*64)
32 uint16_t history_tot[HISTORY_SIZE];
33 uint16_t history_hit[HISTORY_SIZE];
35 #define HISTORY_INDEX(m) (SQUARE_TO_64(m.from)*64 + SQUARE_TO_64(m.to))
37 #define WORST_MATE (INF-2500)
39 int stat_early_transp;
40 int stat_hash_write;
41 int stat_hash_tot;
42 int stat_hash_ex;
43 int stat_hash_low;
44 int stat_hash_upp;
45 int stat_best_cut;
46 int stat_best_first;
47 int stat_search_moves;
48 int stat_best_cut0;
49 int stat_best_first0;
50 int stat_search_moves0;
51 int stat_evaluate;
52 int stat_evaluate_cutoff;
53 int stat_null_move;
54 int stat_null_cut;
57 #if 0
58 #define STAT(x)
59 #else
61 #define S_ALL \
62 S(max_quiesce_nodes); \
63 S(quiesce_nodes); \
64 S(quiesce_hash); \
65 S(quiesce_called); \
66 S(quiesce_best_can_be_first); \
67 S(quiesce_best_was_first); \
68 S(quiesce_cutoff); \
69 S(quiesce_cutoff_first); \
70 S(search_nodes); \
71 S(search_hash); \
72 S(search_best_can_be_first); \
73 S(search_best_was_first); \
74 S(search_cutoff); \
75 S(search_cutoff_first); \
76 S(null_tried); \
77 S(null_successful);
79 #define STAT(x) stat_##x++;
81 #define S(x) uint64_t stat_##x;
82 S_ALL
83 Board max_quiesce_board;
84 int16_t max_quiesce_alpha;
85 int16_t max_quiesce_beta;
86 #undef S
88 void reset_stats()
90 #define S(x) stat_##x = 0;
91 S_ALL
92 #undef S
95 void print_stats()
97 #define S(x) printf("# " #x " = %llu\n", (unsigned long long)stat_##x);
98 S_ALL
99 #undef S
102 #endif
104 /* enable the quiescence search */
105 #define QUIESCENCE 1
107 /* enable the nega-scout pv-search */
108 #define NEGA_SCOUT 1
110 /* enable the null move */
111 #define NULL_MOVE 1
113 /* before doing the alpha beta search check if any of the following positions
114 can give use an early cutoff thanks to the hashtable */
115 #define EARLY_TRANSP_CUTOFF 1
117 /* SEEMS TO BE A BIT BAD:
118 Extend when there is only one decent move */
119 #define SINGULAR_EXTENSION 0
121 /* reduce nodes for moves that are not check/captures that are considered
122 bad from the heuristic */
123 #define LATE_MOVE_REDUCTION 1
125 /* futility pruning: */
126 #define FUTILITY 0
128 /* when the hashtable provides no "best" move, do a depth-2 search */
129 #define INTERNAL_ITERATIVE_DEEPENING 0
131 /* use the history sorting heuristic */
132 #define HISTORY_HEURISTIC 1
134 /* improve the sorting heuristic for pawn strikes */
135 #define PAWNSTRIKE_HEURISTIC 1
137 /* improve the sorting heuristic for moves to the center */
138 #define CENTER_HEURISTIC 0
141 class SearchStack
143 public:
144 int base_depth; /* depth of the search at this ply */
145 int extensions; /* global extensions at this node */
146 Move *moves; /* all generated moves */
147 int num_moves; /* num of moves */
148 int curr_move; /* the move being currently analyzed */
149 int16_t alpha; /* alpha ans beta when the search started (not improved) */
150 int16_t beta;
151 bool under_check;
152 Move threat;
153 int best_move;
156 Move mv_stack[200*MAX_PV];
157 int mv_stack_top = 0;
158 SearchStack stack[MAX_PV];
159 int current_ply = 0;
161 void Engine::print_stat()
163 if(eng_status != ANALYZING)
165 printf("Thinking: ");
166 for(int i=0; i<current_ply; i++)
168 if(stack[i].curr_move == -2)
169 continue; //Internal iterative deepening
170 else if(stack[i].curr_move == -1)
172 printf("<NULL>");
173 board.do_null_move();
175 else
177 char buf[32];
178 printf("%s", move_to_alg(buf, &(stack[i].moves[stack[i].curr_move]) ) );
179 board.do_move(stack[i].moves[stack[i].curr_move]);
181 printf((i != current_ply-1) ? " " : "\n");
183 rewind_thinking();
184 return;
187 if(thinking)
189 char buf[32];
190 output("stat01: %d %llu %d %d %d %s\n",
191 current_time() - start_think_time,
192 (unsigned long long)processed_nodes,
193 stack[0].base_depth/100,
194 stack[0].num_moves-1-stack[0].curr_move,
195 stack[0].num_moves,
196 move_to_alg(buf, &(stack[0].moves[stack[0].curr_move]) ) );
198 else
199 output("stat01: 0 0 0 0 0\n");
202 void Engine::rewind_thinking()
204 for(int i=current_ply-1; i>=0; i--)
206 if(stack[i].curr_move == -2)
208 else if(stack[i].curr_move == -1)
209 board.undo_null_move();
210 else
211 board.undo_move(stack[i].moves[stack[i].curr_move]);
215 bool Engine::null_move_ok()
217 int c = IS_WHITE(board.color_to_move) ? 5 : -1;
218 int numpawns = board.mat_tracking[c+PAWN].count;
219 int numpieces = board.mat_tracking[c+KNIGHT].count + board.mat_tracking[c+BISHOP].count
220 + board.mat_tracking[c+ROOK].count + board.mat_tracking[c+QUEEN].count;
221 if(numpieces >= 2)
222 return true;
223 if(numpieces >= 1 && numpawns >= 2)
224 return true;
225 return false;
228 void Engine::restore_thinking()
230 for(int i=0; i<current_ply; i++)
232 if(stack[i].curr_move == -2)
234 else if(stack[i].curr_move == -1)
235 board.do_null_move();
236 else
237 board.do_move(stack[i].moves[stack[i].curr_move]);
240 /* regenerate pinning info and under_check var, just to be sure :) */
241 Move mvs[250];
242 board.find_moves(mvs);
245 void
246 Engine::moves_heuristic(Move *mv, int num_mv, int ply, int orig_depth,
247 Move best_mv_hash, bool quiesce, Move* prev)
249 int hash_move = -1;
251 for(int i=0;i<num_mv;i++)
253 mv[i].val = 0;
254 mv[i].extend = 0;
256 /* give a high bonus to the move stored in the hash, if any.
257 mark only which is, don't continue, because some extensions
258 may be triggered ad added later (ie pawn strike, etc) */
259 if(mv[i].as_int == best_mv_hash.as_int)
260 hash_move = i;
262 #if 1
263 #if 0
264 if(PIECE_OF(board.data[mv[i].from]) != PAWN &&
265 (mv[i].from == escape_from_1[ply-1] || mv[i].from == escape_from_2[ply-1]) )
267 int x = board.move_see_val(mv[i]);
269 if(x >= 0)
271 mv[i].val += 29000 + x; /* escape */
272 mv[i].extend = PLY;
273 continue;
276 #endif
278 /* process strong pawn moves */
279 if(PIECE_OF(board.data[mv[i].from])==PAWN) /* pawn strike */
281 if( ROW_OF(mv[i].to) == board.seventh_rank[IS_WHITE(board.color_to_move)] )
283 int x = board.move_see_val(mv[i]);
285 if(x>=0)
287 mv[i].val += 29599; /* 7th push */
288 mv[i].extend = PLY; /* extend search */
289 continue;
293 if( mv[i].flags == PROMOTEQUEEN )
295 int x = board.move_see_val(mv[i]);
297 if(x>=0)
299 mv[i].val += 29600; /* promote! */
300 mv[i].extend = PLY; /* extend search */
301 continue;
305 #if 1
306 if(orig_depth >= 2*PLY)
308 /* pawn strike */
309 uint8_t up_right = Board::up_dir[IS_WHITE(board.color_to_move)] + RIGHT;
310 uint8_t up_left = Board::up_dir[IS_WHITE(board.color_to_move)] + LEFT;
311 uint8_t p1 = mv[i].to + up_right;
312 uint8_t p2 = mv[i].to + up_left;
313 uint8_t a = OUT_OF_BOARD(p1) ? 0 : board.data[p1];
314 uint8_t b = OUT_OF_BOARD(p2) ? 0 : board.data[p2];
315 if( (COLOR_OF(a)==board.other_color && PIECE_OF(a)!=PAWN)
316 || (COLOR_OF(b)==board.other_color && PIECE_OF(b)!=PAWN) )
318 int x = board.move_see_val(mv[i]);
320 if(x>=0)
322 mv[i].val = 27000; /* pawn strike! */
323 //mv[i].extend = PLY; /* extend search */
324 continue;
328 #endif
330 #endif
331 if(mv[i].capture)
333 int x = board.move_see_val(mv[i]);
335 if(prev && prev->capture &&
336 (mv[i].to == prev->to) && (x >= Board::simple_values[PIECE_OF(prev->capture)]) )
338 mv[i].val = 29900;
339 if( (x == Board::simple_values[PIECE_OF(prev->capture)]) )
340 mv[i].extend = /*(x==1) ? (PLY/2) :*/ PLY;
341 // else if(prev->capture && ABS(x - Board::simple_values[PIECE_OF(prev->capture)]) == 1)
342 // mv[i].extend = PLY/2;
343 continue;
346 if(x>0)
348 if(orig_depth>-7*PLY && board.move_is_check(mv[i]) )
349 mv[i].val = 29800+x;
350 else
351 mv[i].val = 29000+x;
352 continue;
354 else if(x>=0 && orig_depth>-7*PLY && board.move_is_check(mv[i]) )
356 /* = capture but check */
357 mv[i].val = 29800;
358 continue;
361 else /* add a bonus for checks (but not too deep, or quiescence will get mad) */
362 if(orig_depth>-3*PLY && board.move_is_check(mv[i]) )
364 if(board.move_see_val(mv[i])>=0)
366 mv[i].val = 28799;
367 continue;
371 /* null-move threat */
372 if(null_threat[ply-1] == mv[i])
374 mv[i].val = 28500;
375 continue;
378 mv[i].val += history_hit[HISTORY_INDEX(mv[i])] * 1024 / (history_tot[HISTORY_INDEX(mv[i])] + 4);
380 #if CENTER_HEURISTIC
381 static int bof[128] = {
382 0,0,1,2,2,1,0,0,ENDL,
383 0,1,2,3,3,2,1,0,ENDL,
384 0,2,4,5,5,4,2,0,ENDL,
385 1,2,5,6,6,5,2,1,ENDL,
386 1,2,5,6,6,5,2,1,ENDL,
387 0,2,4,5,5,4,2,0,ENDL,
388 0,1,2,3,3,2,1,0,ENDL,
389 0,0,1,2,2,1,0,0,ENDL
392 /* add a bonus for moves towards the center */
393 mv[i].val += (bof[mv[i].to] - bof[mv[i].from]);
394 #endif //CENTER_HEURISTIC
397 if(hash_move!=-1)
398 mv[hash_move].val = 30000;
402 void
403 Engine::moves_quiescence_heuristic(Move *mv, int num_mv, const Move& best_mv_hash,
404 int static_eval, int alpha, int beta, int orig_depth, Move* prev)
406 for(int i=0;i<num_mv;i++)
408 mv[i].val = -10000;
409 mv[i].extend = 0;
411 /* give a high bonus to the move stored in the hash, if any.
412 mark only which is, don't continue, because some extensions
413 may be triggered ad added later (ie pawn strike, etc) */
414 if(mv[i].as_int == best_mv_hash.as_int)
416 mv[i].val = 30000;
417 continue;
420 #if 0
421 /* process strong pawn moves */
422 if(PIECE_OF(board.data[mv[i].from])==PAWN) /* pawn strike */
424 if( ROW_OF(mv[i].to) == board.seventh_rank[IS_WHITE(board.color_to_move)] )
426 int x = board.move_see_val(mv[i]);
428 if(x<0)
430 mv[i].val += -15000;
431 continue;
434 mv[i].val += 29499; /* 7th push */
435 mv[i].extend = PLY; /* extend search */
436 continue;
439 if( mv[i].flags == PROMOTEQUEEN )
441 int x = board.move_see_val(mv[i]);
443 if(x<0)
445 mv[i].val += -15000;
446 continue;
449 mv[i].val += 29500; /* promote! */
450 mv[i].extend = PLY; /* extend search */
451 continue;
454 #if 0
455 /* pawn strike */
456 uint8_t p1 = mv[i].to + up_right;
457 uint8_t p2 = mv[i].to + up_left;
458 uint8_t a = OUT_OF_BOARD(p1) ? 0 : board.data[p1];
459 uint8_t b = OUT_OF_BOARD(p2) ? 0 : board.data[p2];
460 if( (COLOR_OF(a)==board.other_color && PIECE_OF(a)!=PAWN)
461 || (COLOR_OF(b)==board.other_color && PIECE_OF(b)!=PAWN) )
463 int x = board.move_see_val(mv[i]);
464 if(x<0)
466 mv[i].val += -15000;
467 continue;
470 mv[i].val += 27000; /* pawn strike! */
471 mv[i].extend = PLY; /* extend search */
472 continue;
474 #endif
476 #endif
477 if(mv[i].capture)
479 int x = board.move_see_val(mv[i]);
481 if(prev && prev->capture &&
482 (mv[i].to == prev->to) && (x >= Board::simple_values[PIECE_OF(prev->capture)]) )
484 mv[i].val = 29900;
485 if( (x == Board::simple_values[PIECE_OF(prev->capture)]) )
486 mv[i].extend = /*(x==1) ? (PLY/2) :*/ PLY;
487 // else if(prev->capture && ABS(x - Board::simple_values[PIECE_OF(prev->capture)]) == 1)
488 // mv[i].extend = PLY/2;
489 continue;
492 if(x>0)
494 if(orig_depth>-5*PLY && board.move_is_check(mv[i]) )
495 mv[i].val = 18000+x;
496 else
497 mv[i].val = 10000+x;
498 continue;
500 else if(x>=0 && orig_depth>-5*PLY && board.move_is_check(mv[i]) )
502 /* = capture but check */
503 mv[i].val = 18000;
504 continue;
507 else /* add a bonus for checks (but not too deep, or quiescence will get mad) */
508 if(orig_depth>-3*PLY && board.move_is_check(mv[i]) )
510 if(board.move_see_val(mv[i])>=0)
512 mv[i].val = 7990;
513 continue;
520 /*******************************************************************************
521 The main alpha-beta recursive search function.
522 It handles both normal search (with or without null move)
523 and quiescence search, because i have having 2 almost identical
524 function around.
525 *******************************************************************************/
526 int16_t Engine::search(int ply, int depth, bool pv, int16_t alpha, int16_t beta)
528 SearchStack *s = &stack[ply];
529 int16_t best = -INF;
530 uint16_t cbest_mv_hash = 0; /* the compressed move from the hash */
531 Move best_mv_hash = Move::FromInt(0); /* the move from the hash */
532 int best_mv_found = -1; /* the index of the best move AFTER searching */
533 bool quiesce;
534 bool extended = false;
535 int16_t position_val;
537 if(depth <= 0)
538 STAT(quiesce_nodes)
540 s->base_depth = depth;
541 s->extensions = 0;
542 s->num_moves = 0;
543 s->curr_move = -1;
544 s->alpha = alpha;
545 s->beta = beta;
546 s->threat = Move::FromInt(0);
547 s->best_move = -1;
548 null_threat[ply] = Move::FromInt(0);
549 null_threat_dangerous[ply] = false;
550 //escape_from_1[ply] = escape_from_2[ply] = INVALID;
552 /* check if time is running out */
553 if(check_time())
554 return 0;
556 /* check for a draw for repetition or 50mvs. Of course the draw for
557 repetition must be checked BEFORE probing the hash :)*/
558 if(check_draw())
559 return (board.color_to_move == st_computer_color) ? -35 :
560 ((board.other_color == st_computer_color) ? 35 : 0); /* be aggressive! */
562 /*******************************************************************************
563 Probe the hashtable.
564 If the probe is succesful the hashtable will return us value
565 that can be exact, a lower bound or an upper bound, and if the
566 depth of the hashed search is >= the current depth this value
567 will be used to improve alpha and beta and possibly return immediatly.
568 The hastable will also give us a "best" move that will be searched
569 first.
570 This is the move that caused the "final" cutoff when this position
571 was searched previously. This best move is actually the index of the best
572 move in the array of generated moves (it is supposed to be deterministic :)
573 *******************************************************************************/
574 stat_hash_tot++;
575 HashEntry *h = probe_hash( board.hash );
577 if(h && (h->depth >= s->base_depth))// || ABS(h->value)>INF-1000) )
579 int16_t l = h->lower();
580 int16_t u = h->upper();
581 if(l>=beta || u==l)
582 return l;
583 if(u<=alpha)
584 return u;
586 beta = MIN(beta, u);
587 alpha = MAX(alpha, l);
590 if(h)
591 cbest_mv_hash = h->best_mv;
593 /*******************************************************************************
594 Test if we are under check, and if so extend search
595 *******************************************************************************/
597 s->under_check = board.under_attack(board.king_pos[IS_WHITE(board.color_to_move)],
598 board.other_color);
600 /*******************************************************************************
601 If it is time to quiesce, evaluate and test if we can exit
602 immediately with a beta cut-off (try first a rough eval - delta)
603 *******************************************************************************/
604 quiesce = ((!s->under_check) && (depth<=0)) || (depth<-60*PLY);
606 #if 0 //PAPOPEPO
607 if(quiesce && depth>=-PLY)
609 int num_mv;
610 Move *mv = mv_stack + mv_stack_top;
611 board.do_null_move();
612 num_mv = board.find_moves(mv);
613 uint8_t pup = INVALID;
615 for(int i=0;i<num_mv;i++)
617 if(!mv[i].capture || PIECE_OF(mv[i].capture)==PAWN)
618 continue;
619 if(mv[i].to != pup && board.move_see_val(mv[i])>0)
620 if(pup == INVALID)
621 pup = mv[i].to;
622 else
624 quiesce = false;
625 break;
629 board.undo_null_move();
631 #endif
633 if(quiesce)
635 stat_evaluate++;
636 best = board.evaluate(st_computer_color, alpha, beta);
638 alpha = MAX(alpha, best);
639 if(best >= beta)
641 stat_evaluate_cutoff++;
642 goto search_done;
645 if(ply>60)
646 goto search_done;
649 #if NULL_MOVE
650 /*******************************************************************************
651 Try the null move.
652 *******************************************************************************/
653 if(!s->under_check && (stack[ply-1].curr_move != -1) && depth >= 2*PLY && beta<WORST_MATE && null_move_ok())
655 int16_t val;
656 int sdepth = (depth >= 5*PLY) ? (depth-4*PLY) : depth-3*PLY;
658 stat_null_move++;
659 s->curr_move = -1;
660 current_ply++;
661 board.do_null_move();
662 val = -search( ply+1, sdepth, true, -beta, -beta+1);
663 board.undo_null_move();
664 current_ply--;
666 /* null move cut! */
667 if(val >= beta)
669 stat_null_cut++;
670 return val;
673 if(val < -WORST_MATE)
674 null_threat_dangerous[ply] = true;
675 #if 0
676 if(val<alpha-100 && /* we are facing a threat*/
677 stack[ply+1].best_move != -1) /* be sure we aren't reading random memory */
679 /* ok, officially the array stack[ply+1].moves has already
680 been deallocated, but who cares :) */
681 s->threat = stack[ply+1].moves[stack[ply+1].best_move];
683 #if 0
684 /* Botvinnik-Markoff extension!!! */
685 if(!extended && ply>=3 && (s->threat == stack[ply-2].threat) )
687 depth += 80;
688 extended = true;
690 #endif
692 #endif
694 #endif
698 /*******************************************************************************
699 Now generate the legal moves and look for a check/stalemate
700 *******************************************************************************/
702 /* generate all the legal moves */
703 s->moves = &mv_stack[mv_stack_top];
704 s->num_moves = board.find_moves(s->moves);
705 mv_stack_top += s->num_moves;
706 s->under_check = board.under_check;
708 if(s->under_check==2) /* double check */
710 depth += 2*PLY;
711 extended = true;
713 else if(s->under_check) /* simple check */
715 depth += PLY;
716 if(stack[ply-1].curr_move>=0 &&
717 !board.pins[stack[ply-1].moves[ /* last moved piece is not attacking the king */
718 stack[ply-1].curr_move].to]) /* so this is a discover check */
720 depth += PLY/2;
722 extended = true;
725 /* return now if the positon is terminal */
726 if(!s->num_moves)
728 if(s->under_check)
730 /* while mating, sacrify as much as possible :) */
731 best = -INF + ply;//*50 + board.material[IS_WHITE(eng_color)]/50;
733 else
734 best = 0;
735 goto search_done;
738 /* single-reply extension */
739 if(s->num_moves == 1 && !extended)
741 depth += PLY;
742 extended = true;
744 else if(s->num_moves <= 3 && !extended)
746 depth += PLY/2;
747 extended = true;
750 /*******************************************************************************
751 Sort the moves.
752 First comes the move from the hashtable, if avalable.
753 The remaining moves are sorted with a heuristic that keeps in account
754 the history heuristic (ie the moves that previously caused an alpha
755 cutoff), a MVV/LVA bonus value and a small bonus for moves that go
756 toward the center.
757 *******************************************************************************/
759 /* convert the move we got from the hash to the move structure */
760 if(cbest_mv_hash)
762 best_mv_hash = board.uncompress_move(cbest_mv_hash);
763 /* if it happened that the move we got from the hashtable
764 is not valid, simply no move will get the high
765 heuristic bonus value */
767 #if INTERNAL_ITERATIVE_DEEPENING
768 else if(s->base_depth>3*PLY && pv) /* don't do it only on the pv, or it will be useless :) */
770 s->curr_move = -2;
771 current_ply++;
772 int val = search(ply+1, s->base_depth-2*PLY, true, alpha, beta);
773 current_ply--;
775 HashEntry *h2 = probe_hash( board.hash );
776 if(h2 && h2->best_mv)
778 cbest_mv_hash = h2->best_mv;
779 best_mv_hash = board.uncompress_move(cbest_mv_hash);
782 #endif //INTERNAL_ITERATIVE_DEEPENING
784 /* for each move calculate the heuristic goodness value */
786 Move *prev = (stack[ply-1].curr_move>=0) ? &stack[ply-1].moves[stack[ply-1].curr_move] : NULL;
787 if(quiesce)
788 moves_quiescence_heuristic( s->moves, s->num_moves, best_mv_hash,
789 best, alpha, beta, s->base_depth, prev);
790 else
791 moves_heuristic( s->moves, s->num_moves, ply, s->base_depth,
792 best_mv_hash, quiesce, prev );
795 /* if quiesce rip-off the "non-critical" moves */
796 if(quiesce)
798 int n = 0;
799 for(int i=0;i<s->num_moves;i++)
800 if(s->moves[i].val>0)
801 s->moves[n++] = s->moves[i];
802 mv_stack_top -= s->num_moves-n;
803 s->num_moves = n;
806 /* Don't do it now, do it incrementally */
807 //sort_moves( s->moves, s->num_moves );
810 #if EARLY_TRANSP_CUTOFF
811 /*******************************************************************************
812 Try to get an early beta cutoff using the hash table values
813 of the following moves, and improve alpha too.
814 Try on the first 6 value of the ordered moves (argh, looking into the
815 hashtable is very expensive because of the cache!!!!!!!!)
816 *******************************************************************************/
818 if(depth >= 3*PLY)
820 HashKey hk = board.move_hash(s->moves[0]);
821 for(int i=1;i<s->num_moves;i++)
823 prefetch_hash(hk);
824 HashKey newhk = board.move_hash(s->moves[i]);
825 HashEntry *h2 = probe_hash( hk );
826 hk = newhk;
828 if(h2 && h2->depth >= depth-PLY)
830 if(-h2->up >= beta)
832 best = -h2->up;
833 goto search_done;
835 alpha = MAX(alpha, (int16_t)-h2->up);
839 HashEntry *h2 = probe_hash( hk );
840 if(h2 && h2->depth >= depth-PLY)
842 if(-h2->up >= beta)
844 best = -h2->up;
845 goto search_done;
847 alpha = MAX(alpha, (int16_t)-h2->up);
850 #endif //EARLY_TRANSP_CUTOFF
852 /* set the current best move index (as will be saved in the hash) */
853 best_mv_found = 0;
855 /*******************************************************************************
856 It is now time to loop all the successor moves and search recursively.
857 *******************************************************************************/
859 #if FUTILITY
860 /* calcluate the evaluation (required by fitility pruning) */
861 position_val = quiesce ? best : board.evaluate( st_computer_color, -INF, INF);
862 #endif
864 for(int i=0;i<s->num_moves;i++)
866 int16_t val;
867 int sdepth = depth-100;
869 /* sort moves incrementally, in the hope of a beta cut */
870 incremental_sort_moves(s->moves+i, s->num_moves-i);
872 if(depth < s->base_depth+100)
873 sdepth += s->moves[i].extend; /* extensions calculated during the heuristic sort */
875 #if FUTILITY
876 /* futility pruning, it is done only if we are no under check
877 and the move is not a "critical" move */
878 if(depth>0 && depth<3*PLY && !s->under_check && s->moves[i].val < 28000)
880 static const int mavala[] = { 0, 490, 315, 980, 315, 100, 0 };
882 int16_t fmargin = (depth <= PLY ? 420 : 950);
883 int16_t fval = position_val + mavala[PIECE_OF(s->moves[i].capture)];
884 if(fval < alpha-fmargin)
885 continue;
887 #endif
889 /* collect history statistics */
890 if(s->moves[i].val<28000)
892 int ix = HISTORY_INDEX(s->moves[i]);
893 if(history_tot[ix] > 1024)
895 history_tot[ix] = history_tot[ix]*7/8;
896 history_hit[ix] = history_hit[ix]*7/8;
898 history_tot[ix]++;
901 #if 0
902 if(!quiesce && PIECE_OF(board.data[s->moves[i].from]) == PAWN && s->moves[i].val == 27000) //FIXME, UGLY
904 escape_from_1[ply] = s->moves[i].to + Board::up_dir[IS_WHITE(board.color_to_move)] + RIGHT;
905 escape_from_2[ply] = s->moves[i].to + Board::up_dir[IS_WHITE(board.color_to_move)] + LEFT;
907 else
909 escape_from_1[ply] = escape_from_2[ply] = 0;
911 #endif
913 s->curr_move = i;
914 current_ply++;
915 board.do_move(s->moves[i]);
918 uint64_t q;
919 if(s->base_depth > 0 && sdepth <= 0)
921 STAT(quiesce_called);
922 q = stat_quiesce_nodes;
925 #if NEGA_SCOUT /* use negascout, (null window search for nodes that are not in the pv) */
926 if(i == 0) // || depth<200)
927 val = -search( ply+1, sdepth, pv, -beta, -alpha );
928 else
930 #if LATE_MOVE_REDUCTION
931 /* history pruning, if this is not a "critical" move and the failhigh
932 stats are too low, try a reduced depth search (if it returns >alpha,
933 re-do the full depth search) */
934 // if((sdepth>0) && !s->under_check && depth<=3*PLY && s->moves[i].val<28000 &&
935 // (history_hit[HISTORY_INDEX(s->moves[i])]+1)*3
936 // < (history_tot[HISTORY_INDEX(s->moves[i])]+1))
937 if((sdepth>0) && !s->under_check && s->moves[i].val<28000 && (i>=7 || (i>=5 && s->moves[i].val<350) ) )
939 val = -search( ply+1, sdepth - PLY, false, -alpha-1, -alpha );
940 if(val <= alpha)
941 goto skip_search; /* reduced search was effective */
943 #endif
944 val = -search( ply+1, sdepth, false, -alpha-1, -alpha );
945 if((val>alpha) && pv)
946 val = -search( ply+1, sdepth, true, -beta, -alpha );
948 #else
949 /* normal full width alpha-beta */
950 val = -search( ply+1, sdepth, pv, -beta, -alpha );
951 #endif
953 if(s->base_depth > 0 && sdepth <= 0)
955 q = stat_quiesce_nodes-q;
956 if(q > stat_max_quiesce_nodes)
958 stat_max_quiesce_nodes = q;
959 max_quiesce_board = board;
963 skip_search:
964 board.undo_move(s->moves[i]);
965 current_ply--;
967 /* update the current best value and check for and alpha cut */
968 best = MAX(best, val);
969 if(best > alpha)
971 best_mv_found = i;
972 s->best_move = i;
974 /* alpha cut! */
975 alpha = best;
978 /* beta cut! */
979 if(best >= beta)
980 break;
983 /* update a few stats */
984 if(!quiesce)
986 if(best_mv_found==0)
988 if(best >= beta)
989 stat_best_cut++;
990 else
991 stat_best_first++;
993 stat_search_moves++;
994 if(ply==0 && depth>100)
996 if(best_mv_found==0)
998 if(best >= beta)
999 stat_best_cut0++;
1000 else
1001 stat_best_first0++;
1003 stat_search_moves0++;
1007 /* collect statistics for the history */
1008 if(best >= beta &&
1009 !s->moves[best_mv_found].capture &&
1010 !(s->moves[best_mv_found].flags>=PROMOTE_FIRST))
1012 history_hit[HISTORY_INDEX(s->moves[best_mv_found])]++;
1013 history_tot[HISTORY_INDEX(s->moves[best_mv_found])]++;
1016 search_done:
1017 mv_stack_top -= s->num_moves; /* free the moves we allocated */
1019 /* this is a null move, save what the threat is */
1020 if(stack[ply-1].curr_move == -1 && best_mv_found >= 0)
1021 null_threat[ply-1] = s->moves[best_mv_found];
1023 /* if we found a best move searching, that move will be saved.
1024 if we did no search (ie quiescence), save the old hash value,
1025 or -1 if no hash entry had been found */
1026 int bestmv = cbest_mv_hash;
1027 if(best_mv_found >= 0)
1028 bestmv = board.compress_move(s->moves[best_mv_found]);
1030 /* write the value in the hash, with the index of the best move */
1031 write_hash( best > s->alpha ? best : -INF,
1032 best < beta ? MAX(best, s->alpha) : +INF,
1033 MAX(s->base_depth,-500), bestmv);
1035 return best;
1039 Move Engine::find_best_move()
1041 int num_mate_hits = 0;
1042 SearchStack *s = &stack[0];
1043 Move retv;
1044 Move result[80];
1046 ASSERT(!thinking);
1048 /* initialize the root node */
1049 current_ply = 0;
1050 s->base_depth = 100;
1051 s->extensions = 0;
1052 s->curr_move = -1;
1053 s->alpha = -INF;
1054 s->beta = INF;
1055 s->threat = Move::FromInt(0);
1056 s->best_move = -1;
1058 s->moves = mv_stack;
1059 s->num_moves = mv_stack_top = board.find_moves(s->moves);
1061 /* calculate how much time we will think*/
1062 start_think_time = current_time();
1063 if( analysis_limit == TIME_LIMIT )
1065 if(board.color_to_move == st_computer_color)
1066 calc_best_time();
1067 else /* pondering? analysing? */
1068 time_best_csec = 99999999;
1069 max_think_time = start_think_time + time_best_csec - 2;
1072 /* to print the analysis */
1073 if(post)
1074 output("\tply\tscore\ttime\tnodes\tpv\n");
1076 /* return immediatly if the move is forced. */
1077 if(s->num_moves==1)
1078 return s->moves[0];
1080 /* probe the play lines */
1081 if( eng_status == PLAYING
1082 && st_computer_color == board.color_to_move
1083 && probe_lines( board.hash, &retv))
1085 retv.val = 0;
1086 return retv;
1089 /* probe the book */
1090 if(probe_book( board.hash, &retv)) {
1091 for(int i=0;i<s->num_moves++;i++)
1092 if(retv.as_int == s->moves[i].as_int)
1094 retv.val = 0;
1095 return retv;
1097 output("Error!!! invalid move in the book!!!\n");
1100 #if 0
1101 prof_eval = 0;
1102 prof_find_moves = 0;
1103 prof_find_captures = 0;
1104 prof_heuristic = 0;
1105 prof_sort_moves = 0;
1106 prof_move_is_check = 0;
1107 prof_move_see_val = 0;
1108 prof_tot = rdtsc();
1109 #endif
1111 stat_early_transp = 0;
1112 stat_hash_tot = 0;
1113 stat_hash_ex = 0;
1114 stat_hash_low = 0;
1115 stat_hash_upp = 0;
1116 stat_best_first = 0;
1117 stat_best_cut = 0;
1118 stat_search_moves = 0;
1119 stat_best_first0 = 0;
1120 stat_best_cut0 = 0;
1121 stat_search_moves0 = 0;
1122 stat_evaluate = 0;
1123 stat_evaluate_cutoff = 0;
1124 stat_null_move = 0;
1125 stat_null_cut = 0;
1126 stat_hash_write = 0;
1128 reset_stats();
1129 //reset_hash();
1131 //analysis_color = board.color_to_move;
1132 processed_nodes = 0;
1133 int16_t best_guess = 0;
1134 thinking = true;
1135 make_old_hash();
1137 /* set the back jump for the quick thinking exit */
1138 if(setjmp(back))
1139 goto exit_thinking;
1141 /* start with a guess of 0 */
1142 s->moves[0].val = 0;
1143 retv = s->moves[0];
1145 /* do the iterative deepening thing. */
1146 while(1)
1148 int16_t alpha = -INF;
1149 int i=0;
1151 memset( history_tot, 0, HISTORY_SIZE*sizeof(uint16_t));
1152 memset( history_hit, 0, HISTORY_SIZE*sizeof(uint16_t));
1154 /* save the result of the previous search */
1155 result[s->base_depth/PLY-1] = s->moves[0];
1157 /* for each move call the alpha-beta search function */
1158 int best_mv = 0;
1159 for(i=0;i<s->num_moves;i++)
1161 s->curr_move = i;
1162 current_ply++;
1163 board.do_move(s->moves[i]);
1164 #if NEGA_SCOUT
1165 if(i == 0)
1167 s->moves[i].val = -search( 1, s->base_depth-PLY, true, -INF, -alpha );
1169 else
1171 s->moves[i].val = -search( 1, s->base_depth-PLY, false, -alpha-1, -alpha );
1172 if(s->moves[i].val > alpha)
1173 s->moves[i].val = -search( 1, s->base_depth-PLY, true, -INF, -alpha );
1175 #else
1176 s->moves[i].val = -search( 1, s->base_depth-100, true, -INF, -alpha );
1177 #endif
1178 board.undo_move(s->moves[i]);
1179 current_ply--;
1182 /* cut alpha */
1183 if(s->moves[i].val > alpha)
1185 alpha = s->moves[i].val;
1186 retv = s->moves[i];
1187 best_mv = i;
1188 s->best_move = i;
1190 /* this move caused an alpha cut, so print the new line */
1191 if( post /*&& processed_nodes>100000*/)
1193 output("\t%d\t%d\t%d\t%llu\t", s->base_depth/100,
1194 s->moves[i].val, current_time() - start_think_time, processed_nodes);
1195 print_moves(&s->moves[i], 1, true);
1200 best_guess = alpha;
1202 /* the search is done, sort the moves so that best is searched first */
1203 if(i>1)
1205 s->moves[best_mv].val++;
1206 sort_moves(s->moves, i); /* sort i moves (those that we where able to search) */
1207 s->moves[0].val--;
1210 /* print the result of the analysis at this depth */
1211 if( post /*&& processed_nodes>100000*/)
1213 output("\t%d\t%d\t%d\t%llu\t", s->base_depth/100,
1214 s->moves[0].val, current_time() - start_think_time, processed_nodes);
1215 print_moves(&s->moves[0], 1, true);
1218 /* max depth */
1219 if( s->base_depth >= 40*PLY )
1220 break;
1222 /* return in case of fixed depth search */
1223 if( eng_status == PLAYING && st_computer_color == board.color_to_move &&
1224 analysis_limit == DEPTH_LIMIT && s->base_depth == st_depth*PLY)
1225 break;
1227 /* return if 3/5 of time is gone (we can't search another ply anyway) */
1228 if( eng_status == PLAYING && st_computer_color == board.color_to_move &&
1229 analysis_limit == TIME_LIMIT &&
1230 (current_time()-start_think_time) >= (time_best_csec*3/5) )
1231 break;
1233 /* if a checkmate was detected return immediately */
1234 if( ABS(alpha) > WORST_MATE)
1236 num_mate_hits++;
1237 if(num_mate_hits >= 5)
1238 break;
1241 s->base_depth += PLY;
1244 exit_thinking:
1246 if(post)
1248 #if 0
1249 prof_tot = rdtsc() - prof_tot;
1250 #define SHOW_PROF(a) output("@"#a": %llu (%llu%%)\n", \
1251 prof_##a, prof_##a*100/prof_tot);
1252 SHOW_PROF(tot);
1253 SHOW_PROF(eval);
1254 SHOW_PROF(do_move);
1255 SHOW_PROF(find_moves);
1256 SHOW_PROF(find_captures);
1257 SHOW_PROF(heuristic);
1258 SHOW_PROF(move_is_check);
1259 SHOW_PROF(move_see_val);
1260 SHOW_PROF(sort_moves);
1261 #endif
1262 output("#nodes: %llu (~%llu nps)\n", processed_nodes, (processed_nodes*100)/
1263 MAX(current_time()-start_think_time,1) );
1264 output("# %d times all the moves were seached (non-leaf non-quiesce node)\n",
1265 stat_search_moves);
1266 output("# of which %d times first move caused a cutoff\n", stat_best_cut);
1267 output("# of the remaining %d times first move was really best (%02d%%)\n",
1268 stat_best_first, (stat_best_first*100)/MAX(stat_search_moves-stat_best_cut, 1));
1269 output("# evaluate was called %d times (quiescence node)\n", stat_evaluate);
1270 output("# of which %d times caused an early cutoff (leaf node)\n",
1271 stat_evaluate_cutoff);
1272 output("#hash: %d writes, %d probes (%d succesful, =%d, >%d, <%d)\n",
1273 stat_hash_write, stat_hash_tot, stat_hash_ex+stat_hash_low+stat_hash_upp,
1274 stat_hash_ex, stat_hash_low, stat_hash_upp);
1275 output("#etc: %d\n", stat_early_transp);
1276 output("#null move: %d (%d succesful)\n", stat_null_move, stat_null_cut);
1278 print_stats();
1279 char buf[256];
1280 max_quiesce_board.write_board(buf);
1281 output("#max quiesce board: %s [%d %d]\n", buf, max_quiesce_alpha, max_quiesce_beta);
1284 thinking = false;
1285 return retv;