flush stdout during tests.
[rattatechess.git] / search.cpp
blob8e0235f5c2342cc402d362643d81b8646aaaf780
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 >= 3)
222 return true;
223 if(numpieces >= 2 && numpawns >= 2)
224 return true;
225 if(numpieces >= 1 && numpawns >= 3)
226 return true;
227 return false;
230 void Engine::restore_thinking()
232 for(int i=0; i<current_ply; i++)
234 if(stack[i].curr_move == -2)
236 else if(stack[i].curr_move == -1)
237 board.do_null_move();
238 else
239 board.do_move(stack[i].moves[stack[i].curr_move]);
242 /* regenerate pinning info and under_check var, just to be sure :) */
243 Move mvs[250];
244 board.find_moves(mvs);
247 void
248 Engine::moves_heuristic(Move *mv, int num_mv, int ply, int orig_depth,
249 Move best_mv_hash, bool quiesce, Move* prev)
251 int hash_move = -1;
253 for(int i=0;i<num_mv;i++)
255 mv[i].val = 0;
256 mv[i].extend = 0;
258 /* give a high bonus to the move stored in the hash, if any.
259 mark only which is, don't continue, because some extensions
260 may be triggered ad added later (ie pawn strike, etc) */
261 if(mv[i].as_int == best_mv_hash.as_int)
262 hash_move = i;
264 #if 1
265 #if 0
266 if(PIECE_OF(board.data[mv[i].from]) != PAWN &&
267 (mv[i].from == escape_from_1[ply-1] || mv[i].from == escape_from_2[ply-1]) )
269 int x = board.move_see_val(mv[i]);
271 if(x >= 0)
273 mv[i].val += 29000 + x; /* escape */
274 mv[i].extend = PLY;
275 continue;
278 #endif
280 /* process strong pawn moves */
281 if(PIECE_OF(board.data[mv[i].from])==PAWN) /* pawn strike */
283 if( ROW_OF(mv[i].to) == board.seventh_rank[IS_WHITE(board.color_to_move)] )
285 int x = board.move_see_val(mv[i]);
287 if(x>=0)
289 mv[i].val += 29599; /* 7th push */
290 mv[i].extend = PLY; /* extend search */
291 continue;
295 if( mv[i].flags == PROMOTEQUEEN )
297 int x = board.move_see_val(mv[i]);
299 if(x>=0)
301 mv[i].val += 29600; /* promote! */
302 mv[i].extend = PLY; /* extend search */
303 continue;
307 #if 1
308 if(orig_depth >= 2*PLY)
310 /* pawn strike */
311 uint8_t up_right = Board::up_dir[IS_WHITE(board.color_to_move)] + RIGHT;
312 uint8_t up_left = Board::up_dir[IS_WHITE(board.color_to_move)] + LEFT;
313 uint8_t p1 = mv[i].to + up_right;
314 uint8_t p2 = mv[i].to + up_left;
315 uint8_t a = OUT_OF_BOARD(p1) ? 0 : board.data[p1];
316 uint8_t b = OUT_OF_BOARD(p2) ? 0 : board.data[p2];
317 if( (COLOR_OF(a)==board.other_color && PIECE_OF(a)!=PAWN)
318 || (COLOR_OF(b)==board.other_color && PIECE_OF(b)!=PAWN) )
320 int x = board.move_see_val(mv[i]);
322 if(x>=0)
324 mv[i].val = 27000; /* pawn strike! */
325 //mv[i].extend = PLY; /* extend search */
326 continue;
330 #endif
332 #endif
333 if(mv[i].capture)
335 int x = board.move_see_val(mv[i]);
337 if(prev && prev->capture &&
338 (mv[i].to == prev->to) && (x >= Board::simple_values[PIECE_OF(prev->capture)]) )
340 mv[i].val = 29900;
341 if( (x == Board::simple_values[PIECE_OF(prev->capture)]) )
342 mv[i].extend = /*(x==1) ? (PLY/2) :*/ PLY;
343 // else if(prev->capture && ABS(x - Board::simple_values[PIECE_OF(prev->capture)]) == 1)
344 // mv[i].extend = PLY/2;
345 continue;
348 if(x>0)
350 if(orig_depth>-7*PLY && board.move_is_check(mv[i]) )
351 mv[i].val = 29800+x;
352 else
353 mv[i].val = 29000+x;
354 continue;
356 else if(x>=0 && orig_depth>-7*PLY && board.move_is_check(mv[i]) )
358 /* = capture but check */
359 mv[i].val = 29800;
360 continue;
363 else /* add a bonus for checks (but not too deep, or quiescence will get mad) */
364 if(orig_depth>-3*PLY && board.move_is_check(mv[i]) )
366 if(board.move_see_val(mv[i])>=0)
368 mv[i].val = 28799;
369 continue;
373 /* null-move threat */
374 if(null_threat[ply-1] == mv[i])
376 mv[i].val = 28500;
377 continue;
380 mv[i].val += history_hit[HISTORY_INDEX(mv[i])] * 1024 / (history_tot[HISTORY_INDEX(mv[i])] + 4);
382 #if CENTER_HEURISTIC
383 static int bof[128] = {
384 0,0,1,2,2,1,0,0,ENDL,
385 0,1,2,3,3,2,1,0,ENDL,
386 0,2,4,5,5,4,2,0,ENDL,
387 1,2,5,6,6,5,2,1,ENDL,
388 1,2,5,6,6,5,2,1,ENDL,
389 0,2,4,5,5,4,2,0,ENDL,
390 0,1,2,3,3,2,1,0,ENDL,
391 0,0,1,2,2,1,0,0,ENDL
394 /* add a bonus for moves towards the center */
395 mv[i].val += (bof[mv[i].to] - bof[mv[i].from]);
396 #endif //CENTER_HEURISTIC
399 if(hash_move!=-1)
400 mv[hash_move].val = 30000;
404 void
405 Engine::moves_quiescence_heuristic(Move *mv, int num_mv, const Move& best_mv_hash,
406 int static_eval, int alpha, int beta, int orig_depth, Move* prev)
408 for(int i=0;i<num_mv;i++)
410 mv[i].val = -10000;
411 mv[i].extend = 0;
413 /* give a high bonus to the move stored in the hash, if any.
414 mark only which is, don't continue, because some extensions
415 may be triggered ad added later (ie pawn strike, etc) */
416 if(mv[i].as_int == best_mv_hash.as_int)
418 mv[i].val = 30000;
419 continue;
422 #if 0
423 /* process strong pawn moves */
424 if(PIECE_OF(board.data[mv[i].from])==PAWN) /* pawn strike */
426 if( ROW_OF(mv[i].to) == board.seventh_rank[IS_WHITE(board.color_to_move)] )
428 int x = board.move_see_val(mv[i]);
430 if(x<0)
432 mv[i].val += -15000;
433 continue;
436 mv[i].val += 29499; /* 7th push */
437 mv[i].extend = PLY; /* extend search */
438 continue;
441 if( mv[i].flags == PROMOTEQUEEN )
443 int x = board.move_see_val(mv[i]);
445 if(x<0)
447 mv[i].val += -15000;
448 continue;
451 mv[i].val += 29500; /* promote! */
452 mv[i].extend = PLY; /* extend search */
453 continue;
456 #if 0
457 /* pawn strike */
458 uint8_t p1 = mv[i].to + up_right;
459 uint8_t p2 = mv[i].to + up_left;
460 uint8_t a = OUT_OF_BOARD(p1) ? 0 : board.data[p1];
461 uint8_t b = OUT_OF_BOARD(p2) ? 0 : board.data[p2];
462 if( (COLOR_OF(a)==board.other_color && PIECE_OF(a)!=PAWN)
463 || (COLOR_OF(b)==board.other_color && PIECE_OF(b)!=PAWN) )
465 int x = board.move_see_val(mv[i]);
466 if(x<0)
468 mv[i].val += -15000;
469 continue;
472 mv[i].val += 27000; /* pawn strike! */
473 mv[i].extend = PLY; /* extend search */
474 continue;
476 #endif
478 #endif
479 if(mv[i].capture)
481 int x = board.move_see_val(mv[i]);
483 if(prev && prev->capture &&
484 (mv[i].to == prev->to) && (x >= Board::simple_values[PIECE_OF(prev->capture)]) )
486 mv[i].val = 29900;
487 if( (x == Board::simple_values[PIECE_OF(prev->capture)]) )
488 mv[i].extend = /*(x==1) ? (PLY/2) :*/ PLY;
489 // else if(prev->capture && ABS(x - Board::simple_values[PIECE_OF(prev->capture)]) == 1)
490 // mv[i].extend = PLY/2;
491 continue;
494 if(x>0)
496 if(orig_depth>-5*PLY && board.move_is_check(mv[i]) )
497 mv[i].val = 18000+x;
498 else
499 mv[i].val = 10000+x;
500 continue;
502 else if(x>=0 && orig_depth>-5*PLY && board.move_is_check(mv[i]) )
504 /* = capture but check */
505 mv[i].val = 18000;
506 continue;
509 else /* add a bonus for checks (but not too deep, or quiescence will get mad) */
510 if(orig_depth>-3*PLY && board.move_is_check(mv[i]) )
512 if(board.move_see_val(mv[i])>=0)
514 mv[i].val = 7990;
515 continue;
522 /*******************************************************************************
523 The main alpha-beta recursive search function.
524 It handles both normal search (with or without null move)
525 and quiescence search, because i have having 2 almost identical
526 function around.
527 *******************************************************************************/
528 int16_t Engine::search(int ply, int depth, bool pv, int16_t alpha, int16_t beta)
530 SearchStack *s = &stack[ply];
531 int16_t best = -INF;
532 uint16_t cbest_mv_hash = 0; /* the compressed move from the hash */
533 Move best_mv_hash = Move::FromInt(0); /* the move from the hash */
534 int best_mv_found = -1; /* the index of the best move AFTER searching */
535 bool quiesce;
536 bool extended = false;
537 int16_t position_val;
539 if(depth <= 0)
540 STAT(quiesce_nodes)
542 s->base_depth = depth;
543 s->extensions = 0;
544 s->num_moves = 0;
545 s->curr_move = -1;
546 s->alpha = alpha;
547 s->beta = beta;
548 s->threat = Move::FromInt(0);
549 s->best_move = -1;
550 null_threat[ply] = Move::FromInt(0);
551 null_threat_dangerous[ply] = false;
552 //escape_from_1[ply] = escape_from_2[ply] = INVALID;
554 /* check if time is running out */
555 if(check_time())
556 return 0;
558 /* check for a draw for repetition or 50mvs. Of course the draw for
559 repetition must be checked BEFORE probing the hash :)*/
560 if(check_draw())
561 return (board.color_to_move == st_computer_color) ? -35 :
562 ((board.other_color == st_computer_color) ? 35 : 0); /* be aggressive! */
564 /*******************************************************************************
565 Probe the hashtable.
566 If the probe is succesful the hashtable will return us value
567 that can be exact, a lower bound or an upper bound, and if the
568 depth of the hashed search is >= the current depth this value
569 will be used to improve alpha and beta and possibly return immediatly.
570 The hastable will also give us a "best" move that will be searched
571 first.
572 This is the move that caused the "final" cutoff when this position
573 was searched previously. This best move is actually the index of the best
574 move in the array of generated moves (it is supposed to be deterministic :)
575 *******************************************************************************/
576 stat_hash_tot++;
577 HashEntry *h = probe_hash( board.hash );
579 if(h && (h->depth >= s->base_depth))// || ABS(h->value)>INF-1000) )
581 int16_t l = h->lower();
582 int16_t u = h->upper();
583 if(l>=beta || u==l)
584 return l;
585 if(u<=alpha)
586 return u;
588 beta = MIN(beta, u);
589 alpha = MAX(alpha, l);
592 if(h)
593 cbest_mv_hash = h->best_mv;
595 /*******************************************************************************
596 Test if we are under check, and if so extend search
597 *******************************************************************************/
599 s->under_check = board.under_attack(board.king_pos[IS_WHITE(board.color_to_move)],
600 board.other_color);
602 /*******************************************************************************
603 If it is time to quiesce, evaluate and test if we can exit
604 immediately with a beta cut-off (try first a rough eval - delta)
605 *******************************************************************************/
606 quiesce = ((!s->under_check) && (depth<=0)) || (depth<-60*PLY);
608 #if 0 //PAPOPEPO
609 if(quiesce && depth>=-PLY)
611 int num_mv;
612 Move *mv = mv_stack + mv_stack_top;
613 board.do_null_move();
614 num_mv = board.find_moves(mv);
615 uint8_t pup = INVALID;
617 for(int i=0;i<num_mv;i++)
619 if(!mv[i].capture || PIECE_OF(mv[i].capture)==PAWN)
620 continue;
621 if(mv[i].to != pup && board.move_see_val(mv[i])>0)
622 if(pup == INVALID)
623 pup = mv[i].to;
624 else
626 quiesce = false;
627 break;
631 board.undo_null_move();
633 #endif
635 if(quiesce)
637 stat_evaluate++;
638 best = board.evaluate(st_computer_color, alpha, beta);
640 alpha = MAX(alpha, best);
641 if(best >= beta)
643 stat_evaluate_cutoff++;
644 goto search_done;
647 if(ply>60)
648 goto search_done;
651 #if NULL_MOVE
652 /*******************************************************************************
653 Try the null move.
654 *******************************************************************************/
655 if(!s->under_check && (stack[ply-1].curr_move != -1) && depth >= 2*PLY && beta<WORST_MATE && null_move_ok())
657 int16_t val;
658 int sdepth = (depth >= 5*PLY) ? (depth-4*PLY) : depth-3*PLY;
660 stat_null_move++;
661 s->curr_move = -1;
662 current_ply++;
663 board.do_null_move();
664 val = -search( ply+1, sdepth, true, -beta, -beta+1);
665 board.undo_null_move();
666 current_ply--;
668 /* null move cut! */
669 if(val >= beta)
671 stat_null_cut++;
672 return val;
675 if(val < -WORST_MATE)
676 null_threat_dangerous[ply] = true;
677 #if 0
678 if(val<alpha-100 && /* we are facing a threat*/
679 stack[ply+1].best_move != -1) /* be sure we aren't reading random memory */
681 /* ok, officially the array stack[ply+1].moves has already
682 been deallocated, but who cares :) */
683 s->threat = stack[ply+1].moves[stack[ply+1].best_move];
685 #if 0
686 /* Botvinnik-Markoff extension!!! */
687 if(!extended && ply>=3 && (s->threat == stack[ply-2].threat) )
689 depth += 80;
690 extended = true;
692 #endif
694 #endif
696 #endif
700 /*******************************************************************************
701 Now generate the legal moves and look for a check/stalemate
702 *******************************************************************************/
704 /* generate all the legal moves */
705 s->moves = &mv_stack[mv_stack_top];
706 s->num_moves = board.find_moves(s->moves);
707 mv_stack_top += s->num_moves;
708 s->under_check = board.under_check;
710 if(s->under_check==2) /* double check */
712 depth += 2*PLY;
713 extended = true;
715 else if(s->under_check) /* simple check */
717 depth += PLY;
718 if(stack[ply-1].curr_move>=0 &&
719 !board.pins[stack[ply-1].moves[ /* last moved piece is not attacking the king */
720 stack[ply-1].curr_move].to]) /* so this is a discover check */
722 depth += PLY/2;
724 extended = true;
727 /* return now if the positon is terminal */
728 if(!s->num_moves)
730 if(s->under_check)
732 /* while mating, sacrify as much as possible :) */
733 best = -INF + ply;//*50 + board.material[IS_WHITE(eng_color)]/50;
735 else
736 best = 0;
737 goto search_done;
740 /* single-reply extension */
741 if(s->num_moves == 1 && !extended)
743 depth += PLY;
744 extended = true;
746 else if(s->num_moves <= 3 && !extended)
748 depth += PLY/2;
749 extended = true;
752 /*******************************************************************************
753 Sort the moves.
754 First comes the move from the hashtable, if avalable.
755 The remaining moves are sorted with a heuristic that keeps in account
756 the history heuristic (ie the moves that previously caused an alpha
757 cutoff), a MVV/LVA bonus value and a small bonus for moves that go
758 toward the center.
759 *******************************************************************************/
761 /* convert the move we got from the hash to the move structure */
762 if(cbest_mv_hash)
764 best_mv_hash = board.uncompress_move(cbest_mv_hash);
765 /* if it happened that the move we got from the hashtable
766 is not valid, simply no move will get the high
767 heuristic bonus value */
769 #if INTERNAL_ITERATIVE_DEEPENING
770 else if(s->base_depth>3*PLY && pv) /* don't do it only on the pv, or it will be useless :) */
772 s->curr_move = -2;
773 current_ply++;
774 int val = search(ply+1, s->base_depth-2*PLY, true, alpha, beta);
775 current_ply--;
777 HashEntry *h2 = probe_hash( board.hash );
778 if(h2 && h2->best_mv)
780 cbest_mv_hash = h2->best_mv;
781 best_mv_hash = board.uncompress_move(cbest_mv_hash);
784 #endif //INTERNAL_ITERATIVE_DEEPENING
786 /* for each move calculate the heuristic goodness value */
788 Move *prev = (stack[ply-1].curr_move>=0) ? &stack[ply-1].moves[stack[ply-1].curr_move] : NULL;
789 if(quiesce)
790 moves_quiescence_heuristic( s->moves, s->num_moves, best_mv_hash,
791 best, alpha, beta, s->base_depth, prev);
792 else
793 moves_heuristic( s->moves, s->num_moves, ply, s->base_depth,
794 best_mv_hash, quiesce, prev );
797 /* if quiesce rip-off the "non-critical" moves */
798 if(quiesce)
800 int n = 0;
801 for(int i=0;i<s->num_moves;i++)
802 if(s->moves[i].val>0)
803 s->moves[n++] = s->moves[i];
804 mv_stack_top -= s->num_moves-n;
805 s->num_moves = n;
808 /* Don't do it now, do it incrementally */
809 //sort_moves( s->moves, s->num_moves );
812 #if EARLY_TRANSP_CUTOFF
813 /*******************************************************************************
814 Try to get an early beta cutoff using the hash table values
815 of the following moves, and improve alpha too.
816 Try on the first 6 value of the ordered moves (argh, looking into the
817 hashtable is very expensive because of the cache!!!!!!!!)
818 *******************************************************************************/
820 if(depth >= 3*PLY)
822 HashKey hk = board.move_hash(s->moves[0]);
823 for(int i=1;i<s->num_moves;i++)
825 prefetch_hash(hk);
826 HashKey newhk = board.move_hash(s->moves[i]);
827 HashEntry *h2 = probe_hash( hk );
828 hk = newhk;
830 if(h2 && h2->depth >= depth-PLY)
832 if(-h2->up >= beta)
834 best = -h2->up;
835 goto search_done;
837 alpha = MAX(alpha, (int16_t)-h2->up);
841 HashEntry *h2 = probe_hash( hk );
842 if(h2 && h2->depth >= depth-PLY)
844 if(-h2->up >= beta)
846 best = -h2->up;
847 goto search_done;
849 alpha = MAX(alpha, (int16_t)-h2->up);
852 #endif //EARLY_TRANSP_CUTOFF
854 /* set the current best move index (as will be saved in the hash) */
855 best_mv_found = 0;
857 /*******************************************************************************
858 It is now time to loop all the successor moves and search recursively.
859 *******************************************************************************/
861 #if FUTILITY
862 /* calcluate the evaluation (required by fitility pruning) */
863 position_val = quiesce ? best : board.evaluate( st_computer_color, -INF, INF);
864 #endif
866 for(int i=0;i<s->num_moves;i++)
868 int16_t val;
869 int sdepth = depth-100;
871 /* sort moves incrementally, in the hope of a beta cut */
872 incremental_sort_moves(s->moves+i, s->num_moves-i);
874 if(depth < s->base_depth+100)
875 sdepth += s->moves[i].extend; /* extensions calculated during the heuristic sort */
877 #if FUTILITY
878 /* futility pruning, it is done only if we are no under check
879 and the move is not a "critical" move */
880 if(depth>0 && depth<3*PLY && !s->under_check && s->moves[i].val < 28000)
882 static const int mavala[] = { 0, 490, 315, 980, 315, 100, 0 };
884 int16_t fmargin = (depth <= PLY ? 420 : 950);
885 int16_t fval = position_val + mavala[PIECE_OF(s->moves[i].capture)];
886 if(fval < alpha-fmargin)
887 continue;
889 #endif
891 /* collect history statistics */
892 if(s->moves[i].val<28000)
894 int ix = HISTORY_INDEX(s->moves[i]);
895 if(history_tot[ix] > 1024)
897 history_tot[ix] = history_tot[ix]*7/8;
898 history_hit[ix] = history_hit[ix]*7/8;
900 history_tot[ix]++;
903 #if 0
904 if(!quiesce && PIECE_OF(board.data[s->moves[i].from]) == PAWN && s->moves[i].val == 27000) //FIXME, UGLY
906 escape_from_1[ply] = s->moves[i].to + Board::up_dir[IS_WHITE(board.color_to_move)] + RIGHT;
907 escape_from_2[ply] = s->moves[i].to + Board::up_dir[IS_WHITE(board.color_to_move)] + LEFT;
909 else
911 escape_from_1[ply] = escape_from_2[ply] = 0;
913 #endif
915 s->curr_move = i;
916 current_ply++;
917 board.do_move(s->moves[i]);
920 uint64_t q;
921 if(s->base_depth > 0 && sdepth <= 0)
923 STAT(quiesce_called);
924 q = stat_quiesce_nodes;
927 #if NEGA_SCOUT /* use negascout, (null window search for nodes that are not in the pv) */
928 if(i == 0) // || depth<200)
929 val = -search( ply+1, sdepth, pv, -beta, -alpha );
930 else
932 #if LATE_MOVE_REDUCTION
933 /* history pruning, if this is not a "critical" move and the failhigh
934 stats are too low, try a reduced depth search (if it returns >alpha,
935 re-do the full depth search) */
936 // if((sdepth>0) && !s->under_check && depth<=3*PLY && s->moves[i].val<28000 &&
937 // (history_hit[HISTORY_INDEX(s->moves[i])]+1)*3
938 // < (history_tot[HISTORY_INDEX(s->moves[i])]+1))
939 if((sdepth>0) && !s->under_check && s->moves[i].val<28000 && i>=7 )
941 val = -search( ply+1, sdepth - 60, false, -alpha-1, -alpha );
942 if(val <= alpha)
943 goto skip_search; /* reduced search was effective */
945 #endif
946 val = -search( ply+1, sdepth, false, -alpha-1, -alpha );
947 if((val>alpha) && pv)
948 val = -search( ply+1, sdepth, true, -beta, -alpha );
950 #else
951 /* normal full width alpha-beta */
952 val = -search( ply+1, sdepth, pv, -beta, -alpha );
953 #endif
955 if(s->base_depth > 0 && sdepth <= 0)
957 q = stat_quiesce_nodes-q;
958 if(q > stat_max_quiesce_nodes)
960 stat_max_quiesce_nodes = q;
961 max_quiesce_board = board;
965 skip_search:
966 board.undo_move(s->moves[i]);
967 current_ply--;
969 /* update the current best value and check for and alpha cut */
970 best = MAX(best, val);
971 if(best > alpha)
973 best_mv_found = i;
974 s->best_move = i;
976 /* alpha cut! */
977 alpha = best;
980 /* beta cut! */
981 if(best >= beta)
982 break;
985 /* update a few stats */
986 if(!quiesce)
988 if(best_mv_found==0)
990 if(best >= beta)
991 stat_best_cut++;
992 else
993 stat_best_first++;
995 stat_search_moves++;
996 if(ply==0 && depth>100)
998 if(best_mv_found==0)
1000 if(best >= beta)
1001 stat_best_cut0++;
1002 else
1003 stat_best_first0++;
1005 stat_search_moves0++;
1009 /* collect statistics for the history */
1010 if(best >= beta &&
1011 !s->moves[best_mv_found].capture &&
1012 !(s->moves[best_mv_found].flags>=PROMOTE_FIRST))
1014 history_hit[HISTORY_INDEX(s->moves[best_mv_found])]++;
1015 history_tot[HISTORY_INDEX(s->moves[best_mv_found])]++;
1018 search_done:
1019 mv_stack_top -= s->num_moves; /* free the moves we allocated */
1021 /* this is a null move, save what the threat is */
1022 if(stack[ply-1].curr_move == -1 && best_mv_found >= 0)
1023 null_threat[ply-1] = s->moves[best_mv_found];
1025 /* if we found a best move searching, that move will be saved.
1026 if we did no search (ie quiescence), save the old hash value,
1027 or -1 if no hash entry had been found */
1028 int bestmv = cbest_mv_hash;
1029 if(best_mv_found >= 0)
1030 bestmv = board.compress_move(s->moves[best_mv_found]);
1032 /* write the value in the hash, with the index of the best move */
1033 write_hash( best > s->alpha ? MIN(best, beta) : -INF,
1034 best < beta ? MAX(best, s->alpha) : +INF,
1035 MAX(s->base_depth,-500), bestmv);
1037 return best;
1041 Move Engine::find_best_move()
1043 int num_mate_hits = 0;
1044 SearchStack *s = &stack[0];
1045 Move retv;
1046 Move result[80];
1048 ASSERT(!thinking);
1050 /* initialize the root node */
1051 current_ply = 0;
1052 s->base_depth = 100;
1053 s->extensions = 0;
1054 s->curr_move = -1;
1055 s->alpha = -INF;
1056 s->beta = INF;
1057 s->threat = Move::FromInt(0);
1058 s->best_move = -1;
1060 s->moves = mv_stack;
1061 s->num_moves = mv_stack_top = board.find_moves(s->moves);
1063 /* calculate how much time we will think*/
1064 start_think_time = current_time();
1065 if( analysis_limit == TIME_LIMIT )
1067 if(board.color_to_move == st_computer_color)
1068 calc_best_time();
1069 else /* pondering? analysing? */
1070 time_best_csec = 99999999;
1071 max_think_time = start_think_time + time_best_csec - 2;
1074 /* to print the analysis */
1075 if(post)
1076 output("\tply\tscore\ttime\tnodes\tpv\n");
1078 /* return immediatly if the move is forced. */
1079 if(s->num_moves==1)
1080 return s->moves[0];
1082 /* probe the play lines */
1083 if( eng_status == PLAYING
1084 && st_computer_color == board.color_to_move
1085 && probe_lines( board.hash, &retv))
1087 retv.val = 0;
1088 return retv;
1091 /* probe the book */
1092 if(probe_book( board.hash, &retv)) {
1093 for(int i=0;i<s->num_moves++;i++)
1094 if(retv.as_int == s->moves[i].as_int)
1096 retv.val = 0;
1097 return retv;
1099 output("Error!!! invalid move in the book!!!\n");
1102 #if 0
1103 prof_eval = 0;
1104 prof_find_moves = 0;
1105 prof_find_captures = 0;
1106 prof_heuristic = 0;
1107 prof_sort_moves = 0;
1108 prof_move_is_check = 0;
1109 prof_move_see_val = 0;
1110 prof_tot = rdtsc();
1111 #endif
1113 stat_early_transp = 0;
1114 stat_hash_tot = 0;
1115 stat_hash_ex = 0;
1116 stat_hash_low = 0;
1117 stat_hash_upp = 0;
1118 stat_best_first = 0;
1119 stat_best_cut = 0;
1120 stat_search_moves = 0;
1121 stat_best_first0 = 0;
1122 stat_best_cut0 = 0;
1123 stat_search_moves0 = 0;
1124 stat_evaluate = 0;
1125 stat_evaluate_cutoff = 0;
1126 stat_null_move = 0;
1127 stat_null_cut = 0;
1128 stat_hash_write = 0;
1130 reset_stats();
1131 //reset_hash();
1133 //analysis_color = board.color_to_move;
1134 processed_nodes = 0;
1135 int16_t best_guess = 0;
1136 thinking = true;
1137 make_old_hash();
1139 /* set the back jump for the quick thinking exit */
1140 if(setjmp(back))
1141 goto exit_thinking;
1143 /* start with a guess of 0 */
1144 s->moves[0].val = 0;
1145 retv = s->moves[0];
1147 /* do the iterative deepening thing. */
1148 while(1)
1150 int16_t alpha = -INF;
1151 int i=0;
1153 memset( history_tot, 0, HISTORY_SIZE*sizeof(uint16_t));
1154 memset( history_hit, 0, HISTORY_SIZE*sizeof(uint16_t));
1156 /* save the result of the previous search */
1157 result[s->base_depth/PLY-1] = s->moves[0];
1159 /* for each move call the alpha-beta search function */
1160 int best_mv = 0;
1161 for(i=0;i<s->num_moves;i++)
1163 s->curr_move = i;
1164 current_ply++;
1165 board.do_move(s->moves[i]);
1166 #if NEGA_SCOUT
1167 if(i == 0)
1169 s->moves[i].val = -search( 1, s->base_depth-PLY, true, -INF, -alpha );
1171 else
1173 s->moves[i].val = -search( 1, s->base_depth-PLY, false, -alpha-1, -alpha );
1174 if(s->moves[i].val > alpha)
1175 s->moves[i].val = -search( 1, s->base_depth-PLY, true, -INF, -alpha );
1177 #else
1178 s->moves[i].val = -search( 1, s->base_depth-100, true, -INF, -alpha );
1179 #endif
1180 board.undo_move(s->moves[i]);
1181 current_ply--;
1184 /* cut alpha */
1185 if(s->moves[i].val > alpha)
1187 alpha = s->moves[i].val;
1188 retv = s->moves[i];
1189 best_mv = i;
1190 s->best_move = i;
1192 /* this move caused an alpha cut, so print the new line */
1193 if( post /*&& processed_nodes>100000*/)
1195 output("\t%d\t%d\t%d\t%llu\t", s->base_depth/100,
1196 s->moves[i].val, current_time() - start_think_time, processed_nodes);
1197 print_moves(&s->moves[i], 1, true);
1202 best_guess = alpha;
1204 /* the search is done, sort the moves so that best is searched first */
1205 if(i>1)
1207 s->moves[best_mv].val++;
1208 sort_moves(s->moves, i); /* sort i moves (those that we where able to search) */
1209 s->moves[0].val--;
1212 /* print the result of the analysis at this depth */
1213 if( post /*&& processed_nodes>100000*/)
1215 output("\t%d\t%d\t%d\t%llu\t", s->base_depth/100,
1216 s->moves[0].val, current_time() - start_think_time, processed_nodes);
1217 print_moves(&s->moves[0], 1, true);
1220 /* max depth */
1221 if( s->base_depth >= 40*PLY )
1222 break;
1224 /* return in case of fixed depth search */
1225 if( eng_status == PLAYING && st_computer_color == board.color_to_move &&
1226 analysis_limit == DEPTH_LIMIT && s->base_depth == st_depth*PLY)
1227 break;
1229 /* return if 3/5 of time is gone (we can't search another ply anyway) */
1230 if( eng_status == PLAYING && st_computer_color == board.color_to_move &&
1231 analysis_limit == TIME_LIMIT &&
1232 (current_time()-start_think_time) >= (time_best_csec*3/5) )
1233 break;
1235 /* if a checkmate was detected return immediately */
1236 if( ABS(alpha) > WORST_MATE)
1238 num_mate_hits++;
1239 if(num_mate_hits >= 5)
1240 break;
1243 s->base_depth += PLY;
1246 exit_thinking:
1248 if(post)
1250 #if 0
1251 prof_tot = rdtsc() - prof_tot;
1252 #define SHOW_PROF(a) output("@"#a": %llu (%llu%%)\n", \
1253 prof_##a, prof_##a*100/prof_tot);
1254 SHOW_PROF(tot);
1255 SHOW_PROF(eval);
1256 SHOW_PROF(do_move);
1257 SHOW_PROF(find_moves);
1258 SHOW_PROF(find_captures);
1259 SHOW_PROF(heuristic);
1260 SHOW_PROF(move_is_check);
1261 SHOW_PROF(move_see_val);
1262 SHOW_PROF(sort_moves);
1263 #endif
1264 output("#nodes: %llu (~%llu nps)\n", processed_nodes, (processed_nodes*100)/
1265 MAX(current_time()-start_think_time,1) );
1266 output("# %d times all the moves were seached (non-leaf non-quiesce node)\n",
1267 stat_search_moves);
1268 output("# of which %d times first move caused a cutoff\n", stat_best_cut);
1269 output("# of the remaining %d times first move was really best (%02d%%)\n",
1270 stat_best_first, (stat_best_first*100)/MAX(stat_search_moves-stat_best_cut, 1));
1271 output("# evaluate was called %d times (quiescence node)\n", stat_evaluate);
1272 output("# of which %d times caused an early cutoff (leaf node)\n",
1273 stat_evaluate_cutoff);
1274 output("#hash: %d writes, %d probes (%d succesful, =%d, >%d, <%d)\n",
1275 stat_hash_write, stat_hash_tot, stat_hash_ex+stat_hash_low+stat_hash_upp,
1276 stat_hash_ex, stat_hash_low, stat_hash_upp);
1277 output("#etc: %d\n", stat_early_transp);
1278 output("#null move: %d (%d succesful)\n", stat_null_move, stat_null_cut);
1280 print_stats();
1281 char buf[256];
1282 max_quiesce_board.write_board(buf);
1283 output("#max quiesce board: %s [%d %d]\n", buf, max_quiesce_alpha, max_quiesce_beta);
1286 thinking = false;
1287 return retv;