1 /***************************************************************************
2 search.cpp - description
4 begin : Wed Mar 13 2002
5 copyright : (C) 2002-2005 by Monge Maurizio
6 email : monge@linuz.sns.it
7 ***************************************************************************/
9 /***************************************************************************
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. *
16 ***************************************************************************/
20 #include "search_gui.h"
32 S(max_quiesce_nodes); \
36 S(quiesce_best_can_be_first); \
37 S(quiesce_best_was_first); \
39 S(quiesce_cutoff_first); \
42 S(search_best_can_be_first); \
43 S(search_best_was_first); \
45 S(search_cutoff_first); \
49 #define STAT(x) stat_##x++;
51 #define S(x) uint64_t stat_##x;
53 Board max_quiesce_board
;
54 int16_t max_quiesce_alpha
;
55 int16_t max_quiesce_beta
;
60 #define S(x) stat_##x = 0;
67 #define S(x) printf("# " #x " = %llu\n", (unsigned long long)stat_##x);
75 /* enable the nega-scout pv-search */
78 /* enable the null move */
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: */
95 /* when the hashtable provides no "best" move, do a depth-2 search */
96 #define INTERNAL_ITERATIVE_DEEPENING 0
98 /* use the history sorting heuristic */
99 #define HISTORY_HEURISTIC 1
101 /* improve the sorting heuristic for pawn strikes */
102 #define PAWN_STRIKE 1
104 /* improve the sorting heuristic for moves to the center */
105 #define CENTER_HEURISTIC 0
108 #define IGNORE_ALLBAD_NODES 0
111 void Engine::print_stat()
114 if(eng_status
!= ANALYZING
)
116 printf("Thinking: ");
117 for(int i
=0; i
<current_ply
; i
++)
119 if(stack
[i
].curr_move
== -2)
120 continue; //Internal iterative deepening
121 else if(stack
[i
].curr_move
== -1)
124 board
.do_null_move();
129 printf("%s", move_to_alg(buf
, &(stack
[i
].moves
[stack
[i
].curr_move
]) ) );
130 board
.do_move(stack
[i
].moves
[stack
[i
].curr_move
]);
132 printf((i
!= current_ply
-1) ? " " : "\n");
141 output("stat01: %d %llu %d %d %d %s\n",
142 current_time() - start_think_time
,
143 (unsigned long long)processed_nodes
,
144 stack
[0].base_depth
/100,
145 stack
[0].num_moves
-1-stack
[0].curr_move
,
147 move_to_alg(buf
, &(stack
[0].moves
[stack
[0].curr_move
]) ) );
150 output("stat01: 0 0 0 0 0\n");
154 bool SearchRoot::null_move_ok()
156 int c
= IS_WHITE(board
.color_to_move
) ? 5 : -1;
157 int numpawns
= board
.mat_tracking
[c
+PAWN
].count
;
158 int numpieces
= board
.mat_tracking
[c
+KNIGHT
].count
+ board
.mat_tracking
[c
+BISHOP
].count
159 + board
.mat_tracking
[c
+ROOK
].count
+ board
.mat_tracking
[c
+QUEEN
].count
;
162 if(numpieces
>= 1 && numpawns
>= 2)
168 SearchRoot::moves_heuristic(Move
*mv
, int num_mv
, bool pv
, int ply
, int orig_depth
,
169 Move best_mv_hash
, bool quiesce
, Move
* prev
, int* overall_extensions
,
178 for(int i
=0;i
<num_mv
;i
++)
183 /* give a high bonus to the move stored in the hash, if any.
184 mark only which is, don't continue, because some extensions
185 may be triggered ad added later (ie pawn strike, etc) */
186 if(mv
[i
].as_int
== best_mv_hash
.as_int
)
190 if(num_escapes
<=2 && PIECE_OF(board
.data
[mv
[i
].from
]) != PAWN
&&
191 (mv
[i
].from
== stack
[ply
-1].escape_from
[1] || stack
[ply
-1].escape_from
[2]) )
193 int x
= board
.move_see_val(mv
[i
]);
198 escapes
[num_escapes
] = i
;
204 /* process strong pawn moves */
205 if(PIECE_OF(board
.data
[mv
[i
].from
])==PAWN
) /* pawn strike */
207 uint8_t x
= X(mv
[i
].to
);
208 uint8_t y
= IS_WHITE(board
.color_to_move
) ? Y(mv
[i
].to
) : 7-Y(mv
[i
].to
);
210 if( mv
[i
].flags
== PROMOTEQUEEN
|| y
== 6)
212 int x
= board
.move_see_val(mv
[i
]);
216 mv
[i
].val
+= mv
[i
].flags
? 29600 : 29599; /* promote! */
217 mv
[i
].extend
= PLY
; /* extend search */
224 int other
= IS_WHITE(board
.other_color
);
226 if((!board
.line_pawns
[other
][x
].count
|| board
.line_pawns
[other
][x
].pos
[0] > pos
)
227 && (x
==0 || !board
.line_pawns
[other
][x
-1].count
|| board
.line_pawns
[other
][x
-1].pos
[0] >= pos
)
228 && (x
==7 || !board
.line_pawns
[other
][x
+1].count
|| board
.line_pawns
[other
][x
+1].pos
[0] >= pos
))
230 int x
= board
.move_see_val(mv
[i
]);
234 mv
[i
].val
+= y
>= 5 ? 29550 : 29500; /* passed pawn advancing! */
236 mv
[i
].extend
= PLY
; /* extend search */
243 if(orig_depth
>= 2*PLY
)
246 uint8_t up_right
= Board::up_dir
[IS_WHITE(board
.color_to_move
)] + RIGHT
;
247 uint8_t up_left
= Board::up_dir
[IS_WHITE(board
.color_to_move
)] + LEFT
;
248 uint8_t p1
= mv
[i
].to
+ up_right
;
249 uint8_t p2
= mv
[i
].to
+ up_left
;
250 uint8_t a
= OUT_OF_BOARD(p1
) ? 0 : board
.data
[p1
];
251 uint8_t b
= OUT_OF_BOARD(p2
) ? 0 : board
.data
[p2
];
252 if( (COLOR_OF(a
)==board
.other_color
&& PIECE_OF(a
)!=PAWN
)
253 || (COLOR_OF(b
)==board
.other_color
&& PIECE_OF(b
)!=PAWN
) )
255 int x
= board
.move_see_val(mv
[i
]);
259 mv
[i
].val
= 27000; /* pawn strike! */
268 int x
= board
.move_see_val(mv
[i
]);
271 if(prev
&& prev
->capture
&&
272 (mv
[i
].to
== prev
->to
) && (x
>= Board::simple_values
[PIECE_OF(prev
->capture
)]) )
275 if( (x
== Board::simple_values
[PIECE_OF(prev
->capture
)]) )
282 if(orig_depth
>-7*PLY
&& board
.move_is_check(mv
[i
]) )
288 else if(x
>=0 && orig_depth
>-7*PLY
&& board
.move_is_check(mv
[i
]) )
290 /* = capture but check */
295 else /* add a bonus for checks (but not too deep, or quiescence will get mad) */
296 if(board
.move_is_check(mv
[i
]) )
298 if(board
.move_see_val(mv
[i
])>=0)
305 /* null-move threat */
306 if(stack
[ply
-1].null_threat
== mv
[i
])
313 mv
[i
].val
+= (history_hit
[HISTORY_INDEX(mv
[i
])] + 32) * (1024/3) / (history_tot
[HISTORY_INDEX(mv
[i
])] + 64);
315 int mx
= CAPT_INDEX(mv
[i
]);
318 int ix
= FAST_PLUTO(p1
, mx
);
319 mv
[i
].val
+= (pluto_hit
[ix
] + 24) * (1024/3) / (pluto_tot
[ix
] + 48);
325 int ix
= FAST_PLUTO(p2
, mx
);
326 mv
[i
].val
+= (pluto2_hit
[ix
] + 16) * (1024/3) / (pluto2_tot
[ix
] + 32);
331 mv
[i
].val
+= (history_hit
[HISTORY_INDEX(mv
[i
])] + 32) * 256*4 / (history_tot
[HISTORY_INDEX(mv
[i
])] + 64);
335 static int bof
[128] = {
336 0,0,1,2,2,1,0,0,ENDL
,
337 0,1,2,3,3,2,1,0,ENDL
,
338 0,2,4,5,5,4,2,0,ENDL
,
339 1,2,5,6,6,5,2,1,ENDL
,
340 1,2,5,6,6,5,2,1,ENDL
,
341 0,2,4,5,5,4,2,0,ENDL
,
342 0,1,2,3,3,2,1,0,ENDL
,
346 /* add a bonus for moves towards the center */
347 mv
[i
].val
+= bof
[mv
[i
].to
];
348 //mv[i].val += (bof[mv[i].to] - bof[mv[i].from]);
349 #endif //CENTER_HEURISTIC
353 if((stack
[ply
-1].escape_from
[1] != INVALID
|| stack
[ply
-1].escape_from
[2] != INVALID
) && num_escapes
<= 2)
355 for(int i
=0;i
<num_escapes
;i
++)
356 mv
[escapes
[i
]].extend
= PLY
;
361 mv
[hash_move
].val
= 30000;
366 SearchRoot::moves_quiescence_heuristic(Move
*mv
, int num_mv
, const Move
& best_mv_hash
,
367 int static_eval
, int alpha
, int beta
, int orig_depth
, Move
* prev
)
370 for(int i
=0;i
<num_mv
;i
++)
375 /* give a high bonus to the move stored in the hash, if any.
376 mark only which is, don't continue, because some extensions
377 may be triggered ad added later (ie pawn strike, etc) */
378 if(mv
[i
].as_int
== best_mv_hash
.as_int
)
381 /* process strong pawn moves */
382 if(PIECE_OF(board
.data
[mv
[i
].from
])==PAWN
)
384 if( ROW_OF(mv
[i
].to
) == board
.seventh_rank
[IS_WHITE(board
.color_to_move
)] )
386 int x
= board
.move_see_val(mv
[i
]);
390 mv
[i
].val
+= 29499; /* 7th push */
391 mv
[i
].extend
= PLY
; /* extend search */
396 if( mv
[i
].flags
== PROMOTEQUEEN
)
398 int x
= board
.move_see_val(mv
[i
]);
402 mv
[i
].val
+= 29500; /* promote! */
403 mv
[i
].extend
= PLY
; /* extend search */
411 int x
= board
.move_see_val(mv
[i
]);
413 if(prev
&& prev
->capture
&&
414 (mv
[i
].to
== prev
->to
) && (x
>= Board::simple_values
[PIECE_OF(prev
->capture
)]) )
416 mv
[i
].val
= 29900 + x
;
417 if( (x
== Board::simple_values
[PIECE_OF(prev
->capture
)]) )
422 if( x
>0 && ((orig_depth
>-2*PLY
|| static_eval
==-INF
) ? true : x
*100+static_eval
+200>alpha
) )
427 else if((x
>=0 && orig_depth
>-4*PLY
) && board
.move_is_check(mv
[i
]) )
429 /* = capture but check */
434 else /* add a bonus for checks (but not too deep, or quiescence will get mad) */
435 if(orig_depth
>-2*PLY
&& board
.move_is_check(mv
[i
]) && board
.move_see_val(mv
[i
])>=0)
442 if(hash_move
>= 0 && mv
[hash_move
].val
> 9000)
443 mv
[hash_move
].val
= 30000;
447 /*******************************************************************************
448 The main alpha-beta recursive search function.
449 It handles both normal search (with or without null move)
450 and quiescence search, because i have having 2 almost identical
452 *******************************************************************************/
453 int16_t SearchRoot::search(int ply
, int base_depth
, bool pv
, int16_t orig_alpha
, int16_t beta
, bool expect_allbad
)
455 SearchStack
*s
= &stack
[ply
];
457 uint16_t cbest_mv_hash
= 0; /* the compressed move from the hash */
458 Move best_mv_hash
= Move::FromInt(0); /* the move from the hash */
459 int best_mv_found
= -1; /* the index of the best move AFTER searching */
461 bool extended
= false;
462 bool no_good_moves
= false;
463 int16_t lower_bound
= -INF
;
464 int16_t position_val
;
465 int depth
= base_depth
;
466 int16_t alpha
= orig_alpha
;
469 #if IGNORE_ALLBAD_NODES
470 expect_allbad
= false;
476 for(int i
=ply
-1;i
>=0;i
--)
478 if(stack
[i
].curr_move
== -1)
479 board
.undo_null_move(stack
[i
].save_buf
);
480 else if(stack
[i
].curr_move
>= 0)
481 board
.undo_move(stack
[i
].moves
[stack
[i
].curr_move
], stack
[i
].save_buf
);
483 printf("[FEN \"%s\"]\n", board
.to_fen(BigStr().data()) );
484 for(int i
=0;i
<ply
;i
++)
486 printf(" %s", stack
[i
].curr_move
<= -1 ? "NULL" :
487 board
.move_to_alg(TmpStr().data(), stack
[i
].moves
[stack
[i
].curr_move
]));
488 if(stack
[i
].curr_move
== -1)
489 board
.do_null_move(stack
[i
].save_buf
);
490 else if(stack
[i
].curr_move
>= 0)
491 board
.do_move(stack
[i
].moves
[stack
[i
].curr_move
], stack
[i
].save_buf
);
499 // STAT(quiesce_nodes)
504 s
->null_threat
= Move::FromInt(0);
505 s
->null_threat_dangerous
= false;
507 stack
[ply
].escape_from
[1] = stack
[ply
].escape_from
[2] = INVALID
;
510 /* check if time is running out */
511 engine
->check_time();
513 /* check for a draw for repetition or 50mvs. Of course the draw for
514 repetition must be checked BEFORE probing the hash :)*/
516 return (board
.color_to_move
== engine
->st_computer_color
) ? engine
->v_eval_draw
:
517 ((board
.other_color
== engine
->st_computer_color
) ? -engine
->v_eval_draw
: 0); /* be aggressive! */
519 /*******************************************************************************
521 If the probe is succesful the hashtable will return us value
522 that can be exact, a lower bound or an upper bound, and if the
523 depth of the hashed search is >= the current depth this value
524 will be used to improve alpha and beta and possibly return immediatly.
525 The hastable will also give us a "best" move that will be searched
527 This is the move that caused the "final" cutoff when this position
528 was searched previously. This best move is actually the index of the best
529 move in the array of generated moves (it is supposed to be deterministic :)
530 *******************************************************************************/
532 HashEntry
*h
= engine
->probe_hash( board
.hash
);
535 GUI(notify_hash(ply
, h
->lo
, h
->up
, h
->depth
, alpha
, beta
,
536 h
->best_mv
? board
.uncompress_move(h
->best_mv
) : Move::None(), false,
537 (((h
->depth
>= base_depth
) && (h
->lo
>=beta
|| h
->up
==h
->lo
|| h
->up
<=alpha
))
538 ? SearchGui::Bold
: 0) | SearchGui::Magenta
) );
541 if(h
&& (h
->depth
>= base_depth
))// || ABS(h->value)>INF-1000) )
543 int16_t l
= h
->lower();
544 int16_t u
= h
->upper();
552 best
= alpha
= MAX(alpha
, l
);
556 cbest_mv_hash
= h
->best_mv
;
558 /*******************************************************************************
559 Test if we are under check, and if so extend search
560 *******************************************************************************/
562 s
->under_check
= board
.under_attack(board
.king_pos
[IS_WHITE(board
.color_to_move
)],
565 /*******************************************************************************
566 If it is time to quiesce, evaluate and test if we can exit
567 immediately with a beta cut-off (try first a rough eval - delta)
568 *******************************************************************************/
569 quiesce
= ((!s
->under_check
) && (depth
<=0)) || (ply
> 70);
574 if(quiesce
&& depth
>=-PLY
)
577 Move
*mv
= mv_stack
+ mv_stack_top
;
578 board
.do_null_move();
579 num_mv
= board
.find_moves(mv
);
580 uint8_t pup
= INVALID
;
582 for(int i
=0;i
<num_mv
;i
++)
584 if(!mv
[i
].capture
|| PIECE_OF(mv
[i
].capture
)==PAWN
)
586 if(mv
[i
].to
!= pup
&& board
.move_see_val(mv
[i
])>0)
596 board
.undo_null_move();
600 if(quiesce
&& (best
== -INF
|| ply
>70) )
602 best
= board
.evaluate(engine
->st_computer_color
, alpha
, beta
);
603 lower_bound
= best
; //we have at the very least "best" as lower bound now.
604 GUI(notify_eval(ply
, best
, alpha
, beta
, best
>=beta
? SearchGui::Blue
|SearchGui::Bold
: SearchGui::Blue
));
606 alpha
= MAX(alpha
, best
);
614 if(quiesce
&& h
&& h
->no_good_moves
)
618 /*******************************************************************************
620 *******************************************************************************/
621 if(!expect_allbad
&& !pv
&& !s
->under_check
&& (stack
[ply
-1].curr_move
!= -1)
622 && depth
>= 2*PLY
&& beta
<WORST_MATE
&& null_move_ok())
625 int sdepth
= (depth
>= 5*PLY
) ? (depth
-4*PLY
) : depth
-3*PLY
;
628 board
.do_null_move(s
->save_buf
);
629 GUI1(notify(&board
, Move::None(), ply
, sdepth
, -INF
, beta
-1, beta
, SearchGui::Green
));
630 val
= -search( ply
+1, sdepth
, false, -beta
, -beta
+NULL_SOLIDITY_MARGIN
, false);
631 GUI2(notify_value(ply
, val
, DIFF_NODES
, SearchGui::Bold
));
632 board
.undo_null_move(s
->save_buf
);
638 #if NULL_SOLIDITY_MARGIN > 1
639 if(depth
<= 3*PLY
&& val
> beta
- NULL_SOLIDITY_MARGIN
)
643 if(val
< -WORST_MATE
)
644 s
->null_threat_dangerous
= true;
647 if(val
<alpha
-100 && /* we are facing a threat*/
648 stack
[ply
+1].best_move
!= -1) /* be sure we aren't reading random memory */
650 /* ok, officially the array stack[ply+1].moves has already
651 been deallocated, but who cares :) */
652 s
->threat
= stack
[ply
+1].moves
[stack
[ply
+1].best_move
];
655 /* Botvinnik-Markoff extension!!! */
656 if(!extended
&& ply
>=3 && (s
->threat
== stack
[ply
-2].threat
) )
669 /*******************************************************************************
670 Now generate the legal moves and look for a check/stalemate
671 *******************************************************************************/
673 /* generate all the legal moves */
674 s
->moves
= &mv_stack
[mv_stack_top
];
675 s
->num_moves
= board
.find_moves(s
->moves
);
676 mv_stack_top
+= s
->num_moves
;
677 s
->under_check
= board
.under_check
;
679 pluto1
= stack
[ply
-1].best_move
>=0 ? PLUTO1(stack
[ply
-1].moves
[stack
[ply
-1].best_move
]) : -1;
680 pluto2
= (ply
>=2 && stack
[ply
-1].best_move
>=0 && stack
[ply
-2].best_move
>=0) ?
681 PLUTO2(stack
[ply
-2].moves
[stack
[ply
-2].best_move
], stack
[ply
-1].moves
[stack
[ply
-1].best_move
]) : -1;
683 if(s
->under_check
==2) /* double check */
688 else if(s
->under_check
) /* simple check */
691 if(stack
[ply
-1].curr_move
>=0 &&
692 !board
.pins
[stack
[ply
-1].moves
[ /* last moved piece is not attacking the king */
693 stack
[ply
-1].curr_move
].to
]) /* so this is a discover check */
700 /* return now if the positon is terminal */
705 /* while mating, sacrify as much as possible :) */
706 int mt
= IS_WHITE(board
.other_color
) ? 5 : -1;
707 int16_t matval
= Board::simple_values
[PAWN
]*board
.mat_tracking
[mt
+PAWN
].count
+
708 Board::simple_values
[KNIGHT
]*board
.mat_tracking
[mt
+KNIGHT
].count
+
709 Board::simple_values
[BISHOP
]*board
.mat_tracking
[mt
+BISHOP
].count
+
710 Board::simple_values
[QUEEN
]*board
.mat_tracking
[mt
+QUEEN
].count
;
711 best
= alpha
= beta
= -INF
+ ply
*100 + matval
;
714 best
= alpha
= beta
= 0;
718 /* single-reply extension */
719 if(s
->num_moves
== 1 && !extended
)
724 else if(s
->num_moves
<= 3 && !extended
)
730 /*******************************************************************************
732 First comes the move from the hashtable, if avalable.
733 The remaining moves are sorted with a heuristic that keeps in account
734 the history heuristic (ie the moves that previously caused an alpha
735 cutoff), a MVV/LVA bonus value and a small bonus for moves that go
737 *******************************************************************************/
739 /* convert the move we got from the hash to the move structure */
742 best_mv_hash
= board
.uncompress_move(cbest_mv_hash
);
743 /* if it happened that the move we got from the hashtable
744 is not valid, simply no move will get the high
745 heuristic bonus value */
747 #if INTERNAL_ITERATIVE_DEEPENING
748 else if(base_depth
>3*PLY
&& pv
) /* don't do it only on the pv, or it will be useless :) */
751 GUI1(notify(&board
, Move::None(), ply
, base_depth
-2*PLY
, -INF
, alpha
, beta
, SearchGui::Blue
));
752 int val
= search(ply
+1, base_depth
-2*PLY
, true, alpha
, beta
, expect_allbad
);
753 GUI2(search_gui
->notify_value(ply
, val
, DIFF_NODES
, 0));
755 HashEntry
*h2
= probe_hash( board
.hash
);
756 if(h2
&& h2
->best_mv
)
758 cbest_mv_hash
= h2
->best_mv
;
759 best_mv_hash
= board
.uncompress_move(cbest_mv_hash
);
762 #endif //INTERNAL_ITERATIVE_DEEPENING
764 /* for each move calculate the heuristic goodness value */
766 Move
*prev
= (stack
[ply
-1].curr_move
>=0) ? &stack
[ply
-1].moves
[stack
[ply
-1].curr_move
] : NULL
;
768 moves_quiescence_heuristic( s
->moves
, s
->num_moves
, best_mv_hash
,
769 best
, alpha
, beta
, base_depth
, prev
);
773 moves_heuristic( s
->moves
, s
->num_moves
, pv
, ply
, base_depth
,
774 best_mv_hash
, quiesce
, prev
, &overall_ext
, pluto1
, pluto2
);
775 depth
+= overall_ext
;
779 /* if quiesce rip-off the "non-critical" moves */
783 for(int i
=0;i
<s
->num_moves
;i
++)
784 if(s
->moves
[i
].val
>0)
785 s
->moves
[n
++] = s
->moves
[i
];
786 mv_stack_top
-= s
->num_moves
-n
;
789 no_good_moves
= true;
792 /* Don't do it now, do it incrementally */
793 //sort_moves( s->moves, s->num_moves );
796 #if EARLY_TRANSP_CUTOFF
797 /*******************************************************************************
798 Try to get an early beta cutoff using the hash table values
799 of the following moves, and improve alpha too.
800 Try on the first 6 value of the ordered moves (argh, looking into the
801 hashtable is very expensive because of the cache!!!!!!!!)
802 *******************************************************************************/
806 HashKey hk
= board
.move_hash(s
->moves
[0]);
807 for(int i
=1;i
<s
->num_moves
;i
++)
809 engine
->prefetch_hash(hk
);
810 HashKey newhk
= board
.move_hash(s
->moves
[i
]);
811 HashEntry
*h2
= engine
->probe_hash( hk
);
814 if(h2
&& h2
->depth
>= depth
-PLY
)
821 alpha
= MAX(alpha
, (int16_t)-h2
->up
);
825 HashEntry
*h2
= engine
->probe_hash( hk
);
826 if(h2
&& h2
->depth
>= depth
-PLY
)
833 alpha
= MAX(alpha
, (int16_t)-h2
->up
);
836 #endif //EARLY_TRANSP_CUTOFF
838 /*******************************************************************************
839 It is now time to loop all the successor moves and search recursively.
840 *******************************************************************************/
843 /* calcluate the evaluation (required by fitility pruning) */
844 position_val
= quiesce
? best
: board
.dummy_evaluate(); //evaluate( st_computer_color, -INF, INF);
847 for(int i
=0;i
<s
->num_moves
;i
++)
850 int sdepth
= depth
-100;
852 /* sort moves incrementally, in the hope of a beta cut */
853 engine
->incremental_sort_moves(s
->moves
+i
, s
->num_moves
-i
);
855 /* extensions calculated during the heuristic sort */
856 sdepth
+= s
->moves
[i
].extend
;
859 /* futility pruning, it is done only if we are not under check
860 and the move is not a "critical" move */
861 if(depth
>0 && depth
<=2*PLY
&& !s
->under_check
&& s
->moves
[i
].val
< 28000)
863 static const int mavala
[] = { 0, 500, 325, 975, 325, 100, 0 };
865 int16_t fmargin
= (depth
<= PLY
? 420 : 720);
866 int16_t fval
= position_val
+ mavala
[PIECE_OF(s
->moves
[i
].capture
)];
867 if(fval
< alpha
-fmargin
)
874 int mx
= CAPT_INDEX(s
->moves
[i
]);
876 /* collect history statistics */
877 if(!s
->moves
[i
].capture
&&
878 !(s
->moves
[i
].flags
>=PROMOTE_FIRST
) )
880 int ix
= HISTORY_INDEX(s
->moves
[i
]);
881 if(history_tot
[ix
] > 1024)
883 history_tot
[ix
] = history_tot
[ix
]*7/8;
884 history_hit
[ix
] = history_hit
[ix
]*7/8;
886 history_tot
[ix
] += quiesce
? 8 : 16;
889 if(pluto1
!=-1 && !s
->moves
[i
].capture
&&
890 !(s
->moves
[i
].flags
>=PROMOTE_FIRST
))
892 int ix
= FAST_PLUTO(pluto1
, mx
);
893 if(pluto_tot
[ix
] > 256)
895 pluto_tot
[ix
] = pluto_tot
[ix
]*7/8;
896 pluto_hit
[ix
] = pluto_hit
[ix
]*7/8;
898 pluto_tot
[ix
] += quiesce
? 8 : 16;
901 if(pluto2
!=-1 && !s
->moves
[i
].capture
&&
902 !(s
->moves
[i
].flags
>=PROMOTE_FIRST
))
904 int ix
= FAST_PLUTO(pluto2
, mx
);
905 if(pluto2_tot
[ix
] > 128)
907 pluto2_tot
[ix
] = pluto2_tot
[ix
]*7/8;
908 pluto2_hit
[ix
] = pluto2_hit
[ix
]*7/8;
910 pluto2_tot
[ix
] += quiesce
? 8 : 16;
914 //Set data for pawn-strike
916 if(!quiesce
&& PIECE_OF(board
.data
[s
->moves
[i
].from
]) == PAWN
&& s
->moves
[i
].val
== 27000) //FIXME, UGLY
918 stack
[ply
].escape_from
[1] = s
->moves
[i
].to
+ Board::up_dir
[IS_WHITE(board
.color_to_move
)] + RIGHT
;
919 if(OUT_OF_BOARD(stack
[ply
].escape_from
[1]) || !IS_OF_COLOR(board
.data
[stack
[ply
].escape_from
[1]], board
.other_color
)
920 || PIECE_OF(board
.data
[stack
[ply
].escape_from
[1]])==PAWN
)
921 stack
[ply
].escape_from
[1] = INVALID
;
922 stack
[ply
].escape_from
[2] = s
->moves
[i
].to
+ Board::up_dir
[IS_WHITE(board
.color_to_move
)] + LEFT
;
923 if(OUT_OF_BOARD(stack
[ply
].escape_from
[2]) || !IS_OF_COLOR(board
.data
[stack
[ply
].escape_from
[2]], board
.other_color
)
924 || PIECE_OF(board
.data
[stack
[ply
].escape_from
[2]])==PAWN
)
925 stack
[ply
].escape_from
[2] = INVALID
;
929 stack
[ply
].escape_from
[1] = stack
[ply
].escape_from
[2] = INVALID
;
933 board
.do_move(s
->moves
[i
], s
->save_buf
);
938 if(base_depth
> 0 && sdepth
<= 0)
940 STAT(quiesce_called
);
941 q
= stat_quiesce_nodes
;
945 #if NEGA_SCOUT /* use negascout, (null window search for nodes that are not in the pv) */
946 if(i
== 0 && !expect_allbad
)
948 if(depth
>=0 && pv
&& mv_pv_top
>=ply
)
950 mv_pv
[ply
] = s
->moves
[i
];
954 GUI1(notify(&board
, s
->moves
[i
], ply
, sdepth
, s
->moves
[i
].val
, alpha
, beta
, SearchGui::Bold
));
955 val
= -search( ply
+1, sdepth
, pv
, -beta
, -alpha
, !pv
);
956 GUI2(notify_value(ply
, val
, DIFF_NODES
, 0));
961 bool red_fuck
= false;
962 #if LATE_MOVE_REDUCTION
963 /* history pruning, if this is not a "critical" move and the failhigh
964 stats are too low, try a reduced depth search (if it returns >alpha,
965 re-do the full depth search) */
966 // if((sdepth>0) && !s->under_check && depth<=3*PLY && s->moves[i].val<28000 &&
967 // (history_hit[HISTORY_INDEX(s->moves[i])]+1)*3
968 // < (history_tot[HISTORY_INDEX(s->moves[i])]+1))
969 if( (sdepth
>0) && !s
->under_check
&& !s
->null_threat_dangerous
970 && s
->moves
[i
].val
<28000 && (s
->moves
[i
].val
<(sdepth
<=PLY
? 650 : 450)) )
972 int rdepth
= depth
- 2*PLY
;
973 //sdepth - PLY; //(s->moves[i].val<250 ? 2*PLY : PLY) - s->moves[i].extend;
974 GUI1(notify(&board
, s
->moves
[i
], ply
, rdepth
, s
->moves
[i
].val
, alpha
, alpha
+1, SearchGui::Italic
|SearchGui::Gray
));
975 val
= -search( ply
+1, rdepth
, false, -alpha
-1, -alpha
, false );
976 GUI2(notify_value(ply
, val
, DIFF_NODES
, val
>alpha
? SearchGui::Red
: 0));
978 goto skip_search
; /* reduced search was effective */
982 GUI1(notify(&board
, s
->moves
[i
], ply
, sdepth
, s
->moves
[i
].val
, alpha
, alpha
+1, 0 ));
983 val
= -search( ply
+1, sdepth
, false, -alpha
-1, -alpha
, red_fuck
);
984 GUI2(notify_value(ply
, val
, DIFF_NODES
, val
>alpha
? SearchGui::Red
: 0));
986 if((val
>alpha
) && pv
)
988 if(depth
>=0 && mv_pv_top
>=ply
)
990 mv_pv
[ply
] = s
->moves
[i
];
994 GUI1(notify(&board
, s
->moves
[i
], ply
, sdepth
, -INF
, alpha
, beta
, SearchGui::Bold
));
995 val
= -search( ply
+1, sdepth
, true, -beta
, -alpha
, false );
996 GUI2(notify_value(ply
, val
, DIFF_NODES
, val
>beta
? SearchGui::Red
: 0));
1002 if(base_depth
> 0 && sdepth
<= 0)
1004 q
= stat_quiesce_nodes
-q
;
1005 if(q
> stat_max_quiesce_nodes
)
1007 stat_max_quiesce_nodes
= q
;
1008 max_quiesce_board
= board
;
1014 board
.undo_move(s
->moves
[i
], s
->save_buf
);
1016 /* update the current best value and check for and alpha cut */
1027 /* alpha improvement! */
1036 if(!expect_allbad
|| best
>= beta
)
1038 /* collect statistics for the history */
1039 if(/*best >= beta &&*/ (best_mv_found
!=-1) &&
1040 !s
->moves
[best_mv_found
].capture
&&
1041 !(s
->moves
[best_mv_found
].flags
>=PROMOTE_FIRST
) )
1042 history_hit
[HISTORY_INDEX(s
->moves
[best_mv_found
])] += quiesce
? 8 : 16;
1044 int mx
= CAPT_INDEX(s
->moves
[best_mv_found
]);
1045 if(pluto1
!=-1 && !s
->moves
[best_mv_found
].capture
&&
1046 !(s
->moves
[best_mv_found
].flags
>=PROMOTE_FIRST
))
1048 int ix
= FAST_PLUTO(pluto1
, mx
);
1049 pluto_hit
[ix
] += quiesce
? 8 : 16;
1052 if(pluto2
!=-1 && !s
->moves
[best_mv_found
].capture
&&
1053 !(s
->moves
[best_mv_found
].flags
>=PROMOTE_FIRST
))
1055 int ix
= FAST_PLUTO(pluto2
, mx
);
1056 pluto2_hit
[ix
] += quiesce
? 8 : 16;
1061 mv_stack_top
-= s
->num_moves
; /* free the moves we allocated */
1063 /* this is a null move, save what the threat is */
1064 if(stack
[ply
-1].curr_move
== -1 && best_mv_found
>= 0)
1065 stack
[ply
-1].null_threat
= s
->moves
[best_mv_found
];
1067 /* if we found a best move searching, that move will be saved.
1068 if we did no search (ie quiescence), save the old hash value,
1069 or -1 if no hash entry had been found */
1070 int bestmv
= cbest_mv_hash
;
1071 if(best_mv_found
>= 0)
1072 bestmv
= board
.compress_move(s
->moves
[best_mv_found
]);
1074 /* write the value in the hash, with the index of the best move */
1075 engine
->write_hash( board
.hash
,
1076 best
> orig_alpha
? MIN(best
, beta
) : lower_bound
,
1077 best
< beta
? best
: +INF
,
1078 MAX(base_depth
,-500), bestmv
, no_good_moves
);
1079 GUI(notify_hash(ply
, best
> orig_alpha
? MIN(best
, beta
) : lower_bound
, best
< beta
? best
: +INF
,
1080 MAX(base_depth
,-500), alpha
, beta
, bestmv
? board
.uncompress_move(bestmv
) : Move::None(), true,
1081 SearchGui::Bold
| SearchGui::Magenta
) );
1087 SearchRoot::SearchRoot(Engine
* e
, const Board
& b
)
1090 , search_gui(e
->search_gui
)
1096 SearchRoot::~SearchRoot()
1100 Move
Engine::find_best_move()
1103 Engine
*engine
= this;
1104 int base_depth
= PLY
;
1105 int num_mate_hits
= 0;
1106 SearchRoot
root(this, board
);
1107 SearchRoot::SearchStack
*s
= &root
.stack
[0];
1109 /* initialize the root node */
1112 s
->null_threat
= Move::FromInt(0);
1113 s
->null_threat_dangerous
= false;
1114 s
->moves
= root
.mv_stack
;
1115 s
->num_moves
= root
.mv_stack_top
= root
.board
.find_moves(s
->moves
);
1117 s
->escape_from
[1] = s
->escape_from
[2] = INVALID
;
1118 #endif //PAWN_STRIKE
1122 /* calculate how much time we will think*/
1123 start_think_time
= current_time();
1124 if(analysis_limit
== TIME_LIMIT
)
1126 if(board
.color_to_move
== st_computer_color
)
1128 else /* pondering? analysing? */
1129 time_best_csec
= 99999999;
1130 max_think_time
= start_think_time
+ time_best_csec
- 2;
1133 /* to print the analysis */
1135 output("\tply\tscore\ttime\tnodes\tpv\n");
1137 /* return immediatly if the move is forced. */
1141 output("\t0\t0\t0\t0\t%s (only move)\n", board
.move_to_alg(MoveStr().data(), s
->moves
[0]));
1145 /* probe the play lines */
1146 if(eng_status
== PLAYING
&& st_computer_color
== board
.color_to_move
)
1148 Move retv
= probe_lines(&root
.board
);
1153 output("\t0\t0\t0\t0\t%s (selected line)\n", board
.move_to_alg(MoveStr().data(), retv
));
1158 /* probe the book */
1159 Move bookmove
= probe_book(&root
.board
);
1160 if(bookmove
.valid())
1163 for(int i
=0;i
<s
->num_moves
++;i
++)
1164 if(bookmove
== s
->moves
[i
])
1166 output("Error!!! invalid move in the book!!!\n");
1170 IFGUI(reset_hash()); //Try to be deterministic
1171 processed_nodes
= 0;
1175 memset( root
.history_tot
, 0, HISTORY_SIZE
*sizeof(uint16_t));
1176 memset( root
.history_hit
, 0, HISTORY_SIZE
*sizeof(uint16_t));
1177 memset( root
.pluto_tot
, 0, PLUTO_SIZE
*sizeof(uint16_t));
1178 memset( root
.pluto_hit
, 0, PLUTO_SIZE
*sizeof(uint16_t));
1179 memset( root
.pluto2_tot
, 0, PLUTO_SIZE
*sizeof(uint16_t));
1180 memset( root
.pluto2_hit
, 0, PLUTO_SIZE
*sizeof(uint16_t));
1185 /* set the back jump for the quick thinking exit */
1190 /* do the iterative deepening thing. */
1193 int16_t alpha
= num_mate_hits
? -INF
: -WORST_MATE
;
1194 int16_t beta
= num_mate_hits
? INF
: WORST_MATE
;
1196 GUI(new_root_level(base_depth
));
1198 /* for each move call the alpha-beta search function */
1199 for(int i
=0;i
<s
->num_moves
;i
++)
1202 root
.board
.do_move(s
->moves
[i
], s
->save_buf
);
1206 root
.mv_pv
[0] = s
->moves
[i
];
1209 GUI1(notify(&root
.board
, s
->moves
[i
], 0, base_depth
-PLY
, s
->moves
[i
].val
, alpha
, beta
, SearchGui::Bold
));
1210 s
->moves
[i
].val
= -root
.search( 1, base_depth
-PLY
, true, -beta
, -alpha
, false );
1211 GUI2(notify_value(0, s
->moves
[i
].val
, DIFF_NODES
, 0));
1216 GUI1(notify(&root
.board
, s
->moves
[i
], 0, base_depth
-PLY
, s
->moves
[i
].val
, alpha
, alpha
+1, 0 ));
1217 s
->moves
[i
].val
= -root
.search( 1, base_depth
-PLY
, false, -alpha
-1, -alpha
, false );
1218 GUI2(notify_value(0, s
->moves
[i
].val
, DIFF_NODES
, s
->moves
[i
].val
>alpha
? SearchGui::Red
: 0));
1220 if(s
->moves
[i
].val
> alpha
)
1222 root
.mv_pv
[0] = s
->moves
[i
];
1225 GUI1(notify(&root
.board
, s
->moves
[i
], 0, base_depth
-PLY
, -INF
, alpha
, beta
, SearchGui::Bold
));
1226 s
->moves
[i
].val
= -root
.search( 1, base_depth
-PLY
, true, -beta
, -alpha
, false );
1227 GUI2(notify_value(0, s
->moves
[i
].val
, DIFF_NODES
, 0));
1231 root
.board
.undo_move(s
->moves
[i
], s
->save_buf
);
1234 if(s
->moves
[i
].val
> alpha
)
1236 alpha
= s
->moves
[i
].val
;
1238 Move tmp
= s
->moves
[i
];
1239 for(int q
=i
-1;q
>=0;q
--)
1240 s
->moves
[q
+1] = s
->moves
[q
];
1243 /* this move caused an alpha cut, so print the new line */
1244 if( post
/*&& processed_nodes>100000*/)
1246 output("\t%d\t%d\t%d\t%llu\t",
1249 current_time() - start_think_time
,
1250 (unsigned long long)processed_nodes
);
1251 print_moves(root
.mv_pv
, 1/*root.mv_pv_top*/, true, true);
1256 /* print the result of the analysis at this depth */
1257 if( post
/*&& processed_nodes>100000*/)
1259 output("\t%d\t%d\t%d\t%llu\t",
1262 current_time() - start_think_time
,
1263 (unsigned long long)processed_nodes
);
1264 print_moves(root
.mv_pv
, 1/*root.mv_pv_top*/, true, true);
1268 if( base_depth
>= 40*PLY
)
1271 /* return in case of fixed depth search */
1272 if( eng_status
== PLAYING
&& st_computer_color
== board
.color_to_move
&&
1273 analysis_limit
== DEPTH_LIMIT
&& base_depth
== st_depth
*PLY
)
1276 /* return if 3/5 of time is gone (we can't search another ply anyway) */
1277 if( eng_status
== PLAYING
&& st_computer_color
== board
.color_to_move
&&
1278 analysis_limit
== TIME_LIMIT
&&
1279 (current_time()-start_think_time
) >= (time_best_csec
*3/5) )
1282 /* if a checkmate was detected return immediately */
1283 if( ABS(alpha
) > WORST_MATE
)
1286 if(num_mate_hits
>= 5)
1298 output("#max quiesce board: %s [%d %d]\n", max_quiesce_board
.to_fen(BigStr().data()),
1299 max_quiesce_alpha
, max_quiesce_beta
);