void idle_loop(int threadID, SplitPoint* sp);
template <bool Fake>
- bool split(const Position& pos, SearchStack* ss, int ply, Value* alpha, const Value beta, Value* bestValue,
- Depth depth, bool mateThreat, int* moves, MovePicker* mp, int master, bool pvNode);
+ void split(const Position& pos, SearchStack* ss, int ply, Value* alpha, const Value beta, Value* bestValue,
+ Depth depth, bool mateThreat, int* moveCount, MovePicker* mp, int master, bool pvNode);
private:
friend void poll();
// RootMove::operator<() is the comparison function used when
// sorting the moves. A move m1 is considered to be better
// than a move m2 if it has a higher score, or if the moves
- // have equal score but m1 has the higher node count.
+ // have equal score but m1 has the higher beta cut-off count.
bool operator<(const RootMove& m) const {
return score != m.score ? score < m.score : theirBeta <= m.theirBeta;
const Depth IIDDepth[2] = { 8 * OnePly /* non-PV */, 5 * OnePly /* PV */};
// At Non-PV nodes we do an internal iterative deepening search
- // when the static evaluation is at most IIDMargin below beta.
+ // when the static evaluation is bigger then beta - IIDMargin.
const Value IIDMargin = Value(0x100);
// Step 11. Decide the new search depth
if (!PvNode && tte && ok_to_use_TT(tte, depth, beta, ply))
{
// Refresh tte entry to avoid aging
- TT.store(posKey, tte->value(), tte->type(), tte->depth(), ttMove);
+ TT.store(posKey, tte->value(), tte->type(), tte->depth(), ttMove, tte->static_value(), tte->king_danger());
ss[ply].currentMove = ttMove; // Can be MOVE_NONE
return value_from_tt(tte->value(), ply);
isCheck = pos.is_check();
if (!isCheck)
{
- if (tte && (tte->type() & VALUE_TYPE_EVAL))
- ss[ply].eval = value_from_tt(tte->value(), ply);
+ if (tte && tte->static_value() != VALUE_NONE)
+ {
+ ss[ply].eval = tte->static_value();
+ ei.kingDanger[pos.side_to_move()] = tte->king_danger();
+ }
else
ss[ply].eval = evaluate(pos, ei, threadID);
}
// Step 9. Internal iterative deepening
- if ( depth >= IIDDepth[PvNode]
- && ttMove == MOVE_NONE
+ if ( depth >= IIDDepth[PvNode]
+ && (ttMove == MOVE_NONE || (PvNode && tte->depth() <= depth - 4 * OnePly))
&& (PvNode || (!isCheck && ss[ply].eval >= beta - IIDMargin)))
{
Depth d = (PvNode ? depth - 2 * OnePly : depth / 2);
value = -search<PV>(pos, ss, -beta, -alpha, newDepth, ply+1, false, threadID);
else
{
- // Step 14. Reduced search
- // if the move fails high will be re-searched at full depth.
+ // Step 14. Reduced depth search
+ // If the move fails high will be re-searched at full depth.
bool doFullDepthSearch = true;
if ( depth >= 3 * OnePly
value = -search<NonPV>(pos, ss, -(alpha+1), -alpha, newDepth-ss[ply].reduction, ply+1, true, threadID);
doFullDepthSearch = (value > alpha);
}
+
+ // The move failed high, but if reduction is very big we could
+ // face a false positive, retry with a less aggressive reduction,
+ // if the move fails high again then go with full depth search.
+ if (doFullDepthSearch && ss[ply].reduction > 2 * OnePly)
+ {
+ ss[ply].reduction = OnePly;
+ value = -search<NonPV>(pos, ss, -(alpha+1), -alpha, newDepth-ss[ply].reduction, ply+1, true, threadID);
+ doFullDepthSearch = (value > alpha);
+ }
}
// Step 15. Full depth search
&& Iteration <= 99
&& TM.available_thread_exists(threadID)
&& !AbortSearch
- && !TM.thread_should_stop(threadID)
- && TM.split<FakeSplit>(pos, ss, ply, &alpha, beta, &bestValue, depth,
- mateThreat, &moveCount, &mp, threadID, PvNode))
- break;
+ && !TM.thread_should_stop(threadID))
+ TM.split<FakeSplit>(pos, ss, ply, &alpha, beta, &bestValue, depth,
+ mateThreat, &moveCount, &mp, threadID, PvNode);
}
// Step 19. Check for mate and stalemate
return bestValue;
if (bestValue <= oldAlpha)
- TT.store(posKey, value_to_tt(bestValue, ply), VALUE_TYPE_UPPER, depth, MOVE_NONE);
+ TT.store(posKey, value_to_tt(bestValue, ply), VALUE_TYPE_UPPER, depth, MOVE_NONE, ss[ply].eval, ei.kingDanger[pos.side_to_move()]);
else if (bestValue >= beta)
{
TM.incrementBetaCounter(pos.side_to_move(), depth, threadID);
move = ss[ply].pv[ply];
- TT.store(posKey, value_to_tt(bestValue, ply), VALUE_TYPE_LOWER, depth, move);
+ TT.store(posKey, value_to_tt(bestValue, ply), VALUE_TYPE_LOWER, depth, move, ss[ply].eval, ei.kingDanger[pos.side_to_move()]);
if (!pos.move_is_capture_or_promotion(move))
{
update_history(pos, move, depth, movesSearched, moveCount);
}
}
else
- TT.store(posKey, value_to_tt(bestValue, ply), VALUE_TYPE_EXACT, depth, ss[ply].pv[ply]);
+ TT.store(posKey, value_to_tt(bestValue, ply), VALUE_TYPE_EXACT, depth, ss[ply].pv[ply], ss[ply].eval, ei.kingDanger[pos.side_to_move()]);
assert(bestValue > -VALUE_INFINITE && bestValue < VALUE_INFINITE);
if (!PvNode && tte && ok_to_use_TT(tte, depth, beta, ply))
{
- assert(tte->type() != VALUE_TYPE_EVAL);
-
ss[ply].currentMove = ttMove; // Can be MOVE_NONE
return value_from_tt(tte->value(), ply);
}
// Evaluate the position statically
if (isCheck)
staticValue = -VALUE_INFINITE;
- else if (tte && (tte->type() & VALUE_TYPE_EVAL))
- staticValue = value_from_tt(tte->value(), ply);
+ else if (tte && tte->static_value() != VALUE_NONE)
+ {
+ staticValue = tte->static_value();
+ ei.kingDanger[pos.side_to_move()] = tte->king_danger();
+ }
else
staticValue = evaluate(pos, ei, threadID);
if (bestValue >= beta)
{
// Store the score to avoid a future costly evaluation() call
- if (!isCheck && !tte && ei.kingDanger[pos.side_to_move()] == 0)
- TT.store(pos.get_key(), value_to_tt(bestValue, ply), VALUE_TYPE_EV_LO, Depth(-127*OnePly), MOVE_NONE);
+ if (!isCheck && !tte)
+ TT.store(pos.get_key(), value_to_tt(bestValue, ply), VALUE_TYPE_LOWER, Depth(-127*OnePly), MOVE_NONE, ss[ply].eval, ei.kingDanger[pos.side_to_move()]);
return bestValue;
}
{
// If bestValue isn't changed it means it is still the static evaluation
// of the node, so keep this info to avoid a future evaluation() call.
- ValueType type = (bestValue == staticValue && !ei.kingDanger[pos.side_to_move()] ? VALUE_TYPE_EV_UP : VALUE_TYPE_UPPER);
- TT.store(pos.get_key(), value_to_tt(bestValue, ply), type, d, MOVE_NONE);
+ TT.store(pos.get_key(), value_to_tt(bestValue, ply), VALUE_TYPE_UPPER, d, MOVE_NONE, ss[ply].eval, ei.kingDanger[pos.side_to_move()]);
}
else if (bestValue >= beta)
{
move = ss[ply].pv[ply];
- TT.store(pos.get_key(), value_to_tt(bestValue, ply), VALUE_TYPE_LOWER, d, move);
+ TT.store(pos.get_key(), value_to_tt(bestValue, ply), VALUE_TYPE_LOWER, d, move, ss[ply].eval, ei.kingDanger[pos.side_to_move()]);
// Update killers only for good checking moves
if (!pos.move_is_capture_or_promotion(move))
update_killers(move, ss[ply]);
}
else
- TT.store(pos.get_key(), value_to_tt(bestValue, ply), VALUE_TYPE_EXACT, d, ss[ply].pv[ply]);
+ TT.store(pos.get_key(), value_to_tt(bestValue, ply), VALUE_TYPE_EXACT, d, ss[ply].pv[ply], ss[ply].eval, ei.kingDanger[pos.side_to_move()]);
assert(bestValue > -VALUE_INFINITE && bestValue < VALUE_INFINITE);
&& (move = sp->mp->get_next_move()) != MOVE_NONE
&& !TM.thread_should_stop(threadID))
{
- moveCount = ++sp->moves;
+ moveCount = ++sp->moveCount;
lock_release(&(sp->lock));
assert(move_is_ok(move));
pos.do_move(move, st, ci, moveIsCheck);
// Step 14. Reduced search
- // if the move fails high will be re-searched at full depth.
+ // If the move fails high will be re-searched at full depth.
bool doFullDepthSearch = true;
if ( !dangerous
value = -search<NonPV>(pos, ss, -(localAlpha+1), -localAlpha, newDepth-ss[sp->ply].reduction, sp->ply+1, true, threadID);
doFullDepthSearch = (value > localAlpha);
}
+
+ // The move failed high, but if reduction is very big we could
+ // face a false positive, retry with a less aggressive reduction,
+ // if the move fails high again then go with full depth search.
+ if (doFullDepthSearch && ss[sp->ply].reduction > 2 * OnePly)
+ {
+ ss[sp->ply].reduction = OnePly;
+ Value localAlpha = sp->alpha;
+ value = -search<NonPV>(pos, ss, -(localAlpha+1), -localAlpha, newDepth-ss[sp->ply].reduction, sp->ply+1, true, threadID);
+ doFullDepthSearch = (value > localAlpha);
+ }
}
// Step 15. Full depth search
// split() does the actual work of distributing the work at a node between
- // several threads at PV nodes. If it does not succeed in splitting the
+ // several available threads. If it does not succeed in splitting the
// node (because no idle threads are available, or because we have no unused
- // split point objects), the function immediately returns false. If
- // splitting is possible, a SplitPoint object is initialized with all the
- // data that must be copied to the helper threads (the current position and
- // search stack, alpha, beta, the search depth, etc.), and we tell our
- // helper threads that they have been assigned work. This will cause them
- // to instantly leave their idle loops and call sp_search(). When all
- // threads have returned from sp_search() then split() returns true.
+ // split point objects), the function immediately returns. If splitting is
+ // possible, a SplitPoint object is initialized with all the data that must be
+ // copied to the helper threads and we tell our helper threads that they have
+ // been assigned work. This will cause them to instantly leave their idle loops
+ // and call sp_search(). When all threads have returned from sp_search() then
+ // split() returns.
template <bool Fake>
- bool ThreadsManager::split(const Position& p, SearchStack* sstck, int ply, Value* alpha,
+ void ThreadsManager::split(const Position& p, SearchStack* sstck, int ply, Value* alpha,
const Value beta, Value* bestValue, Depth depth, bool mateThreat,
- int* moves, MovePicker* mp, int master, bool pvNode) {
+ int* moveCount, MovePicker* mp, int master, bool pvNode) {
assert(p.is_ok());
assert(sstck != NULL);
assert(ply >= 0 && ply < PLY_MAX);
assert(master >= 0 && master < ActiveThreads);
assert(ActiveThreads > 1);
- SplitPoint* splitPoint;
-
lock_grab(&MPLock);
// If no other thread is available to help us, or if we have too many
|| threads[master].activeSplitPoints >= ACTIVE_SPLIT_POINTS_MAX)
{
lock_release(&MPLock);
- return false;
+ return;
}
// Pick the next available split point object from the split point stack
- splitPoint = &SplitPointStack[master][threads[master].activeSplitPoints];
+ SplitPoint* splitPoint = &SplitPointStack[master][threads[master].activeSplitPoints];
// Initialize the split point object
splitPoint->parent = threads[master].splitPoint;
splitPoint->beta = beta;
splitPoint->pvNode = pvNode;
splitPoint->bestValue = *bestValue;
- splitPoint->master = master;
splitPoint->mp = mp;
- splitPoint->moves = *moves;
+ splitPoint->moveCount = *moveCount;
splitPoint->pos = &p;
splitPoint->parentSstack = sstck;
for (int i = 0; i < ActiveThreads; i++)
threads[master].splitPoint = splitPoint->parent;
lock_release(&MPLock);
- return true;
}