template <bool Fake>
void split(const Position& pos, SearchStack* ss, int ply, Value* alpha, const Value beta, Value* bestValue,
- Depth depth, bool mateThreat, int* moveCount, MovePicker* mp, bool pvNode);
+ Depth depth, Move threatMove, bool mateThreat, int* moveCount, MovePicker* mp, bool pvNode);
private:
friend void poll();
void wait_for_stop_or_ponderhit();
void init_ss_array(SearchStack* ss, int size);
void print_pv_info(const Position& pos, Move pv[], Value alpha, Value beta, Value value);
+ void insert_pv_in_tt(const Position& pos, Move pv[]);
+ void extract_pv_from_tt(const Position& pos, Move bestMove, Move pv[]);
#if !defined(_MSC_VER)
void *init_thread(void *threadID);
// Called at the beginning of search() when starting to examine a new node.
void SearchStack::init() {
- currentMove = threatMove = bestMove = MOVE_NONE;
+ currentMove = bestMove = MOVE_NONE;
}
// SearchStack::initKillers() initializes killers for a search stack entry
// Write PV to transposition table, in case the relevant entries have
// been overwritten during the search.
- TT.insert_pv(p, pv);
+ insert_pv_in_tt(p, pv);
if (AbortSearch)
break; // Value cannot be trusted. Break out immediately!
// the score before research in case we run out of time while researching.
rml.set_move_score(i, value);
ss->bestMove = move;
- TT.extract_pv(pos, move, pv, PLY_MAX);
+ extract_pv_from_tt(pos, move, pv);
rml.set_move_pv(i, pv);
// Print information to the standard output
// Update PV
rml.set_move_score(i, value);
ss->bestMove = move;
- TT.extract_pv(pos, move, pv, PLY_MAX);
+ extract_pv_from_tt(pos, move, pv);
rml.set_move_pv(i, pv);
if (MultiPV == 1)
bool mateThreat = false;
int moveCount = 0;
int threadID = pos.thread();
+ Move threatMove = MOVE_NONE;
refinedValue = bestValue = value = -VALUE_INFINITE;
oldAlpha = alpha;
ei.kingDanger[pos.side_to_move()] = tte->king_danger();
}
else
+ {
ss->eval = evaluate(pos, ei);
+ TT.store(posKey, VALUE_NONE, VALUE_TYPE_NONE, DEPTH_NONE, MOVE_NONE, ss->eval, ei.kingDanger[pos.side_to_move()]);
+ }
refinedValue = refine_eval(tte, ss->eval, ply); // Enhance accuracy with TT value if possible
update_gains(pos, (ss-1)->currentMove, (ss-1)->eval, ss->eval);
&& !value_is_mate(beta)
&& !pos.has_pawn_on_7th(pos.side_to_move()))
{
- // Pass ss->eval to qsearch() and avoid an evaluate call
- if (!tte)
- TT.store(posKey, VALUE_NONE, VALUE_TYPE_NONE, DEPTH_NONE, MOVE_NONE, ss->eval, ei.kingDanger[pos.side_to_move()]);
-
Value rbeta = beta - razor_margin(depth);
Value v = qsearch<NonPV>(pos, ss, rbeta-1, rbeta, Depth(0), ply);
if (v < rbeta)
if (nullValue == value_mated_in(ply + 2))
mateThreat = true;
- ss->threatMove = (ss+1)->currentMove;
+ threatMove = (ss+1)->currentMove;
if ( depth < ThreatDepth
&& (ss-1)->reduction
- && connected_moves(pos, (ss-1)->currentMove, ss->threatMove))
+ && connected_moves(pos, (ss-1)->currentMove, threatMove))
return beta - 1;
}
}
{
// Move count based pruning
if ( moveCount >= futility_move_count(depth)
- && !(ss->threatMove && connected_threat(pos, move, ss->threatMove))
+ && !(threatMove && connected_threat(pos, move, threatMove))
&& bestValue > value_mated_in(PLY_MAX))
continue;
&& !TM.thread_should_stop(threadID)
&& Iteration <= 99)
TM.split<FakeSplit>(pos, ss, ply, &alpha, beta, &bestValue, depth,
- mateThreat, &moveCount, &mp, PvNode);
+ threatMove, mateThreat, &moveCount, &mp, PvNode);
}
// Step 19. Check for mate and stalemate
{
// Move count based pruning
if ( moveCount >= futility_move_count(sp->depth)
- && !(ss->threatMove && connected_threat(pos, move, ss->threatMove))
+ && !(sp->threatMove && connected_threat(pos, move, sp->threatMove))
&& sp->bestValue > value_mated_in(PLY_MAX))
{
lock_grab(&(sp->lock));
}
+ // insert_pv_in_tt() is called at the end of a search iteration, and inserts
+ // the PV back into the TT. This makes sure the old PV moves are searched
+ // first, even if the old TT entries have been overwritten.
+
+ void insert_pv_in_tt(const Position& pos, Move pv[]) {
+
+ StateInfo st;
+ TTEntry* tte;
+ Position p(pos, pos.thread());
+ EvalInfo ei;
+ Value v;
+
+ for (int i = 0; pv[i] != MOVE_NONE; i++)
+ {
+ tte = TT.retrieve(p.get_key());
+ if (!tte || tte->move() != pv[i])
+ {
+ v = (p.is_check() ? VALUE_NONE : evaluate(p, ei));
+ TT.store(p.get_key(), VALUE_NONE, VALUE_TYPE_NONE, DEPTH_NONE, pv[i], v, ei.kingDanger[pos.side_to_move()]);
+ }
+ p.do_move(pv[i], st);
+ }
+ }
+
+
+ // extract_pv_from_tt() builds a PV by adding moves from the transposition table.
+ // We consider also failing high nodes and not only VALUE_TYPE_EXACT nodes. This
+ // allow to always have a ponder move even when we fail high at root and also a
+ // long PV to print that is important for position analysis.
+
+ void extract_pv_from_tt(const Position& pos, Move bestMove, Move pv[]) {
+
+ StateInfo st;
+ TTEntry* tte;
+ Position p(pos, pos.thread());
+ int ply = 0;
+
+ assert(bestMove != MOVE_NONE);
+
+ pv[ply] = bestMove;
+ p.do_move(pv[ply++], st);
+
+ while ( (tte = TT.retrieve(p.get_key())) != NULL
+ && tte->move() != MOVE_NONE
+ && move_is_legal(p, tte->move())
+ && ply < PLY_MAX
+ && (!p.is_draw() || ply < 2))
+ {
+ pv[ply] = tte->move();
+ p.do_move(pv[ply++], st);
+ }
+ pv[ply] = MOVE_NONE;
+ }
+
+
// init_thread() is the function which is called when a new thread is
// launched. It simply calls the idle_loop() function with the supplied
// threadID. There are two versions of this function; one for POSIX
template <bool Fake>
void ThreadsManager::split(const Position& p, SearchStack* ss, int ply, Value* alpha,
- const Value beta, Value* bestValue, Depth depth, bool mateThreat,
- int* moveCount, MovePicker* mp, bool pvNode) {
+ const Value beta, Value* bestValue, Depth depth, Move threatMove,
+ bool mateThreat, int* moveCount, MovePicker* mp, bool pvNode) {
assert(p.is_ok());
assert(ply > 0 && ply < PLY_MAX);
assert(*bestValue >= -VALUE_INFINITE);
splitPoint.stopRequest = false;
splitPoint.ply = ply;
splitPoint.depth = depth;
+ splitPoint.threatMove = threatMove;
splitPoint.mateThreat = mateThreat;
splitPoint.alpha = *alpha;
splitPoint.beta = beta;