/** * Principle Variation Search (root node) * Difference with normal (non-root) search: * - Sending new PVs asap to the interface * - Sort order based on pv and amount of nodes of the subtrees * - No pruning and hash table lookup/store * - Compatible with all supported wild variants */ int search_t::pvs_root(int alpha, int beta, int depth) { assert(root.move_count > 0); int best = -score::INF; const bool is_pv = true; root.sort_moves(&stack->best_move); /* * Moves loop */ for (int i = 0; i < root.move_count; i++) { root_move_t * rmove = &root.moves[i]; move_t * move = &rmove->move; int nodes_before = nodes; //extensions and reductions int extend = extension(move, depth, is_pv, rmove->gives_check); int reduce = reduction(depth, i, rmove->is_dangerous); int score = 0; //go forward and search one level deeper forward(move, rmove->gives_check); if (i == 0) { score = -pvs(-beta, -alpha, depth - 1 + extend); } else { score = -pvs(-alpha - 1, -alpha, depth - 1 - reduce + extend); if (score > alpha) { //open window research w/o reductions score = -pvs(-beta, -alpha, depth - 1 + extend); } } backward(move); //handle results rmove->nodes += nodes - nodes_before; if (stop_all) { return alpha; } else if (score > best) { best = score; result_score = score; rmove->nodes += i; stack->best_move.set(move); bool exact = score::flags(score, alpha, beta) == score::EXACT; if (exact || false == move->equals(&stack->pv_moves[0])) { update_pv(&rmove->move); } uci::send_pv(best, depth, sel_depth, nodes + pruned_nodes, game->tm.elapsed(), pv_to_string().c_str(), score::flags(best, alpha, beta)); if (!exact) { //adjust asp. window return score; } assert(alpha < best); alpha = best; } } return best; }
Value name_NT_InCheck(qsearch)(Pos* pos, Stack* ss, Value alpha, BETA_ARG Depth depth) { assert(InCheck == !!pos_checkers()); assert(alpha >= -VALUE_INFINITE && alpha < beta && beta <= VALUE_INFINITE); assert(PvNode || (alpha == beta - 1)); assert(depth <= DEPTH_ZERO); Move pv[MAX_PLY+1]; TTEntry *tte; Key posKey; Move ttMove, move, bestMove; Value bestValue, value, ttValue, futilityValue, futilityBase, oldAlpha; int ttHit, ttPv, givesCheck, evasionPrunable; Depth ttDepth; int moveCount; if (PvNode) { oldAlpha = alpha; // To flag BOUND_EXACT when eval above alpha and no available moves (ss+1)->pv = pv; ss->pv[0] = 0; } bestMove = 0; moveCount = 0; // Check for an instant draw or if the maximum ply has been reached if (is_draw(pos) || ss->ply >= MAX_PLY) return ss->ply >= MAX_PLY && !InCheck ? evaluate(pos) : VALUE_DRAW; assert(0 <= ss->ply && ss->ply < MAX_PLY); // Decide whether or not to include checks: this fixes also the type of // TT entry depth that we are going to use. Note that in qsearch we use // only two types of depth in TT: DEPTH_QS_CHECKS or DEPTH_QS_NO_CHECKS. ttDepth = InCheck || depth >= DEPTH_QS_CHECKS ? DEPTH_QS_CHECKS : DEPTH_QS_NO_CHECKS; // Transposition table lookup posKey = pos_key(); tte = tt_probe(posKey, &ttHit); ttValue = ttHit ? value_from_tt(tte_value(tte), ss->ply) : VALUE_NONE; ttMove = ttHit ? tte_move(tte) : 0; ttPv = ttHit ? tte_is_pv(tte) : 0; if ( !PvNode && ttHit && tte_depth(tte) >= ttDepth && ttValue != VALUE_NONE // Only in case of TT access race && (ttValue >= beta ? (tte_bound(tte) & BOUND_LOWER) : (tte_bound(tte) & BOUND_UPPER))) return ttValue; // Evaluate the position statically if (InCheck) { ss->staticEval = VALUE_NONE; bestValue = futilityBase = -VALUE_INFINITE; } else { if (ttHit) { // Never assume anything on values stored in TT if ((ss->staticEval = bestValue = tte_eval(tte)) == VALUE_NONE) ss->staticEval = bestValue = evaluate(pos); // Can ttValue be used as a better position evaluation? if (ttValue != VALUE_NONE) if (tte_bound(tte) & (ttValue > bestValue ? BOUND_LOWER : BOUND_UPPER)) bestValue = ttValue; } else ss->staticEval = bestValue = (ss-1)->currentMove != MOVE_NULL ? evaluate(pos) : -(ss-1)->staticEval + 2 * Tempo; // Stand pat. Return immediately if static value is at least beta if (bestValue >= beta) { if (!ttHit) tte_save(tte, posKey, value_to_tt(bestValue, ss->ply), ttPv, BOUND_LOWER, DEPTH_NONE, 0, ss->staticEval, tt_generation()); return bestValue; } if (PvNode && bestValue > alpha) alpha = bestValue; futilityBase = bestValue + 128; } ss->history = &(*pos->counterMoveHistory)[0][0]; // Initialize move picker data for the current position, and prepare // to search the moves. Because the depth is <= 0 here, only captures, // queen promotions and checks (only if depth >= DEPTH_QS_CHECKS) will // be generated. mp_init_q(pos, ttMove, depth, to_sq((ss-1)->currentMove)); // Loop through the moves until no moves remain or a beta cutoff occurs while ((move = next_move(pos, 0))) { assert(move_is_ok(move)); givesCheck = gives_check(pos, ss, move); moveCount++; // Futility pruning if ( !InCheck && !givesCheck && futilityBase > -VALUE_KNOWN_WIN && !advanced_pawn_push(pos, move)) { assert(type_of_m(move) != ENPASSANT); // Due to !advanced_pawn_push futilityValue = futilityBase + PieceValue[EG][piece_on(to_sq(move))]; if (futilityValue <= alpha) { bestValue = max(bestValue, futilityValue); continue; } if (futilityBase <= alpha && !see_test(pos, move, 1)) { bestValue = max(bestValue, futilityBase); continue; } } // Detect non-capture evasions that are candidates to be pruned evasionPrunable = InCheck && (depth != DEPTH_ZERO || moveCount > 2) && bestValue > VALUE_MATED_IN_MAX_PLY && !is_capture(pos, move); // Don't search moves with negative SEE values if ( (!InCheck || evasionPrunable) && !see_test(pos, move, 0)) continue; // Speculative prefetch as early as possible prefetch(tt_first_entry(key_after(pos, move))); // Check for legality just before making the move if (!is_legal(pos, move)) { moveCount--; continue; } ss->currentMove = move; ss->history = &(*pos->counterMoveHistory)[moved_piece(move)][to_sq(move)]; // Make and search the move do_move(pos, move, givesCheck); #if PvNode value = givesCheck ? -qsearch_PV_true(pos, ss+1, -beta, -alpha, depth - ONE_PLY) : -qsearch_PV_false(pos, ss+1, -beta, -alpha, depth - ONE_PLY); #else value = givesCheck ? -qsearch_NonPV_true(pos, ss+1, -beta, depth - ONE_PLY) : -qsearch_NonPV_false(pos, ss+1, -beta, depth - ONE_PLY); #endif undo_move(pos, move); assert(value > -VALUE_INFINITE && value < VALUE_INFINITE); // Check for a new best move if (value > bestValue) { bestValue = value; if (value > alpha) { bestMove = move; if (PvNode) // Update pv even in fail-high case update_pv(ss->pv, move, (ss+1)->pv); if (PvNode && value < beta) // Update alpha here! alpha = value; else break; // Fail high } } } // All legal moves have been searched. A special case: If we're in check // and no legal moves were found, it is checkmate. if (InCheck && bestValue == -VALUE_INFINITE) return mated_in(ss->ply); // Plies to mate from the root tte_save(tte, posKey, value_to_tt(bestValue, ss->ply), ttPv, bestValue >= beta ? BOUND_LOWER : PvNode && bestValue > oldAlpha ? BOUND_EXACT : BOUND_UPPER, ttDepth, bestMove, ss->staticEval, tt_generation()); assert(bestValue > -VALUE_INFINITE && bestValue < VALUE_INFINITE); return bestValue; }