X-Git-Url: https://git.sesse.net/?p=stockfish;a=blobdiff_plain;f=src%2Fsearch.cpp;h=13c46eca0abce1e830fa2798775b767d5bb98b6d;hp=145c7aa2a33a750b5f677868790df109bbc36a01;hb=2cec7347dbb8e85891ba1fde85736f1732f52dc0;hpb=f0106851369a330d2cd1c9af370290d1a33d1f46 diff --git a/src/search.cpp b/src/search.cpp index 145c7aa2..13c46eca 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -54,6 +54,10 @@ namespace { /// Types enum NodeType { NonPV, PV }; + // Set to true to force running with one thread. + // Used for debugging SMP code. + const bool FakeSplit = false; + // ThreadsManager class is used to handle all the threads related stuff in search, // init, starting, parking and, the most important, launching a slave thread at a // split point are what this class does. All the access to shared thread data is @@ -83,6 +87,8 @@ namespace { void wake_sleeping_threads(); void put_threads_to_sleep(); void idle_loop(int threadID, SplitPoint* sp); + + template 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); @@ -179,8 +185,7 @@ namespace { // Step 9. Internal iterative deepening // Minimum depth for use of internal iterative deepening - const Depth IIDDepthAtPVNodes = 5 * OnePly; - const Depth IIDDepthAtNonPVNodes = 8 * OnePly; + 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. @@ -282,16 +287,21 @@ namespace { template Value search(Position& pos, SearchStack ss[], Value alpha, Value beta, Depth depth, int ply, bool allowNullmove, int threadID, Move excludedMove = MOVE_NONE); + template Value qsearch(Position& pos, SearchStack ss[], Value alpha, Value beta, Depth depth, int ply, int threadID); + + template void sp_search(SplitPoint* sp, int threadID); - void sp_search_pv(SplitPoint* sp, int threadID); + + template + Depth extension(const Position& pos, Move m, bool captureOrPromotion, bool moveIsCheck, bool singleEvasion, bool mateThreat, bool* dangerous); + void init_node(SearchStack ss[], int ply, int threadID); void update_pv(SearchStack ss[], int ply); void sp_update_pv(SearchStack* pss, SearchStack ss[], int ply); bool connected_moves(const Position& pos, Move m1, Move m2); bool value_is_mate(Value value); bool move_is_killer(Move m, const SearchStack& ss); - Depth extension(const Position&, Move, bool, bool, bool, bool, bool, bool*); bool ok_to_do_nullmove(const Position& pos); bool ok_to_prune(const Position& pos, Move m, Move threat); bool ok_to_use_TT(const TTEntry* tte, Depth depth, Value beta, int ply); @@ -844,7 +854,7 @@ namespace { // Step 11. Decide the new search depth depth = (Iteration - 2) * OnePly + InitialDepth; - ext = extension(pos, move, true, captureOrPromotion, moveIsCheck, false, false, &dangerous); + ext = extension(pos, move, captureOrPromotion, moveIsCheck, false, false, &dangerous); newDepth = depth + ext; // Step 12. Futility pruning (omitted at root) @@ -1021,14 +1031,15 @@ namespace { } - // search_pv() is the main search function for PV nodes. + // search<>() is the main search function for both PV and non-PV nodes template - Value search(Position& pos, SearchStack ss[], Value alpha, Value beta, - Depth depth, int ply, bool allowNullmove, int threadID, Move excludedMove) { + Value search(Position& pos, SearchStack ss[], Value alpha, Value beta, Depth depth, + int ply, bool allowNullmove, int threadID, Move excludedMove) { assert(alpha >= -VALUE_INFINITE && alpha <= VALUE_INFINITE); assert(beta > alpha && beta <= VALUE_INFINITE); + assert(PvNode || alpha == beta - 1); assert(ply >= 0 && ply < PLY_MAX); assert(threadID >= 0 && threadID < TM.active_threads()); @@ -1047,7 +1058,7 @@ namespace { oldAlpha = alpha; if (depth < OnePly) - return qsearch(pos, ss, alpha, beta, Depth(0), ply, threadID); + return qsearch(pos, ss, alpha, beta, Depth(0), ply, threadID); // Step 1. Initialize node and poll // Polling can abort search. @@ -1097,7 +1108,7 @@ namespace { isCheck = pos.is_check(); if (!isCheck) { - if (!PvNode && tte && (tte->type() & VALUE_TYPE_EVAL)) + if (tte && (tte->type() & VALUE_TYPE_EVAL)) ss[ply].eval = value_from_tt(tte->value(), ply); else ss[ply].eval = evaluate(pos, ei, threadID); @@ -1117,7 +1128,7 @@ namespace { && !pos.has_pawn_on_7th(pos.side_to_move())) { Value rbeta = beta - razor_margin(depth); - Value v = qsearch(pos, ss, rbeta-1, rbeta, Depth(0), ply, threadID); + Value v = qsearch(pos, ss, rbeta-1, rbeta, Depth(0), ply, threadID); if (v < rbeta) // Logically we should return (v + razor_margin(depth)), but // surprisingly this did slightly weaker in tests. @@ -1195,23 +1206,12 @@ namespace { } // Step 9. Internal iterative deepening - // We have different rules for PV nodes and non-pv nodes - if ( PvNode - && depth >= IIDDepthAtPVNodes - && ttMove == MOVE_NONE) - { - search(pos, ss, alpha, beta, depth-2*OnePly, ply, false, threadID); - ttMove = ss[ply].pv[ply]; - tte = TT.retrieve(posKey); - } - - if ( !PvNode - && depth >= IIDDepthAtNonPVNodes + if ( depth >= IIDDepth[PvNode] && ttMove == MOVE_NONE - && !isCheck - && ss[ply].eval >= beta - IIDMargin) + && (PvNode || (!isCheck && ss[ply].eval >= beta - IIDMargin))) { - search(pos, ss, alpha, beta, depth/2, ply, false, threadID); + Depth d = (PvNode ? depth - 2 * OnePly : depth / 2); + search(pos, ss, alpha, beta, d, ply, false, threadID); ttMove = ss[ply].pv[ply]; tte = TT.retrieve(posKey); } @@ -1240,7 +1240,7 @@ namespace { captureOrPromotion = pos.move_is_capture_or_promotion(move); // Step 11. Decide the new search depth - ext = extension(pos, move, PvNode, captureOrPromotion, moveIsCheck, singleEvasion, mateThreat, &dangerous); + ext = extension(pos, move, captureOrPromotion, moveIsCheck, singleEvasion, mateThreat, &dangerous); // Singular extension search. We extend the TT move if its value is much better than // its siblings. To verify this we do a reduced search on all the other moves but the @@ -1257,9 +1257,10 @@ namespace { if (abs(ttValue) < VALUE_KNOWN_WIN) { - Value excValue = search(pos, ss, ttValue - SingularExtensionMargin - 1, ttValue - SingularExtensionMargin, depth / 2, ply, false, threadID, move); + Value b = ttValue - SingularExtensionMargin; + Value v = search(pos, ss, b - 1, b, depth / 2, ply, false, threadID, move); - if (excValue < ttValue - SingularExtensionMargin) + if (v < ttValue - SingularExtensionMargin) ext = OnePly; } } @@ -1284,7 +1285,7 @@ namespace { continue; // Value based pruning - Depth predictedDepth = newDepth - reduction(depth, moveCount); // We illogically ignore reduction condition depth >= 3*OnePly + Depth predictedDepth = newDepth - reduction(depth, moveCount); // FIXME We illogically ignore reduction condition depth >= 3*OnePly futilityValueScaled = ss[ply].eval + futility_margin(predictedDepth, moveCount) + H.gain(pos.piece_on(move_from(move)), move_to(move)); @@ -1305,34 +1306,36 @@ namespace { value = -search(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. - bool doFullDepthSearch = true; - - if ( depth >= 3 * OnePly - && !dangerous - && !captureOrPromotion - && !move_is_castle(move) - && !move_is_killer(move, ss[ply])) - { - ss[ply].reduction = reduction(depth, moveCount); - if (ss[ply].reduction) - { - value = -search(pos, ss, -(alpha+1), -alpha, newDepth-ss[ply].reduction, ply+1, true, threadID); - doFullDepthSearch = (value > alpha); - } - } - - // Step 15. Full depth search - if (doFullDepthSearch) - { - ss[ply].reduction = Depth(0); - value = -search(pos, ss, -(alpha+1), -alpha, newDepth, ply+1, true, threadID); + // Step 14. Reduced search + // if the move fails high will be re-searched at full depth. + bool doFullDepthSearch = true; + + if ( depth >= 3 * OnePly + && !dangerous + && !captureOrPromotion + && !move_is_castle(move) + && !move_is_killer(move, ss[ply])) + { + ss[ply].reduction = reduction(depth, moveCount); + if (ss[ply].reduction) + { + value = -search(pos, ss, -(alpha+1), -alpha, newDepth-ss[ply].reduction, ply+1, true, threadID); + doFullDepthSearch = (value > alpha); + } + } - // Step extra. pv search (only in PV nodes) - if (PvNode && value > alpha && value < beta) - value = -search(pos, ss, -beta, -alpha, newDepth, ply+1, false, threadID); - } + // Step 15. Full depth search + if (doFullDepthSearch) + { + ss[ply].reduction = Depth(0); + value = -search(pos, ss, -(alpha+1), -alpha, newDepth, ply+1, true, threadID); + + // Step extra. pv search (only in PV nodes) + // Search only for possible new PV nodes, if instead value >= beta then + // parent node fails low with value <= alpha and tries another move. + if (PvNode && value > alpha && value < beta) + value = -search(pos, ss, -beta, -alpha, newDepth, ply+1, false, threadID); + } } // Step 16. Undo move @@ -1346,7 +1349,9 @@ namespace { bestValue = value; if (value > alpha) { - alpha = value; + if (PvNode && value < beta) // This guarantees that always: alpha < beta + alpha = value; + update_pv(ss, ply); if (value == value_mate_in(ply + 1)) ss[ply].mateKiller = move; @@ -1361,8 +1366,8 @@ namespace { && TM.available_thread_exists(threadID) && !AbortSearch && !TM.thread_should_stop(threadID) - && TM.split(pos, ss, ply, &alpha, beta, &bestValue, - depth, mateThreat, &moveCount, &mp, threadID, PvNode)) + && TM.split(pos, ss, ply, &alpha, beta, &bestValue, depth, + mateThreat, &moveCount, &mp, threadID, PvNode)) break; } @@ -1406,11 +1411,13 @@ namespace { // search function when the remaining depth is zero (or, to be more precise, // less than OnePly). + template Value qsearch(Position& pos, SearchStack ss[], Value alpha, Value beta, Depth depth, int ply, int threadID) { assert(alpha >= -VALUE_INFINITE && alpha <= VALUE_INFINITE); assert(beta >= -VALUE_INFINITE && beta <= VALUE_INFINITE); + assert(PvNode || alpha == beta - 1); assert(depth <= 0); assert(ply >= 0 && ply < PLY_MAX); assert(threadID >= 0 && threadID < TM.active_threads()); @@ -1422,7 +1429,6 @@ namespace { bool isCheck, enoughMaterial, moveIsCheck, evasionPrunable; const TTEntry* tte = NULL; int moveCount = 0; - bool pvNode = (beta - alpha != 1); Value oldAlpha = alpha; // Initialize, and make an early exit in case of an aborted search, @@ -1441,7 +1447,7 @@ namespace { tte = TT.retrieve(pos.get_key()); ttMove = (tte ? tte->move() : MOVE_NONE); - if (!pvNode && tte && ok_to_use_TT(tte, depth, beta, ply)) + if (!PvNode && tte && ok_to_use_TT(tte, depth, beta, ply)) { assert(tte->type() != VALUE_TYPE_EVAL); @@ -1506,9 +1512,9 @@ namespace { ss[ply].currentMove = move; // Futility pruning - if ( enoughMaterial + if ( !PvNode + && enoughMaterial && !isCheck - && !pvNode && !moveIsCheck && move != ttMove && !move_is_promotion(move) @@ -1534,8 +1540,8 @@ namespace { && !pos.can_castle(pos.side_to_move()); // Don't search moves with negative SEE values - if ( (!isCheck || evasionPrunable) - && !pvNode + if ( !PvNode + && (!isCheck || evasionPrunable) && move != ttMove && !move_is_promotion(move) && pos.see_sign(move) < 0) @@ -1543,7 +1549,7 @@ namespace { // Make and search the move pos.do_move(move, st, ci, moveIsCheck); - value = -qsearch(pos, ss, -beta, -alpha, depth-OnePly, ply+1, threadID); + value = -qsearch(pos, ss, -beta, -alpha, depth-OnePly, ply+1, threadID); pos.undo_move(move); assert(value > -VALUE_INFINITE && value < VALUE_INFINITE); @@ -1600,6 +1606,7 @@ namespace { // also don't need to store anything to the hash table here: This is taken // care of after we return from the split point. + template void sp_search(SplitPoint* sp, int threadID) { assert(threadID >= 0 && threadID < TM.active_threads()); @@ -1608,7 +1615,8 @@ namespace { StateInfo st; Move move; Depth ext, newDepth; - Value value, futilityValueScaled; + Value value; + Value futilityValueScaled; // NonPV specific bool isCheck, moveIsCheck, captureOrPromotion, dangerous; int moveCount; value = -VALUE_INFINITE; @@ -1623,8 +1631,8 @@ namespace { lock_grab(&(sp->lock)); while ( sp->bestValue < sp->beta - && !TM.thread_should_stop(threadID) - && (move = sp->mp->get_next_move()) != MOVE_NONE) + && (move = sp->mp->get_next_move()) != MOVE_NONE + && !TM.thread_should_stop(threadID)) { moveCount = ++sp->moves; lock_release(&(sp->lock)); @@ -1635,14 +1643,15 @@ namespace { captureOrPromotion = pos.move_is_capture_or_promotion(move); // Step 11. Decide the new search depth - ext = extension(pos, move, false, captureOrPromotion, moveIsCheck, false, sp->mateThreat, &dangerous); + ext = extension(pos, move, captureOrPromotion, moveIsCheck, false, sp->mateThreat, &dangerous); newDepth = sp->depth - OnePly + ext; // Update current move ss[sp->ply].currentMove = move; - // Step 12. Futility pruning - if ( !isCheck + // Step 12. Futility pruning (is omitted in PV nodes) + if ( !PvNode + && !isCheck && !dangerous && !captureOrPromotion && !move_is_castle(move)) @@ -1683,135 +1692,24 @@ namespace { && !move_is_castle(move) && !move_is_killer(move, ss[sp->ply])) { - ss[sp->ply].reduction = reduction(sp->depth, moveCount); - if (ss[sp->ply].reduction) - { - value = -search(pos, ss, -(sp->alpha+1), -(sp->alpha), newDepth-ss[sp->ply].reduction, sp->ply+1, true, threadID); - doFullDepthSearch = (value >= sp->beta && !TM.thread_should_stop(threadID)); - } - } - - // Step 15. Full depth search - if (doFullDepthSearch) - { - ss[sp->ply].reduction = Depth(0); - value = -search(pos, ss, -(sp->alpha+1), -(sp->alpha), newDepth, sp->ply+1, true, threadID); - } - - // Step 16. Undo move - pos.undo_move(move); - - assert(value > -VALUE_INFINITE && value < VALUE_INFINITE); - - // Step 17. Check for new best move - lock_grab(&(sp->lock)); - - if (value > sp->bestValue && !TM.thread_should_stop(threadID)) - { - sp->bestValue = value; - if (sp->bestValue >= sp->beta) - { - sp->stopRequest = true; - sp_update_pv(sp->parentSstack, ss, sp->ply); - } - } - } - - /* Here we have the lock still grabbed */ - - sp->slaves[threadID] = 0; - sp->cpus--; - - lock_release(&(sp->lock)); - } - - - // sp_search_pv() is used to search from a PV split point. This function - // is called by each thread working at the split point. It is similar to - // the normal search_pv() function, but simpler. Because we have already - // probed the hash table and searched the first move before splitting, we - // don't have to repeat all this work in sp_search_pv(). We also don't - // need to store anything to the hash table here: This is taken care of - // after we return from the split point. - - void sp_search_pv(SplitPoint* sp, int threadID) { - - assert(threadID >= 0 && threadID < TM.active_threads()); - assert(TM.active_threads() > 1); - - StateInfo st; - Move move; - Depth ext, newDepth; - Value value; - bool moveIsCheck, captureOrPromotion, dangerous; - int moveCount; - value = -VALUE_INFINITE; - - Position pos(*sp->pos); - CheckInfo ci(pos); - SearchStack* ss = sp->sstack[threadID]; - - // Step 10. Loop through moves - // Loop through all legal moves until no moves remain or a beta cutoff occurs - lock_grab(&(sp->lock)); - - while ( sp->alpha < sp->beta - && !TM.thread_should_stop(threadID) - && (move = sp->mp->get_next_move()) != MOVE_NONE) - { - moveCount = ++sp->moves; - lock_release(&(sp->lock)); - - assert(move_is_ok(move)); - - moveIsCheck = pos.move_is_check(move, ci); - captureOrPromotion = pos.move_is_capture_or_promotion(move); - - // Step 11. Decide the new search depth - ext = extension(pos, move, true, captureOrPromotion, moveIsCheck, false, sp->mateThreat, &dangerous); - newDepth = sp->depth - OnePly + ext; - - // Update current move - ss[sp->ply].currentMove = move; - - // Step 12. Futility pruning (is omitted in PV nodes) - - // Step 13. Make the move - pos.do_move(move, st, ci, moveIsCheck); - - // Step 14. Reduced search - // if the move fails high will be re-searched at full depth. - bool doFullDepthSearch = true; - - if ( !dangerous - && !captureOrPromotion - && !move_is_castle(move) - && !move_is_killer(move, ss[sp->ply])) - { - ss[sp->ply].reduction = reduction(sp->depth, moveCount); + ss[sp->ply].reduction = reduction(sp->depth, moveCount); if (ss[sp->ply].reduction) { Value localAlpha = sp->alpha; value = -search(pos, ss, -(localAlpha+1), -localAlpha, newDepth-ss[sp->ply].reduction, sp->ply+1, true, threadID); - doFullDepthSearch = (value > localAlpha && !TM.thread_should_stop(threadID)); + doFullDepthSearch = (value > localAlpha); } } // Step 15. Full depth search if (doFullDepthSearch) { - Value localAlpha = sp->alpha; ss[sp->ply].reduction = Depth(0); + Value localAlpha = sp->alpha; value = -search(pos, ss, -(localAlpha+1), -localAlpha, newDepth, sp->ply+1, true, threadID); - if (value > localAlpha && value < sp->beta && !TM.thread_should_stop(threadID)) - { - // If another thread has failed high then sp->alpha has been increased - // to be higher or equal then beta, if so, avoid to start a PV search. - localAlpha = sp->alpha; - if (localAlpha < sp->beta) - value = -search(pos, ss, -sp->beta, -localAlpha, newDepth, sp->ply+1, false, threadID); - } + if (PvNode && value > localAlpha && value < sp->beta) + value = -search(pos, ss, -sp->beta, -sp->alpha, newDepth, sp->ply+1, false, threadID); } // Step 16. Undo move @@ -1825,17 +1723,16 @@ namespace { if (value > sp->bestValue && !TM.thread_should_stop(threadID)) { sp->bestValue = value; - if (value > sp->alpha) + + if (sp->bestValue > sp->alpha) { - // Ask threads to stop before to modify sp->alpha - if (value >= sp->beta) + if (!PvNode || value >= sp->beta) sp->stopRequest = true; - sp->alpha = value; + if (PvNode && value < sp->beta) // This guarantees that always: sp->alpha < sp->beta + sp->alpha = value; sp_update_pv(sp->parentSstack, ss, sp->ply); - if (value == value_mate_in(sp->ply + 1)) - ss[sp->ply].mateKiller = move; } } } @@ -1848,7 +1745,6 @@ namespace { lock_release(&(sp->lock)); } - // init_node() is called at the beginning of all the search functions // (search() qsearch(), and so on) and initializes the // search stack object corresponding to the current node. Once every @@ -1875,7 +1771,6 @@ namespace { ss[ply + 2].initKillers(); } - // update_pv() is called whenever a search returns a value > alpha. // It updates the PV in the SearchStack object corresponding to the // current node. @@ -2002,9 +1897,9 @@ namespace { // any case are marked as 'dangerous'. Note that also if a move is not // extended, as example because the corresponding UCI option is set to zero, // the move is marked as 'dangerous' so, at least, we avoid to prune it. - - Depth extension(const Position& pos, Move m, bool pvNode, bool captureOrPromotion, - bool moveIsCheck, bool singleEvasion, bool mateThreat, bool* dangerous) { + template + Depth extension(const Position& pos, Move m, bool captureOrPromotion, bool moveIsCheck, + bool singleEvasion, bool mateThreat, bool* dangerous) { assert(m != MOVE_NONE); @@ -2014,13 +1909,13 @@ namespace { if (*dangerous) { if (moveIsCheck) - result += CheckExtension[pvNode]; + result += CheckExtension[PvNode]; if (singleEvasion) - result += SingleEvasionExtension[pvNode]; + result += SingleEvasionExtension[PvNode]; if (mateThreat) - result += MateThreatExtension[pvNode]; + result += MateThreatExtension[PvNode]; } if (pos.type_of_piece_on(move_from(m)) == PAWN) @@ -2028,12 +1923,12 @@ namespace { Color c = pos.side_to_move(); if (relative_rank(c, move_to(m)) == RANK_7) { - result += PawnPushTo7thExtension[pvNode]; + result += PawnPushTo7thExtension[PvNode]; *dangerous = true; } if (pos.pawn_is_passed(c, move_to(m))) { - result += PassedPawnExtension[pvNode]; + result += PassedPawnExtension[PvNode]; *dangerous = true; } } @@ -2045,11 +1940,11 @@ namespace { && !move_is_promotion(m) && !move_is_ep(m)) { - result += PawnEndgameExtension[pvNode]; + result += PawnEndgameExtension[PvNode]; *dangerous = true; } - if ( pvNode + if ( PvNode && captureOrPromotion && pos.type_of_piece_on(move_to(m)) != PAWN && pos.see_sign(m) >= 0) @@ -2502,9 +2397,9 @@ namespace { threads[threadID].state = THREAD_SEARCHING; if (threads[threadID].splitPoint->pvNode) - sp_search_pv(threads[threadID].splitPoint, threadID); + sp_search(threads[threadID].splitPoint, threadID); else - sp_search(threads[threadID].splitPoint, threadID); + sp_search(threads[threadID].splitPoint, threadID); assert(threads[threadID].state == THREAD_SEARCHING); @@ -2702,17 +2597,16 @@ namespace { // threads have returned from sp_search_pv (or, equivalently, when // splitPoint->cpus becomes 0), split() returns true. - bool 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) { - + template + bool 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) { assert(p.is_ok()); assert(sstck != NULL); assert(ply >= 0 && ply < PLY_MAX); assert(*bestValue >= -VALUE_INFINITE); - assert( ( pvNode && *bestValue <= *alpha) - || (!pvNode && *bestValue < beta )); - assert(!pvNode || *alpha < beta); + assert(*bestValue <= *alpha); + assert(*alpha < beta); assert(beta <= VALUE_INFINITE); assert(depth > Depth(0)); assert(master >= 0 && master < ActiveThreads); @@ -2760,7 +2654,7 @@ namespace { assert(threads[master].state != THREAD_AVAILABLE); // Allocate available threads setting state to THREAD_BOOKED - for (int i = 0; i < ActiveThreads && splitPoint->cpus < MaxThreadsPerSplitPoint; i++) + for (int i = 0; !Fake && i < ActiveThreads && splitPoint->cpus < MaxThreadsPerSplitPoint; i++) if (thread_is_available(i, master)) { threads[i].state = THREAD_BOOKED; @@ -2769,7 +2663,7 @@ namespace { splitPoint->cpus++; } - assert(splitPoint->cpus > 1); + assert(Fake || splitPoint->cpus > 1); // We can release the lock because slave threads are already booked and master is not available lock_release(&MPLock); @@ -2874,7 +2768,7 @@ namespace { init_ss_array(ss); pos.do_move(cur->move, st); moves[count].move = cur->move; - moves[count].score = -qsearch(pos, ss, -VALUE_INFINITE, VALUE_INFINITE, Depth(0), 1, 0); + moves[count].score = -qsearch(pos, ss, -VALUE_INFINITE, VALUE_INFINITE, Depth(0), 1, 0); moves[count].pv[0] = cur->move; moves[count].pv[1] = MOVE_NONE; pos.undo_move(cur->move);