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();
Depth PassedPawnExtension[2], PawnEndgameExtension[2], MateThreatExtension[2];
// Minimum depth for use of singular extension
- const Depth SingularExtensionDepth[2] = { 8 * OnePly /* non-PV */, 6 * OnePly /* PV */};
+ const Depth SingularExtensionDepth[2] = { 7 * OnePly /* non-PV */, 6 * OnePly /* PV */};
// If the TT move is at least SingularExtensionMargin better then the
// remaining ones we will extend it.
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);
}
-// SearchStack::init() initializes a search stack entry.
-// Called at the beginning of search() when starting to examine a new node.
-void SearchStack::init() {
-
- currentMove = threatMove = bestMove = MOVE_NONE;
-}
-
-// SearchStack::initKillers() initializes killers for a search stack entry
-void SearchStack::initKillers() {
-
- mateKiller = MOVE_NONE;
- for (int i = 0; i < KILLER_MAX; i++)
- killers[i] = MOVE_NONE;
-}
-
-
/// perft() is our utility to verify move generation is bug free. All the legal
/// moves up to given depth are generated and counted and the sum returned.
// 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!
beta = *betaPtr;
isCheck = pos.is_check();
- // Step 1. Initialize node and poll (omitted at root, init_ss_array() has already initialized root node)
+ // Step 1. Initialize node (polling is omitted at root)
+ ss->currentMove = ss->bestMove = MOVE_NONE;
+
// Step 2. Check for aborted search (omitted at root)
// Step 3. Mate distance pruning (omitted at root)
// Step 4. Transposition table lookup (omitted at root)
// 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)
StateInfo st;
const TTEntry* tte;
Key posKey;
- Move ttMove, move, excludedMove;
+ Move ttMove, move, excludedMove, threatMove;
Depth ext, newDepth;
Value bestValue, value, oldAlpha;
Value refinedValue, nullValue, futilityValueScaled; // Non-PV specific
// Step 1. Initialize node and poll. Polling can abort search
TM.incrementNodeCounter(threadID);
- ss->init();
- (ss+2)->initKillers();
+ ss->currentMove = ss->bestMove = threatMove = MOVE_NONE;
+ (ss+2)->killers[0] = (ss+2)->killers[1] = (ss+2)->mateKiller = MOVE_NONE;
if (threadID == 0 && ++NodesSincePoll > NodesBetweenPolls)
{
isCheck = pos.is_check();
if (!isCheck)
{
- if (tte && tte->static_value() != VALUE_NONE)
+ if (tte)
{
+ assert(tte->static_value() != VALUE_NONE);
ss->eval = tte->static_value();
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 || tte->static_value() == VALUE_NONE)
- TT.store(posKey, ss->eval, VALUE_TYPE_EXACT, Depth(-127*OnePly), 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;
}
}
&& is_lower_bound(tte->type())
&& tte->depth() >= depth - 3 * OnePly;
+ // Avoid to do an expensive singular extension search on nodes where
+ // such search had already failed in the past.
+ if ( !PvNode
+ && singularExtensionNode
+ && depth < SingularExtensionDepth[PvNode] + 5 * OnePly)
+ {
+ TTEntry* ttx = TT.retrieve(pos.get_exclusion_key());
+ if (ttx && is_lower_bound(ttx->type()))
+ singularExtensionNode = false;
+ }
+
// Step 10. Loop through moves
// Loop through all legal moves until no moves remain or a beta cutoff occurs
while ( bestValue < beta
{
// 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
}
else
{
- if (tte && tte->static_value() != VALUE_NONE)
+ if (tte)
{
+ assert(tte->static_value() != VALUE_NONE);
ei.kingDanger[pos.side_to_move()] = tte->king_danger();
bestValue = tte->static_value();
}
if (bestValue >= beta)
{
if (!tte)
- TT.store(pos.get_key(), value_to_tt(bestValue, ply), VALUE_TYPE_LOWER, Depth(-127*OnePly), MOVE_NONE, ss->eval, ei.kingDanger[pos.side_to_move()]);
+ TT.store(pos.get_key(), value_to_tt(bestValue, ply), VALUE_TYPE_LOWER, DEPTH_NONE, MOVE_NONE, ss->eval, ei.kingDanger[pos.side_to_move()]);
return bestValue;
}
{
// 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));
bool move_is_killer(Move m, SearchStack* ss) {
- const Move* k = ss->killers;
- for (int i = 0; i < KILLER_MAX; i++, k++)
- if (*k == m)
- return true;
+ if (ss->killers[0] == m || ss->killers[1] == m)
+ return true;
return false;
}
if (m == ss->killers[0])
return;
- for (int i = KILLER_MAX - 1; i > 0; i--)
- ss->killers[i] = ss->killers[i - 1];
-
+ ss->killers[1] = ss->killers[0];
ss->killers[0] = m;
}
ss->reduction = Depth(0);
if (i < 3)
- {
- ss->init();
- ss->initKillers();
- }
+ ss->killers[0] = ss->killers[1] = ss->mateKiller = MOVE_NONE;
}
}
}
+ // 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
#endif
// Initialize global locks
- lock_init(&MPLock, NULL);
- lock_init(&WaitLock, NULL);
+ lock_init(&MPLock);
+ lock_init(&WaitLock);
#if !defined(_MSC_VER)
pthread_cond_init(&WaitCond, NULL);
// Initialize splitPoints[] locks
for (i = 0; i < MAX_THREADS; i++)
for (int j = 0; j < MAX_ACTIVE_SPLIT_POINTS; j++)
- lock_init(&(threads[i].splitPoints[j].lock), NULL);
+ lock_init(&(threads[i].splitPoints[j].lock));
// Will be set just before program exits to properly end the threads
AllThreadsShouldExit = false;
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;
StateInfo st;
bool includeAllMoves = (searchMoves[0] == MOVE_NONE);
+ // Initialize search stack
+ init_ss_array(ss, PLY_MAX_PLUS_2);
+ ss[0].currentMove = ss[0].bestMove = MOVE_NONE;
+ ss[0].eval = VALUE_NONE;
+
// Generate all legal moves
MoveStack* last = generate_moves(pos, mlist);
continue;
// Find a quick score for the move
- init_ss_array(ss, PLY_MAX_PLUS_2);
- ss[0].eval = VALUE_NONE;
- ss[0].currentMove = cur->move;
pos.do_move(cur->move, st);
+ ss[0].currentMove = cur->move;
moves[count].move = cur->move;
moves[count].score = -qsearch<PV>(pos, ss+1, -VALUE_INFINITE, VALUE_INFINITE, Depth(0), 1);
moves[count].pv[0] = cur->move;