/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
void put_threads_to_sleep();
void idle_loop(int threadID, SplitPoint* waitSp);
bool split(const Position& pos, SearchStack* ss, int ply, Value* alpha, const Value beta, Value* bestValue,
void put_threads_to_sleep();
void idle_loop(int threadID, SplitPoint* waitSp);
bool split(const Position& pos, SearchStack* ss, int ply, Value* alpha, const Value beta, Value* bestValue,
- Depth depth, int* moves, MovePicker* mp, int master, bool pvNode);
+ Depth depth, bool mateThreat, int* moves, MovePicker* mp, int master, bool pvNode);
- int RootMoveNumber, SearchStartTime, MaxNodes, MaxDepth;
- int MaxSearchTime, AbsoluteMaxSearchTime, ExtraSearchTime, ExactMaxTime;
+ int SearchStartTime, MaxNodes, MaxDepth, MaxSearchTime;
+ int AbsoluteMaxSearchTime, ExtraSearchTime, ExactMaxTime;
bool UseTimeManagement, InfiniteSearch, PonderSearch, StopOnPonderhit;
bool UseTimeManagement, InfiniteSearch, PonderSearch, StopOnPonderhit;
- bool AbortSearch, Quit, AspirationFailLow;
-
- // Show current line?
- bool ShowCurrentLine;
+ bool FirstRootMove, AbortSearch, Quit, AspirationFailLow, ZugDetection;
- Value root_search(Position& pos, SearchStack ss[], RootMoveList& rml, Value& oldAlpha, Value& beta);
+ Value root_search(Position& pos, SearchStack ss[], RootMoveList& rml, Value* alphaPtr, Value* betaPtr);
Value search_pv(Position& pos, SearchStack ss[], Value alpha, Value beta, Depth depth, int ply, int threadID);
Value search(Position& pos, SearchStack ss[], Value beta, Depth depth, int ply, bool allowNullmove, int threadID, Move excludedMove = MOVE_NONE);
Value qsearch(Position& pos, SearchStack ss[], Value alpha, Value beta, Depth depth, int ply, int threadID);
Value search_pv(Position& pos, SearchStack ss[], Value alpha, Value beta, Depth depth, int ply, int threadID);
Value search(Position& pos, SearchStack ss[], Value beta, Depth depth, int ply, bool allowNullmove, int threadID, Move excludedMove = MOVE_NONE);
Value qsearch(Position& pos, SearchStack ss[], Value alpha, Value beta, Depth depth, int ply, int threadID);
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);
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);
+ bool ok_to_use_TT(const TTEntry* tte, Depth depth, Value beta, int ply, bool allowNullmove);
Value refine_eval(const TTEntry* tte, Value defaultEval, int ply);
void update_history(const Position& pos, Move move, Depth depth, Move movesSearched[], int moveCount);
void update_killers(Move m, SearchStack& ss);
Value refine_eval(const TTEntry* tte, Value defaultEval, int ply);
void update_history(const Position& pos, Move move, Depth depth, Move movesSearched[], int moveCount);
void update_killers(Move m, SearchStack& ss);
// Initialize global search variables
StopOnPonderhit = AbortSearch = Quit = AspirationFailLow = false;
// Initialize global search variables
StopOnPonderhit = AbortSearch = Quit = AspirationFailLow = false;
MinimumSplitDepth = get_option_value_int("Minimum Split Depth") * OnePly;
MaxThreadsPerSplitPoint = get_option_value_int("Maximum Number of Threads per Split Point");
MinimumSplitDepth = get_option_value_int("Minimum Split Depth") * OnePly;
MaxThreadsPerSplitPoint = get_option_value_int("Maximum Number of Threads per Split Point");
MultiPV = get_option_value_int("MultiPV");
Chess960 = get_option_value_bool("UCI_Chess960");
UseLogFile = get_option_value_bool("Use Search Log");
MultiPV = get_option_value_int("MultiPV");
Chess960 = get_option_value_bool("UCI_Chess960");
UseLogFile = get_option_value_bool("Use Search Log");
- // Search to the current depth, rml is updated and sorted
- value = root_search(p, ss, rml, alpha, beta);
+ // Search to the current depth, rml is updated and sorted, alpha and beta could change
+ value = root_search(p, ss, rml, &alpha, &beta);
// Write PV to transposition table, in case the relevant entries have
// been overwritten during the search.
// Write PV to transposition table, in case the relevant entries have
// been overwritten during the search.
// scheme, prints some information to the standard output and handles
// the fail low/high loops.
// scheme, prints some information to the standard output and handles
// the fail low/high loops.
- Value root_search(Position& pos, SearchStack ss[], RootMoveList& rml, Value& oldAlpha, Value& beta) {
+ Value root_search(Position& pos, SearchStack ss[], RootMoveList& rml, Value* alphaPtr, Value* betaPtr) {
bool isCheck, moveIsCheck, captureOrPromotion, dangerous;
bool isCheck, moveIsCheck, captureOrPromotion, dangerous;
isCheck = pos.is_check();
// Step 1. Initialize node and poll (omitted at root, but I can see no good reason for this, FIXME)
isCheck = pos.is_check();
// Step 1. Initialize node and poll (omitted at root, but I can see no good reason for this, FIXME)
// Step 10. Loop through all moves in the root move list
for (int i = 0; i < rml.move_count() && !AbortSearch; i++)
{
// Step 10. Loop through all moves in the root move list
for (int i = 0; i < rml.move_count() && !AbortSearch; i++)
{
print_pv_info(pos, ss, alpha, beta, value);
// Prepare for a research after a fail high, each time with a wider window
print_pv_info(pos, ss, alpha, beta, value);
// Prepare for a research after a fail high, each time with a wider window
rml.set_move_nodes(i, TM.nodes_searched() - nodes);
assert(value >= -VALUE_INFINITE && value <= VALUE_INFINITE);
rml.set_move_nodes(i, TM.nodes_searched() - nodes);
assert(value >= -VALUE_INFINITE && value <= VALUE_INFINITE);
// Print information to the standard output
print_pv_info(pos, ss, alpha, beta, value);
// Print information to the standard output
print_pv_info(pos, ss, alpha, beta, value);
// Initialize a MovePicker object for the current position
mateThreat = pos.has_mate_threat(opposite_color(pos.side_to_move()));
MovePicker mp = MovePicker(pos, ttMove, depth, H, &ss[ply]);
CheckInfo ci(pos);
// Initialize a MovePicker object for the current position
mateThreat = pos.has_mate_threat(opposite_color(pos.side_to_move()));
MovePicker mp = MovePicker(pos, ttMove, depth, H, &ss[ply]);
CheckInfo ci(pos);
&& !AbortSearch
&& !TM.thread_should_stop(threadID)
&& TM.split(pos, ss, ply, &alpha, beta, &bestValue,
&& !AbortSearch
&& !TM.thread_should_stop(threadID)
&& TM.split(pos, ss, ply, &alpha, beta, &bestValue,
- depth, &moveCount, &mp, threadID, true))
+ depth, mateThreat, &moveCount, &mp, threadID, true))
- if (tte && ok_to_use_TT(tte, depth, beta, ply))
+ if (tte && ok_to_use_TT(tte, depth, beta, ply, allowNullmove))
{
ss[ply].currentMove = ttMove; // Can be MOVE_NONE
return value_from_tt(tte->value(), ply);
{
ss[ply].currentMove = ttMove; // Can be MOVE_NONE
return value_from_tt(tte->value(), ply);
&& !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);
if (v < rbeta)
&& !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);
if (v < rbeta)
}
// Step 7. Static null move pruning
// We're betting that the opponent doesn't have a move that will reduce
}
// Step 7. Static null move pruning
// We're betting that the opponent doesn't have a move that will reduce
- // the score by more than fuility_margin(depth) if we do a null move.
- if ( !isCheck
- && allowNullmove
- && depth < RazorDepth
- && refinedValue - futility_margin(depth, 0) >= beta)
+ // the score by more than futility_margin(depth) if we do a null move.
+ if ( allowNullmove
+ && depth < RazorDepth
+ && !isCheck
+ && !value_is_mate(beta)
+ && ok_to_do_nullmove(pos)
+ && refinedValue >= beta + futility_margin(depth, 0))
return refinedValue - futility_margin(depth, 0);
// Step 8. Null move search with verification search
return refinedValue - futility_margin(depth, 0);
// Step 8. Null move search with verification search
nullValue = -search(pos, ss, -(beta-1), depth-R*OnePly, ply+1, false, threadID);
pos.undo_null_move();
if (nullValue >= beta)
{
nullValue = -search(pos, ss, -(beta-1), depth-R*OnePly, ply+1, false, threadID);
pos.undo_null_move();
if (nullValue >= beta)
{
- if (depth < 6 * OnePly)
- return beta;
+ // Do not return unproven mate scores
+ if (nullValue >= value_mate_in(PLY_MAX))
+ nullValue = beta;
+
+ // Do zugzwang verification search for high depths, don't store in TT
+ // if search was stopped.
+ if ( ( depth < 6 * OnePly
+ || search(pos, ss, beta, depth-5*OnePly, ply, false, threadID) >= beta)
+ && !AbortSearch
+ && !TM.thread_should_stop(threadID))
+ {
+ assert(value_to_tt(nullValue, ply) == nullValue);
- // Do zugzwang verification search
- Value v = search(pos, ss, beta, depth-5*OnePly, ply, false, threadID);
- if (v >= beta)
- return beta;
+ TT.store(posKey, nullValue, VALUE_TYPE_NS_LO, depth, MOVE_NONE);
+ return nullValue;
+ }
} else {
// The null move failed low, which means that we may be faced with
// some kind of threat. If the previous move was reduced, check if
} else {
// The null move failed low, which means that we may be faced with
// some kind of threat. If the previous move was reduced, check if
// Initialize a MovePicker object for the current position
MovePicker mp = MovePicker(pos, ttMove, depth, H, &ss[ply], beta);
CheckInfo ci(pos);
// Initialize a MovePicker object for the current position
MovePicker mp = MovePicker(pos, ttMove, depth, H, &ss[ply], beta);
CheckInfo ci(pos);
// Step 13. Make the move
pos.do_move(move, st, ci, moveIsCheck);
// 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.
+ // Step 14. Reduced search, if the move fails high
+ // will be re-searched at full depth.
&& !AbortSearch
&& !TM.thread_should_stop(threadID)
&& TM.split(pos, ss, ply, NULL, beta, &bestValue,
&& !AbortSearch
&& !TM.thread_should_stop(threadID)
&& TM.split(pos, ss, ply, NULL, beta, &bestValue,
- depth, &moveCount, &mp, threadID, false))
+ depth, mateThreat, &moveCount, &mp, threadID, false))
// Step 20. Update tables
// If the search is not aborted, update the transposition table,
// Step 20. Update tables
// If the search is not aborted, update the transposition table,
- if (!pvNode && tte && ok_to_use_TT(tte, depth, beta, ply))
+ if (!pvNode && tte && ok_to_use_TT(tte, depth, beta, ply, true))
// Initialize a MovePicker object for the current position, and prepare
// to search the moves. Because the depth is <= 0 here, only captures,
// Initialize a MovePicker object for the current position, and prepare
// to search the moves. Because the depth is <= 0 here, only captures,
enoughMaterial = pos.non_pawn_material(pos.side_to_move()) > RookValueMidgame;
futilityBase = staticValue + FutilityMarginQS + ei.futilityMargin[pos.side_to_move()];
enoughMaterial = pos.non_pawn_material(pos.side_to_move()) > RookValueMidgame;
futilityBase = staticValue + FutilityMarginQS + ei.futilityMargin[pos.side_to_move()];
// All legal moves have been searched. A special case: If we're in check
// and no legal moves were found, it is checkmate.
// All legal moves have been searched. A special case: If we're in check
// and no legal moves were found, it is checkmate.
// splitting, we don't have to repeat all this work in sp_search(). 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.
// splitting, we don't have to repeat all this work in sp_search(). 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.
- ext = extension(pos, move, false, captureOrPromotion, moveIsCheck, false, false, &dangerous);
+ ext = extension(pos, move, false, captureOrPromotion, moveIsCheck, false, sp->mateThreat, &dangerous);
// 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.
// 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.
- ext = extension(pos, move, true, captureOrPromotion, moveIsCheck, false, false, &dangerous);
+ ext = extension(pos, move, true, captureOrPromotion, moveIsCheck, false, sp->mateThreat, &dangerous);
- // ok_to_use_TT() returns true if a transposition table score
- // can be used at a given point in search.
+ // ok_to_use_TT() returns true if a transposition table score can be used at a
+ // given point in search. To avoid zugzwang issues TT cutoffs at the root node
+ // of a null move verification search are not allowed if the TT value was found
+ // by a null search, this is implemented testing allowNullmove and TT entry type.
- bool ok_to_use_TT(const TTEntry* tte, Depth depth, Value beta, int ply) {
+ bool ok_to_use_TT(const TTEntry* tte, Depth depth, Value beta, int ply, bool allowNullmove) {
|| v >= Max(value_mate_in(PLY_MAX), beta)
|| v < Min(value_mated_in(PLY_MAX), beta))
|| v >= Max(value_mate_in(PLY_MAX), beta)
|| v < Min(value_mated_in(PLY_MAX), beta))
cout << "info nodes " << TM.nodes_searched() << " nps " << nps()
<< " time " << t << " hashfull " << TT.full() << endl;
cout << "info nodes " << TM.nodes_searched() << " nps " << nps()
<< " time " << t << " hashfull " << TT.full() << endl;
-
- // We only support current line printing in single thread mode
- if (ShowCurrentLine && TM.active_threads() == 1)
- {
- cout << "info currline";
- for (int p = 0; p < ply; p++)
- cout << " " << ss[p].currentMove;
-
- cout << endl;
- }
bool ThreadsManager::split(const Position& p, SearchStack* sstck, int ply,
Value* alpha, const Value beta, Value* bestValue,
bool ThreadsManager::split(const Position& p, SearchStack* sstck, int ply,
Value* alpha, const Value beta, Value* bestValue,
- Depth depth, int* moves, MovePicker* mp, int master, bool pvNode) {
+ Depth depth, bool mateThreat, int* moves, MovePicker* mp, int master, bool pvNode) {