/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
- Copyright (C) 2008 Marco Costalba
+ Copyright (C) 2008-2009 Marco Costalba
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
// Apart for the first one that has its score, following moves
// normally have score -VALUE_INFINITE, so are ordered according
// to the number of beta cutoffs occurred under their subtree during
- // the last iteration.
+ // the last iteration. The counters are per thread variables to avoid
+ // concurrent accessing under SMP case.
struct BetaCounterType {
void clear();
void add(Color us, Depth d, int threadID);
void read(Color us, int64_t& our, int64_t& their);
-
- int64_t hits[THREAD_MAX][2];
};
};
- /// Constants and variables initialized from UCI options
-
- // Minimum number of full depth (i.e. non-reduced) moves at PV and non-PV
- // nodes
- int LMRPVMoves, LMRNonPVMoves;
-
- // Depth limit for use of dynamic threat detection
- Depth ThreatDepth;
+ /// Constants
// Depth limit for selective search
- Depth SelectiveDepth;
+ const Depth SelectiveDepth = 7*OnePly;
// Use internal iterative deepening?
const bool UseIIDAtPVNodes = true;
const bool PruneDefendingMoves = false;
const bool PruneBlockingMoves = false;
- // Use futility pruning?
- bool UseQSearchFutilityPruning, UseFutilityPruning;
-
// Margins for futility pruning in the quiescence search, and at frontier
// and near frontier nodes
const Value FutilityMarginQS = Value(0x80);
- // Remaining depth: 1 ply 1.5 ply 2 ply 2.5 ply 3 ply 3.5 ply
- const Value FutilityMargins[12] = { Value(0x100), Value(0x120), Value(0x200), Value(0x220), Value(0x250), Value(0x270),
+ // Remaining depth: 1 ply 1.5 ply 2 ply 2.5 ply 3 ply 3.5 ply
+ const Value FutilityMargins[12] = { Value(0x100), Value(0x120), Value(0x200), Value(0x220), Value(0x250), Value(0x270),
// 4 ply 4.5 ply 5 ply 5.5 ply 6 ply 6.5 ply
Value(0x2A0), Value(0x2C0), Value(0x340), Value(0x360), Value(0x3A0), Value(0x3C0) };
- // Razoring
- const Depth RazorDepth = 4*OnePly;
+ // Razoring
+ const Depth RazorDepth = 4*OnePly;
// Remaining depth: 1 ply 1.5 ply 2 ply 2.5 ply 3 ply 3.5 ply
const Value RazorMargins[6] = { Value(0x180), Value(0x300), Value(0x300), Value(0x3C0), Value(0x3C0), Value(0x3C0) };
// Remaining depth: 1 ply 1.5 ply 2 ply 2.5 ply 3 ply 3.5 ply
- const Value RazorApprMargins[6] = { Value(0x520), Value(0x300), Value(0x300), Value(0x300), Value(0x300), Value(0x300) };
+ const Value RazorApprMargins[6] = { Value(0x520), Value(0x300), Value(0x300), Value(0x300), Value(0x300), Value(0x300) };
+
+
+ /// Variables initialized from UCI options
+
+ // Minimum number of full depth (i.e. non-reduced) moves at PV and non-PV
+ // nodes
+ int LMRPVMoves, LMRNonPVMoves;
+
+ // Depth limit for use of dynamic threat detection
+ Depth ThreatDepth;
// Last seconds noise filtering (LSN)
bool UseLSNFiltering;
// Search depth at iteration 1
const Depth InitialDepth = OnePly /*+ OnePly/2*/;
- // Node counters
+ // Node counters, used only by thread[0]
int NodesSincePoll;
int NodesBetweenPolls = 30000;
// Iteration counters
int Iteration;
- BetaCounterType BetaCounter;
+ BetaCounterType BetaCounter; // does not have internal data
- // Scores and number of times the best move changed for each iteration:
+ // Scores and number of times the best move changed for each iteration
IterationInfoType IterationInfo[PLY_MAX_PLUS_2];
int BestMoveChangesByIteration[PLY_MAX_PLUS_2];
bool move_is_killer(Move m, const SearchStack& ss);
Depth extension(const Position &pos, Move m, bool pvNode, bool capture, bool check, bool singleReply, bool mateThreat, bool* dangerous);
bool ok_to_do_nullmove(const Position &pos);
- bool ok_to_prune(const Position &pos, Move m, Move threat, Depth d);
+ bool ok_to_prune(const Position &pos, Move m, Move threat, Depth d, const History& H);
bool ok_to_use_TT(const TTEntry* tte, Depth depth, Value beta, int ply);
bool ok_to_history(const Position &pos, Move m);
- void update_history(const Position& pos, Move m, Depth depth, Move movesSearched[], int moveCount);
+ void update_history(const Position& pos, Move m, Depth depth, History& H, Move movesSearched[], int moveCount);
void update_killers(Move m, SearchStack& ss);
bool fail_high_ply_1();
////
// The main transposition table
-TranspositionTable TT = TranspositionTable(TTDefaultSize);
+TranspositionTable TT;
// Number of active threads:
// but it could turn out to be useful for debugging.
Lock IOLock;
-History H; // Should be made local?
-
-// The empty search stack
-SearchStack EmptySearchStack;
-
// SearchStack::init() initializes a search stack. Used at the beginning of a
// new search from the root.
////
/// think() is the external interface to Stockfish's search, and is called when
-/// the program receives the UCI 'go' command. It initializes various
-/// search-related global variables, and calls root_search()
+/// the program receives the UCI 'go' command. It initializes various
+/// search-related global variables, and calls root_search(). It returns false
+/// when a quit command is received during the search.
-void think(const Position &pos, bool infinite, bool ponder, int side_to_move,
+bool think(const Position &pos, bool infinite, bool ponder, int side_to_move,
int time[], int increment[], int movesToGo, int maxDepth,
int maxNodes, int maxTime, Move searchMoves[]) {
{
Move bookMove;
if (get_option_value_string("Book File") != OpeningBook.file_name())
- {
- OpeningBook.close();
OpeningBook.open("book.bin");
- }
+
bookMove = OpeningBook.get_move(pos);
if (bookMove != MOVE_NONE)
{
std::cout << "bestmove " << bookMove << std::endl;
- return;
+ return true;
}
}
LMRPVMoves = get_option_value_int("Full Depth Moves (PV nodes)") + 1;
LMRNonPVMoves = get_option_value_int("Full Depth Moves (non-PV nodes)") + 1;
ThreatDepth = get_option_value_int("Threat Depth") * OnePly;
- SelectiveDepth = get_option_value_int("Selective Plies") * OnePly;
Chess960 = get_option_value_bool("UCI_Chess960");
ShowCurrentLine = get_option_value_bool("UCI_ShowCurrLine");
if (UseLogFile)
LogFile.open(get_option_value_string("Search Log Filename").c_str(), std::ios::out | std::ios::app);
- UseQSearchFutilityPruning = get_option_value_bool("Futility Pruning (Quiescence Search)");
- UseFutilityPruning = get_option_value_bool("Futility Pruning (Main Search)");
-
UseLSNFiltering = get_option_value_bool("LSN filtering");
LSNTime = get_option_value_int("LSN Time Margin (sec)") * 1000;
LSNValue = value_from_centipawns(get_option_value_int("LSN Value Margin"));
if (UseLogFile)
LogFile.close();
- if (Quit)
- {
- OpeningBook.close();
- stop_threads();
- quit_eval();
- exit(0);
- }
Idle = true;
+ return !Quit;
}
// Wait until the thread has finished launching:
while (!Threads[i].running);
}
-
- // Init also the empty search stack
- EmptySearchStack.init(0);
- EmptySearchStack.initKillers();
}
// Initialize
TT.new_search();
- H.clear();
+ for (int i = 0; i < THREAD_MAX; i++)
+ Threads[i].H.clear();
+
for (int i = 0; i < 3; i++)
{
ss[i].init(i);
if (i < MultiPV)
{
+ // Aspiration window is disabled in multi-pv case
+ if (MultiPV > 1)
+ alpha = -VALUE_INFINITE;
+
value = -search_pv(pos, ss, -beta, -alpha, newDepth, 1, 0);
// If the value has dropped a lot compared to the last iteration,
// set the boolean variable Problem to true. This variable is used
// Transposition table lookup. At PV nodes, we don't use the TT for
// pruning, but only for move ordering.
- const TTEntry* tte = TT.retrieve(pos);
+ const TTEntry* tte = TT.retrieve(pos.get_key());
Move ttMove = (tte ? tte->move() : MOVE_NONE);
// Go with internal iterative deepening if we don't have a TT move
// Initialize a MovePicker object for the current position, and prepare
// to search all moves
- MovePicker mp = MovePicker(pos, true, ttMove, ss[ply], depth);
+ MovePicker mp = MovePicker(pos, true, ttMove, depth, Threads[threadID].H, &ss[ply]);
Move move, movesSearched[256];
int moveCount = 0;
return bestValue;
if (bestValue <= oldAlpha)
- TT.store(pos, value_to_tt(bestValue, ply), depth, MOVE_NONE, VALUE_TYPE_UPPER);
+ TT.store(pos.get_key(), value_to_tt(bestValue, ply), VALUE_TYPE_UPPER, depth, MOVE_NONE);
else if (bestValue >= beta)
{
Move m = ss[ply].pv[ply];
if (ok_to_history(pos, m)) // Only non capture moves are considered
{
- update_history(pos, m, depth, movesSearched, moveCount);
+ update_history(pos, m, depth, Threads[threadID].H, movesSearched, moveCount);
update_killers(m, ss[ply]);
}
- TT.store(pos, value_to_tt(bestValue, ply), depth, m, VALUE_TYPE_LOWER);
+ TT.store(pos.get_key(), value_to_tt(bestValue, ply), VALUE_TYPE_LOWER, depth, m);
}
else
- TT.store(pos, value_to_tt(bestValue, ply), depth, ss[ply].pv[ply], VALUE_TYPE_EXACT);
+ TT.store(pos.get_key(), value_to_tt(bestValue, ply), VALUE_TYPE_EXACT, depth, ss[ply].pv[ply]);
return bestValue;
}
return beta - 1;
// Transposition table lookup
- const TTEntry* tte = TT.retrieve(pos);
+ const TTEntry* tte = TT.retrieve(pos.get_key());
Move ttMove = (tte ? tte->move() : MOVE_NONE);
if (tte && ok_to_use_TT(tte, depth, beta, ply))
else if ( !value_is_mate(beta)
&& depth < RazorDepth
&& approximateEval < beta - RazorApprMargins[int(depth) - 2]
+ && ss[ply - 1].currentMove != MOVE_NULL
&& ttMove == MOVE_NONE
&& !pos.has_pawn_on_7th(pos.side_to_move()))
{
// Initialize a MovePicker object for the current position, and prepare
// to search all moves:
- MovePicker mp = MovePicker(pos, false, ttMove, ss[ply], depth);
+ MovePicker mp = MovePicker(pos, false, ttMove, depth, Threads[threadID].H, &ss[ply]);
Move move, movesSearched[256];
int moveCount = 0;
Value value, bestValue = -VALUE_INFINITE;
Bitboard dcCandidates = mp.discovered_check_candidates();
Value futilityValue = VALUE_NONE;
- bool useFutilityPruning = UseFutilityPruning
- && depth < SelectiveDepth
+ bool useFutilityPruning = depth < SelectiveDepth
&& !isCheck;
// Loop through all legal moves until no moves remain or a beta cutoff
{
// History pruning. See ok_to_prune() definition
if ( moveCount >= 2 + int(depth)
- && ok_to_prune(pos, move, ss[ply].threatMove, depth))
+ && ok_to_prune(pos, move, ss[ply].threatMove, depth, Threads[threadID].H))
continue;
// Value based pruning
- if (depth < 7 * OnePly && approximateEval < beta)
+ if (approximateEval < beta)
{
if (futilityValue == VALUE_NONE)
futilityValue = evaluate(pos, ei, threadID)
return bestValue;
if (bestValue < beta)
- TT.store(pos, value_to_tt(bestValue, ply), depth, MOVE_NONE, VALUE_TYPE_UPPER);
+ TT.store(pos.get_key(), value_to_tt(bestValue, ply), VALUE_TYPE_UPPER, depth, MOVE_NONE);
else
{
BetaCounter.add(pos.side_to_move(), depth, threadID);
Move m = ss[ply].pv[ply];
if (ok_to_history(pos, m)) // Only non capture moves are considered
{
- update_history(pos, m, depth, movesSearched, moveCount);
+ update_history(pos, m, depth, Threads[threadID].H, movesSearched, moveCount);
update_killers(m, ss[ply]);
}
- TT.store(pos, value_to_tt(bestValue, ply), depth, m, VALUE_TYPE_LOWER);
+ TT.store(pos.get_key(), value_to_tt(bestValue, ply), VALUE_TYPE_LOWER, depth, m);
}
assert(bestValue > -VALUE_INFINITE && bestValue < VALUE_INFINITE);
bool pvNode = (beta - alpha != 1);
if (!pvNode)
{
- tte = TT.retrieve(pos);
+ tte = TT.retrieve(pos.get_key());
if (tte && ok_to_use_TT(tte, depth, beta, ply))
{
assert(tte->type() != VALUE_TYPE_EVAL);
{
// Store the score to avoid a future costly evaluation() call
if (!isCheck && !tte && ei.futilityMargin == 0)
- TT.store(pos, value_to_tt(bestValue, ply), Depth(-127*OnePly), MOVE_NONE, VALUE_TYPE_EVAL);
+ TT.store(pos.get_key(), value_to_tt(bestValue, ply), VALUE_TYPE_EVAL, Depth(-127*OnePly), MOVE_NONE);
return bestValue;
}
// Initialize a MovePicker object 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 == 0) will be generated.
- MovePicker mp = MovePicker(pos, pvNode, ttMove, EmptySearchStack, depth);
+ MovePicker mp = MovePicker(pos, pvNode, ttMove, depth, Threads[threadID].H);
Move move;
int moveCount = 0;
Bitboard dcCandidates = mp.discovered_check_candidates();
ss[ply].currentMove = move;
// Futility pruning
- if ( UseQSearchFutilityPruning
- && enoughMaterial
+ if ( enoughMaterial
&& !isCheck
&& !pvNode
&& !move_promotion(move)
{
Depth d = (depth == Depth(0) ? Depth(0) : Depth(-1));
if (bestValue < beta)
- TT.store(pos, value_to_tt(bestValue, ply), d, MOVE_NONE, VALUE_TYPE_UPPER);
+ TT.store(pos.get_key(), value_to_tt(bestValue, ply), VALUE_TYPE_UPPER, d, MOVE_NONE);
else
- TT.store(pos, value_to_tt(bestValue, ply), d, m, VALUE_TYPE_LOWER);
+ TT.store(pos.get_key(), value_to_tt(bestValue, ply), VALUE_TYPE_LOWER, d, m);
}
// Update killers only for good check moves
Value value;
Move move;
bool isCheck = pos.is_check();
- bool useFutilityPruning = UseFutilityPruning
- && sp->depth < SelectiveDepth
+ bool useFutilityPruning = sp->depth < SelectiveDepth
&& !isCheck;
while ( sp->bestValue < sp->beta
&& !moveIsCapture
&& !move_promotion(move)
&& moveCount >= 2 + int(sp->depth)
- && ok_to_prune(pos, move, ss[sp->ply].threatMove, sp->depth))
+ && ok_to_prune(pos, move, ss[sp->ply].threatMove, sp->depth, Threads[threadID].H))
continue;
// Make and search the move.
void BetaCounterType::clear() {
for (int i = 0; i < THREAD_MAX; i++)
- hits[i][WHITE] = hits[i][BLACK] = 0ULL;
+ Threads[i].betaCutOffs[WHITE] = Threads[i].betaCutOffs[BLACK] = 0ULL;
}
void BetaCounterType::add(Color us, Depth d, int threadID) {
// Weighted count based on depth
- hits[threadID][us] += int(d);
+ Threads[threadID].betaCutOffs[us] += unsigned(d);
}
void BetaCounterType::read(Color us, int64_t& our, int64_t& their) {
our = their = 0UL;
for (int i = 0; i < THREAD_MAX; i++)
{
- our += hits[i][us];
- their += hits[i][opposite_color(us)];
+ our += Threads[i].betaCutOffs[us];
+ their += Threads[i].betaCutOffs[opposite_color(us)];
}
}
// Constructor
RootMove::RootMove() {
- nodes = cumulativeNodes = 0ULL;
+ nodes = cumulativeNodes = ourBeta = theirBeta = 0ULL;
}
// RootMove::operator<() is the comparison function used when
for (int k = 0; !includeMove && searchMoves[k] != MOVE_NONE; k++)
includeMove = (searchMoves[k] == mlist[i].move);
- if (includeMove)
- {
- // Find a quick score for the move
- StateInfo st;
- SearchStack ss[PLY_MAX_PLUS_2];
-
- moves[count].move = mlist[i].move;
- moves[count].nodes = 0ULL;
- pos.do_move(moves[count].move, st);
- moves[count].score = -qsearch(pos, ss, -VALUE_INFINITE, VALUE_INFINITE,
- Depth(0), 1, 0);
- pos.undo_move(moves[count].move);
- moves[count].pv[0] = moves[i].move;
- moves[count].pv[1] = MOVE_NONE; // FIXME
- count++;
- }
+ if (!includeMove)
+ continue;
+
+ // Find a quick score for the move
+ StateInfo st;
+ SearchStack ss[PLY_MAX_PLUS_2];
+
+ moves[count].move = mlist[i].move;
+ pos.do_move(moves[count].move, st);
+ moves[count].score = -qsearch(pos, ss, -VALUE_INFINITE, VALUE_INFINITE, Depth(0), 1, 0);
+ pos.undo_move(moves[count].move);
+ moves[count].pv[0] = moves[count].move;
+ moves[count].pv[1] = MOVE_NONE; // FIXME
+ count++;
}
sort();
}
// non-tactical moves late in the move list close to the leaves are
// candidates for pruning.
- bool ok_to_prune(const Position &pos, Move m, Move threat, Depth d) {
+ bool ok_to_prune(const Position &pos, Move m, Move threat, Depth d, const History& H) {
Square mfrom, mto, tfrom, tto;
assert(move_is_ok(m));
return false;
// Case 4: Don't prune moves with good history.
- if (!H.ok_to_prune(pos.piece_on(move_from(m)), m, d))
+ if (!H.ok_to_prune(pos.piece_on(mfrom), mto, d))
return false;
// Case 5: If the moving piece in the threatened move is a slider, don't
// update_history() registers a good move that produced a beta-cutoff
// in history and marks as failures all the other moves of that ply.
- void update_history(const Position& pos, Move m, Depth depth,
+ void update_history(const Position& pos, Move m, Depth depth, History& H,
Move movesSearched[], int moveCount) {
- H.success(pos.piece_on(move_from(m)), m, depth);
+ H.success(pos.piece_on(move_from(m)), move_to(m), depth);
for (int i = 0; i < moveCount - 1; i++)
{
assert(m != movesSearched[i]);
if (ok_to_history(pos, movesSearched[i]))
- H.failure(pos.piece_on(move_from(movesSearched[i])), movesSearched[i]);
+ H.failure(pos.piece_on(move_from(movesSearched[i])), move_to(movesSearched[i]));
}
}
AbortSearch = true;
PonderSearch = false;
Quit = true;
+ return;
}
else if(command == "stop")
{
// after which the bestmove and pondermove will be printed (in id_loop()).
void wait_for_stop_or_ponderhit() {
+
std::string command;
- while(true) {
- if(!std::getline(std::cin, command))
- command = "quit";
-
- if(command == "quit") {
- OpeningBook.close();
- stop_threads();
- quit_eval();
- exit(0);
- }
- else if(command == "ponderhit" || command == "stop")
- break;
+ while (true)
+ {
+ if (!std::getline(std::cin, command))
+ command = "quit";
+
+ if (command == "quit")
+ {
+ Quit = true;
+ break;
+ }
+ else if(command == "ponderhit" || command == "stop")
+ break;
}
}