const bool FakeSplit = false;
// Different node types, used as template parameter
- enum NodeType { Root, PV, NonPV, SplitPointPV, SplitPointNonPV };
+ enum NodeType { Root, PV, NonPV, SplitPointRoot, SplitPointPV, SplitPointNonPV };
// RootMove struct is used for moves at the root of the tree. For each root
// move, we store a score, a node count, and a PV (really a refutation
bool connected_moves(const Position& pos, Move m1, Move m2);
Value value_to_tt(Value v, int ply);
Value value_from_tt(Value v, int ply);
- bool ok_to_use_TT(const TTEntry* tte, Depth depth, Value beta, int ply);
+ bool can_return_tt(const TTEntry* tte, Depth depth, Value beta, int ply);
bool connected_threat(const Position& pos, Move m, Move threat);
Value refine_eval(const TTEntry* tte, Value defaultEval, int ply);
void update_history(const Position& pos, Move move, Depth depth, Move movesSearched[], int moveCount);
// MovePickerExt template class extends MovePicker and allows to choose at compile
// time the proper moves source according to the type of node. In the default case
// we simply create and use a standard MovePicker object.
- template<NodeType> struct MovePickerExt : public MovePicker {
+ template<bool SpNode> struct MovePickerExt : public MovePicker {
MovePickerExt(const Position& p, Move ttm, Depth d, const History& h, SearchStack* ss, Value b)
: MovePicker(p, ttm, d, h, ss, b) {}
};
// In case of a SpNode we use split point's shared MovePicker object as moves source
- template<> struct MovePickerExt<SplitPointNonPV> : public MovePicker {
+ template<> struct MovePickerExt<true> : public MovePicker {
MovePickerExt(const Position& p, Move ttm, Depth d, const History& h, SearchStack* ss, Value b)
: MovePicker(p, ttm, d, h, ss, b), mp(ss->sp->mp) {}
MovePicker* mp;
};
- template<> struct MovePickerExt<SplitPointPV> : public MovePickerExt<SplitPointNonPV> {
-
- MovePickerExt(const Position& p, Move ttm, Depth d, const History& h, SearchStack* ss, Value b)
- : MovePickerExt<SplitPointNonPV>(p, ttm, d, h, ss, b) {}
- };
-
// Overload operator<<() to make it easier to print moves in a coordinate
// notation compatible with UCI protocol.
std::ostream& operator<<(std::ostream& os, Move m) {
// Send full PV info to GUI if we are going to leave the loop or
// if we have a fail high/low and we are deep in the search.
if ((value > alpha && value < beta) || current_search_time() > 2000)
- for (int i = 0; i < Min(UCIMultiPV, MultiPVIteration); i++)
- {
+ for (int i = 0; i < Min(UCIMultiPV, MultiPVIteration + 1); i++)
cout << "info"
<< depth_to_uci(depth * ONE_PLY)
<< (i == MultiPVIteration ? score_to_uci(Rml[i].score, alpha, beta) :
<< speed_to_uci(pos.nodes_searched())
<< pv_to_uci(&Rml[i].pv[0], i + 1, pos.is_chess960())
<< endl;
- }
// In case of failing high/low increase aspiration window and research,
// otherwise exit the fail high/low loop.
template <NodeType NT>
Value search(Position& pos, SearchStack* ss, Value alpha, Value beta, Depth depth) {
- const bool PvNode = (NT == PV || NT == Root || NT == SplitPointPV);
- const bool SpNode = (NT == SplitPointPV || NT == SplitPointNonPV);
- const bool RootNode = (NT == Root);
+ const bool PvNode = (NT == PV || NT == Root || NT == SplitPointPV || NT == SplitPointRoot);
+ const bool SpNode = (NT == SplitPointPV || NT == SplitPointNonPV || NT == SplitPointRoot);
+ const bool RootNode = (NT == Root || NT == SplitPointRoot);
assert(alpha >= -VALUE_INFINITE && alpha <= VALUE_INFINITE);
assert(beta > alpha && beta <= VALUE_INFINITE);
excludedMove = ss->excludedMove;
posKey = excludedMove ? pos.get_exclusion_key() : pos.get_key();
tte = TT.probe(posKey);
- ttMove = tte ? tte->move() : MOVE_NONE;
+ ttMove = RootNode ? Rml[MultiPVIteration].pv[0] : tte ? tte->move() : MOVE_NONE;
// At PV nodes we check for exact scores, while at non-PV nodes we check for
// a fail high/low. Biggest advantage at probing at PV nodes is to have a
// smooth experience in analysis mode. We don't probe at Root nodes otherwise
// we should also update RootMoveList to avoid bogus output.
if (!RootNode && tte && (PvNode ? tte->depth() >= depth && tte->type() == VALUE_TYPE_EXACT
- : ok_to_use_TT(tte, depth, beta, ss->ply)))
+ : can_return_tt(tte, depth, beta, ss->ply)))
{
TT.refresh(tte);
ss->bestMove = ttMove; // Can be MOVE_NONE
split_point_start: // At split points actual search starts from here
// Initialize a MovePicker object for the current position
- MovePickerExt<NT> mp(pos, RootNode ? Rml[MultiPVIteration].pv[0] : ttMove, depth, H, ss, PvNode ? -VALUE_INFINITE : beta);
+ MovePickerExt<SpNode> mp(pos, ttMove, depth, H, ss, PvNode ? -VALUE_INFINITE : beta);
CheckInfo ci(pos);
ss->bestMove = MOVE_NONE;
futilityBase = ss->eval + ss->evalMargin;
// At root obey the "searchmoves" option and skip moves not listed in Root Move List.
// Also in MultiPV mode we skip moves which already have got an exact score
- // in previous MultiPV Iteration.
+ // in previous MultiPV Iteration. Finally any illegal move is skipped here.
if (RootNode && !Rml.find(move, MultiPVIteration))
continue;
// If it's time to send nodes info, do it here where we have the
// correct accumulated node counts searched by each thread.
- if (SendSearchedNodes)
+ if (!SpNode && SendSearchedNodes)
{
SendSearchedNodes = false;
cout << "info" << speed_to_uci(pos.nodes_searched()) << endl;
}
// For long searches send current move info to GUI
- if (current_search_time() > 2000)
+ if (pos.thread() == 0 && current_search_time() > 2000)
cout << "info" << depth_to_uci(depth)
- << " currmove " << move << " currmovenumber " << moveCount + MultiPVIteration << endl;
+ << " currmove " << move
+ << " currmovenumber " << moveCount + MultiPVIteration << endl;
}
// At Root and at first iteration do a PV search on all the moves to score root moves
- isPvMove = (PvNode && moveCount <= ((RootNode && depth <= ONE_PLY) ? MAX_MOVES : 1));
+ isPvMove = (PvNode && moveCount <= (RootNode && depth <= ONE_PLY ? MAX_MOVES : 1));
givesCheck = pos.move_gives_check(move, ci);
captureOrPromotion = pos.move_is_capture_or_promotion(move);
alpha = sp->alpha;
}
- if (value > bestValue)
- {
- bestValue = value;
- ss->bestMove = move;
-
- if ( !RootNode
- && PvNode
- && value > alpha
- && value < beta) // We want always alpha < beta
- alpha = value;
-
- if (SpNode && !thread.cutoff_occurred())
- {
- sp->bestValue = value;
- sp->ss->bestMove = move;
- sp->alpha = alpha;
- sp->is_betaCutoff = (value >= beta);
- }
- }
if (RootNode)
{
// the best move changes frequently, we allocate some more time.
if (!isPvMove && MultiPV == 1)
Rml.bestMoveChanges++;
-
- // Update alpha.
- if (value > alpha)
- alpha = value;
}
else
// All other moves but the PV are set to the lowest value, this
} // RootNode
+ if (value > bestValue)
+ {
+ bestValue = value;
+ ss->bestMove = move;
+
+ if ( PvNode
+ && value > alpha
+ && value < beta) // We want always alpha < beta
+ alpha = value;
+
+ if (SpNode && !thread.cutoff_occurred())
+ {
+ sp->bestValue = value;
+ sp->ss->bestMove = move;
+ sp->alpha = alpha;
+ sp->is_betaCutoff = (value >= beta);
+ }
+ }
+
// Step 19. Check for split
- if ( !RootNode
- && !SpNode
+ if ( !SpNode
&& depth >= Threads.min_split_depth()
&& bestValue < beta
&& Threads.available_slave_exists(pos.thread())
&& !StopRequest
&& !thread.cutoff_occurred())
Threads.split<FakeSplit>(pos, ss, &alpha, beta, &bestValue, depth,
- threatMove, moveCount, &mp, PvNode);
+ threatMove, moveCount, &mp, NT);
}
// Step 20. Check for mate and stalemate
tte = TT.probe(pos.get_key());
ttMove = (tte ? tte->move() : MOVE_NONE);
- if (!PvNode && tte && ok_to_use_TT(tte, ttDepth, beta, ss->ply))
+ if (!PvNode && tte && can_return_tt(tte, ttDepth, beta, ss->ply))
{
ss->bestMove = ttMove; // Can be MOVE_NONE
return value_from_tt(tte->value(), ss->ply);
}
- // ok_to_use_TT() returns true if a transposition table score
- // can be used at a given point in search.
+ // can_return_tt() returns true if a transposition table score
+ // can be used to cut-off at a given point in search.
- bool ok_to_use_TT(const TTEntry* tte, Depth depth, Value beta, int ply) {
+ bool can_return_tt(const TTEntry* tte, Depth depth, Value beta, int ply) {
Value v = value_from_tt(tte->value(), ply);
memcpy(ss, tsp->ss - 1, 4 * sizeof(SearchStack));
(ss+1)->sp = tsp;
- if (tsp->pvNode)
+ if (tsp->nodeType == Root)
+ search<SplitPointRoot>(pos, ss+1, tsp->alpha, tsp->beta, tsp->depth);
+ else if (tsp->nodeType == PV)
search<SplitPointPV>(pos, ss+1, tsp->alpha, tsp->beta, tsp->depth);
- else
+ else if (tsp->nodeType == NonPV)
search<SplitPointNonPV>(pos, ss+1, tsp->alpha, tsp->beta, tsp->depth);
assert(threads[threadID].state == Thread::SEARCHING);