bool ok_to_do_nullmove(const Position& pos);
bool ok_to_prune(const Position& pos, Move m, Move threat, Depth d);
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_killers(Move m, SearchStack& ss);
//// Functions
////
+
+/// perft() is our utility to verify move generation is bug free. All the
+/// legal moves up to given depth are generated and counted and the sum returned.
+
+int perft(Position& pos, Depth depth)
+{
+ Move move;
+ MovePicker mp = MovePicker(pos, MOVE_NONE, depth, H);
+ Bitboard dcCandidates = pos.discovered_check_candidates(pos.side_to_move());
+ int sum = 0;
+
+ // If we are at the last ply we don't need to do and undo
+ // the moves, just to count them.
+ if (depth <= OnePly) // Replace with '<' to test also qsearch
+ {
+ while ((move = mp.get_next_move()) != MOVE_NONE) sum++;
+ return sum;
+ }
+
+ // Loop through all legal moves
+ while ((move = mp.get_next_move()) != MOVE_NONE)
+ {
+ StateInfo st;
+ pos.do_move(move, st, dcCandidates);
+ sum += perft(pos, depth - OnePly);
+ pos.undo_move(move);
+ }
+ return sum;
+}
+
+
/// 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(). It returns false
if (movesToGo == 1)
{
MaxSearchTime = myTime / 2;
- AbsoluteMaxSearchTime =
+ AbsoluteMaxSearchTime =
(myTime > 3000)? (myTime - 500) : ((myTime * 3) / 4);
} else {
MaxSearchTime = myTime / Min(movesToGo, 20);
<< " currmovenumber " << i + 1 << std::endl;
// Decide search depth for this move
- bool moveIsCapture = pos.move_is_capture(move);
+ bool captureOrPromotion = pos.move_is_capture_or_promotion(move);
bool dangerous;
- ext = extension(pos, move, true, moveIsCapture, pos.move_is_check(move), false, false, &dangerous);
+ ext = extension(pos, move, true, captureOrPromotion, pos.move_is_check(move), false, false, &dangerous);
newDepth = (Iteration - 2) * OnePly + ext + InitialDepth;
// Make the move, and search it
if ( newDepth >= 3*OnePly
&& i >= MultiPV + LMRPVMoves
&& !dangerous
- && !moveIsCapture
- && !move_is_promotion(move)
+ && !captureOrPromotion
&& !move_is_castle(move))
{
ss[0].reduction = OnePly;
std::cout << std::endl;
if (UseLogFile)
- LogFile << pretty_pv(pos, current_search_time(), Iteration, nodes_searched(), value,
+ LogFile << pretty_pv(pos, current_search_time(), Iteration, nodes_searched(), value,
((value >= beta)? VALUE_TYPE_LOWER
: ((value <= alpha)? VALUE_TYPE_UPPER : VALUE_TYPE_EXACT)),
ss[0].pv)
assert(ply >= 0 && ply < PLY_MAX);
assert(threadID >= 0 && threadID < ActiveThreads);
+ Move movesSearched[256];
+ EvalInfo ei;
+ StateInfo st;
+ Bitboard dcCandidates;
+ const TTEntry* tte;
+ Move ttMove, move;
+ Depth ext, newDepth;
+ Value oldAlpha, value;
+ bool isCheck, mateThreat, singleReply, moveIsCheck, captureOrPromotion, dangerous;
+ int moveCount = 0;
+ Value bestValue = -VALUE_INFINITE;
+
if (depth < OnePly)
return qsearch(pos, ss, alpha, beta, Depth(0), ply, threadID);
if (pos.is_draw())
return VALUE_DRAW;
- EvalInfo ei;
-
if (ply >= PLY_MAX - 1)
return pos.is_check() ? quick_evaluate(pos) : evaluate(pos, ei, threadID);
// Mate distance pruning
- Value oldAlpha = alpha;
+ oldAlpha = alpha;
alpha = Max(value_mated_in(ply), alpha);
beta = Min(value_mate_in(ply+1), beta);
if (alpha >= beta)
// 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.get_key());
- Move ttMove = (tte ? tte->move() : MOVE_NONE);
+ tte = TT.retrieve(pos.get_key());
+ ttMove = (tte ? tte->move() : MOVE_NONE);
// Go with internal iterative deepening if we don't have a TT move
if (UseIIDAtPVNodes && ttMove == MOVE_NONE && depth >= 5*OnePly)
// Initialize a MovePicker object for the current position, and prepare
// to search all moves
- Move move, movesSearched[256];
- int moveCount = 0;
- Value value, bestValue = -VALUE_INFINITE;
- Color us = pos.side_to_move();
- bool isCheck = pos.is_check();
- bool mateThreat = pos.has_mate_threat(opposite_color(us));
-
+ isCheck = pos.is_check();
+ mateThreat = pos.has_mate_threat(opposite_color(pos.side_to_move()));
+ dcCandidates = pos.discovered_check_candidates(pos.side_to_move());
MovePicker mp = MovePicker(pos, ttMove, depth, H, &ss[ply]);
- Bitboard dcCandidates = mp.discovered_check_candidates();
// Loop through all legal moves until no moves remain or a beta cutoff
// occurs.
{
assert(move_is_ok(move));
- bool singleReply = (isCheck && mp.number_of_evasions() == 1);
- bool moveIsCheck = pos.move_is_check(move, dcCandidates);
- bool moveIsCapture = pos.move_is_capture(move);
+ singleReply = (isCheck && mp.number_of_evasions() == 1);
+ moveIsCheck = pos.move_is_check(move, dcCandidates);
+ captureOrPromotion = pos.move_is_capture_or_promotion(move);
movesSearched[moveCount++] = ss[ply].currentMove = move;
// Decide the new search depth
- bool dangerous;
- Depth ext = extension(pos, move, true, moveIsCapture, moveIsCheck, singleReply, mateThreat, &dangerous);
- Depth newDepth = depth - OnePly + ext;
+ ext = extension(pos, move, true, captureOrPromotion, moveIsCheck, singleReply, mateThreat, &dangerous);
+ newDepth = depth - OnePly + ext;
// Make and search the move
- StateInfo st;
pos.do_move(move, st, dcCandidates);
if (moveCount == 1) // The first move in list is the PV
if ( depth >= 3*OnePly
&& moveCount >= LMRPVMoves
&& !dangerous
- && !moveIsCapture
- && !move_is_promotion(move)
+ && !captureOrPromotion
&& !move_is_castle(move)
&& !move_is_killer(move, ss[ply]))
{
else if (bestValue >= beta)
{
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
+ move = ss[ply].pv[ply];
+ if (!pos.move_is_capture_or_promotion(move))
{
- update_history(pos, m, depth, movesSearched, moveCount);
- update_killers(m, ss[ply]);
+ update_history(pos, move, depth, movesSearched, moveCount);
+ update_killers(move, ss[ply]);
}
- TT.store(pos.get_key(), value_to_tt(bestValue, ply), VALUE_TYPE_LOWER, depth, m);
+ TT.store(pos.get_key(), value_to_tt(bestValue, ply), VALUE_TYPE_LOWER, depth, move);
}
else
TT.store(pos.get_key(), value_to_tt(bestValue, ply), VALUE_TYPE_EXACT, depth, ss[ply].pv[ply]);
assert(ply >= 0 && ply < PLY_MAX);
assert(threadID >= 0 && threadID < ActiveThreads);
+ Move movesSearched[256];
+ EvalInfo ei;
+ StateInfo st;
+ Bitboard dcCandidates;
+ const TTEntry* tte;
+ Move ttMove, move;
+ Depth ext, newDepth;
+ Value approximateEval, nullValue, value, futilityValue;
+ bool isCheck, useFutilityPruning, singleReply, moveIsCheck, captureOrPromotion, dangerous;
+ bool mateThreat = false;
+ int moveCount = 0;
+ Value bestValue = -VALUE_INFINITE;
+
if (depth < OnePly)
return qsearch(pos, ss, beta-1, beta, Depth(0), ply, threadID);
if (pos.is_draw())
return VALUE_DRAW;
- EvalInfo ei;
-
if (ply >= PLY_MAX - 1)
return pos.is_check() ? quick_evaluate(pos) : evaluate(pos, ei, threadID);
return beta - 1;
// Transposition table lookup
- const TTEntry* tte = TT.retrieve(pos.get_key());
- Move ttMove = (tte ? tte->move() : MOVE_NONE);
+ tte = TT.retrieve(pos.get_key());
+ ttMove = (tte ? tte->move() : MOVE_NONE);
if (tte && ok_to_use_TT(tte, depth, beta, ply))
{
return value_from_tt(tte->value(), ply);
}
- Value approximateEval = quick_evaluate(pos);
- bool mateThreat = false;
- bool isCheck = pos.is_check();
+ approximateEval = quick_evaluate(pos);
+ isCheck = pos.is_check();
// Null move search
if ( allowNullmove
{
ss[ply].currentMove = MOVE_NULL;
- StateInfo st;
pos.do_null_move(st);
int R = (depth >= 5 * OnePly ? 4 : 3); // Null move dynamic reduction
- Value nullValue = -search(pos, ss, -(beta-1), depth-R*OnePly, ply+1, false, threadID);
+ nullValue = -search(pos, ss, -(beta-1), depth-R*OnePly, ply+1, false, threadID);
pos.undo_null_move();
// Initialize a MovePicker object for the current position, and prepare
// to search all moves.
MovePicker mp = MovePicker(pos, ttMove, depth, 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 = depth < SelectiveDepth
- && !isCheck;
+ dcCandidates = pos.discovered_check_candidates(pos.side_to_move());
+ futilityValue = VALUE_NONE;
+ useFutilityPruning = depth < SelectiveDepth && !isCheck;
// Avoid calling evaluate() if we already have the score in TT
if (tte && (tte->type() & VALUE_TYPE_EVAL))
{
assert(move_is_ok(move));
- bool singleReply = (isCheck && mp.number_of_evasions() == 1);
- bool moveIsCheck = pos.move_is_check(move, dcCandidates);
- bool moveIsCapture = pos.move_is_capture(move);
+ singleReply = (isCheck && mp.number_of_evasions() == 1);
+ moveIsCheck = pos.move_is_check(move, dcCandidates);
+ captureOrPromotion = pos.move_is_capture_or_promotion(move);
movesSearched[moveCount++] = ss[ply].currentMove = move;
// Decide the new search depth
- bool dangerous;
- Depth ext = extension(pos, move, false, moveIsCapture, moveIsCheck, singleReply, mateThreat, &dangerous);
- Depth newDepth = depth - OnePly + ext;
+ ext = extension(pos, move, false, captureOrPromotion, moveIsCheck, singleReply, mateThreat, &dangerous);
+ newDepth = depth - OnePly + ext;
// Futility pruning
if ( useFutilityPruning
&& !dangerous
- && !moveIsCapture
- && !move_is_promotion(move))
+ && !captureOrPromotion)
{
// History pruning. See ok_to_prune() definition
if ( moveCount >= 2 + int(depth)
}
// Make and search the move
- StateInfo st;
pos.do_move(move, st, dcCandidates);
// Try to reduce non-pv search depth by one ply if move seems not problematic,
if ( depth >= 3*OnePly
&& moveCount >= LMRNonPVMoves
&& !dangerous
- && !moveIsCapture
- && !move_is_promotion(move)
+ && !captureOrPromotion
&& !move_is_castle(move)
&& !move_is_killer(move, ss[ply]))
{
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
+ move = ss[ply].pv[ply];
+ if (!pos.move_is_capture_or_promotion(move))
{
- update_history(pos, m, depth, movesSearched, moveCount);
- update_killers(m, ss[ply]);
+ update_history(pos, move, depth, movesSearched, moveCount);
+ update_killers(move, ss[ply]);
}
- TT.store(pos.get_key(), value_to_tt(bestValue, ply), VALUE_TYPE_LOWER, depth, m);
+ TT.store(pos.get_key(), value_to_tt(bestValue, ply), VALUE_TYPE_LOWER, depth, move);
}
assert(bestValue > -VALUE_INFINITE && bestValue < VALUE_INFINITE);
assert(ply >= 0 && ply < PLY_MAX);
assert(threadID >= 0 && threadID < ActiveThreads);
+ EvalInfo ei;
+ StateInfo st;
+ Bitboard dcCandidates;
+ Move ttMove, move;
+ Value staticValue, bestValue, value, futilityValue;
+ bool isCheck, enoughMaterial;
+ const TTEntry* tte = NULL;
+ int moveCount = 0;
+ bool pvNode = (beta - alpha != 1);
+
// Initialize, and make an early exit in case of an aborted search,
// an instant draw, maximum ply reached, etc.
init_node(ss, ply, threadID);
return VALUE_DRAW;
// Transposition table lookup, only when not in PV
- TTEntry* tte = NULL;
- bool pvNode = (beta - alpha != 1);
if (!pvNode)
{
tte = TT.retrieve(pos.get_key());
return value_from_tt(tte->value(), ply);
}
}
- Move ttMove = (tte ? tte->move() : MOVE_NONE);
+ ttMove = (tte ? tte->move() : MOVE_NONE);
// Evaluate the position statically
- EvalInfo ei;
- Value staticValue;
- bool isCheck = pos.is_check();
+ isCheck = pos.is_check();
ei.futilityMargin = Value(0); // Manually initialize futilityMargin
if (isCheck)
// Initialize "stand pat score", and return it immediately if it is
// at least beta.
- Value bestValue = staticValue;
+ bestValue = staticValue;
if (bestValue >= beta)
{
// 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, ttMove, depth, H);
- Move move;
- int moveCount = 0;
- Bitboard dcCandidates = mp.discovered_check_candidates();
- Color us = pos.side_to_move();
- bool enoughMaterial = pos.non_pawn_material(us) > RookValueMidgame;
+ dcCandidates = pos.discovered_check_candidates(pos.side_to_move());
+ enoughMaterial = pos.non_pawn_material(pos.side_to_move()) > RookValueMidgame;
// Loop through the moves until no moves remain or a beta cutoff
// occurs.
&& !pos.move_is_check(move, dcCandidates)
&& !pos.move_is_passed_pawn_push(move))
{
- Value futilityValue = staticValue
- + Max(pos.midgame_value_of_piece_on(move_to(move)),
- pos.endgame_value_of_piece_on(move_to(move)))
- + (move_is_ep(move) ? PawnValueEndgame : Value(0))
- + FutilityMarginQS
- + ei.futilityMargin;
+ futilityValue = staticValue
+ + Max(pos.midgame_value_of_piece_on(move_to(move)),
+ pos.endgame_value_of_piece_on(move_to(move)))
+ + (move_is_ep(move) ? PawnValueEndgame : Value(0))
+ + FutilityMarginQS
+ + ei.futilityMargin;
if (futilityValue < alpha)
{
&& pos.see_sign(move) < 0)
continue;
- // Make and search the move.
- StateInfo st;
+ // Make and search the move
pos.do_move(move, st, dcCandidates);
- Value 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);
assert(bestValue > -VALUE_INFINITE && bestValue < VALUE_INFINITE);
// Update transposition table
- Move m = ss[ply].pv[ply];
+ move = ss[ply].pv[ply];
if (!pvNode)
{
// If bestValue isn't changed it means it is still the static evaluation of
if (bestValue < beta)
TT.store(pos.get_key(), value_to_tt(bestValue, ply), type, d, MOVE_NONE);
else
- TT.store(pos.get_key(), value_to_tt(bestValue, ply), VALUE_TYPE_LOWER, d, m);
+ TT.store(pos.get_key(), value_to_tt(bestValue, ply), VALUE_TYPE_LOWER, d, move);
}
// Update killers only for good check moves
- if (alpha >= beta && ok_to_history(pos, m)) // Only non capture moves are considered
- update_killers(m, ss[ply]);
+ if (alpha >= beta && !pos.move_is_capture_or_promotion(move))
+ update_killers(move, ss[ply]);
return bestValue;
}
assert(move_is_ok(move));
bool moveIsCheck = pos.move_is_check(move, sp->dcCandidates);
- bool moveIsCapture = pos.move_is_capture(move);
+ bool captureOrPromotion = pos.move_is_capture_or_promotion(move);
lock_grab(&(sp->lock));
int moveCount = ++sp->moves;
// Decide the new search depth.
bool dangerous;
- Depth ext = extension(pos, move, false, moveIsCapture, moveIsCheck, false, false, &dangerous);
+ Depth ext = extension(pos, move, false, captureOrPromotion, moveIsCheck, false, false, &dangerous);
Depth newDepth = sp->depth - OnePly + ext;
// Prune?
if ( useFutilityPruning
&& !dangerous
- && !moveIsCapture
- && !move_is_promotion(move))
+ && !captureOrPromotion)
{
// History pruning. See ok_to_prune() definition
if ( moveCount >= 2 + int(sp->depth)
// if the move fails high will be re-searched at full depth.
if ( !dangerous
&& moveCount >= LMRNonPVMoves
- && !moveIsCapture
- && !move_is_promotion(move)
+ && !captureOrPromotion
&& !move_is_castle(move)
&& !move_is_killer(move, ss[sp->ply]))
{
&& (move = sp->mp->get_next_move(sp->lock)) != MOVE_NONE)
{
bool moveIsCheck = pos.move_is_check(move, sp->dcCandidates);
- bool moveIsCapture = pos.move_is_capture(move);
+ bool captureOrPromotion = pos.move_is_capture_or_promotion(move);
assert(move_is_ok(move));
// Decide the new search depth.
bool dangerous;
- Depth ext = extension(pos, move, true, moveIsCapture, moveIsCheck, false, false, &dangerous);
+ Depth ext = extension(pos, move, true, captureOrPromotion, moveIsCheck, false, false, &dangerous);
Depth newDepth = sp->depth - OnePly + ext;
// Make and search the move.
// if the move fails high will be re-searched at full depth.
if ( !dangerous
&& moveCount >= LMRPVMoves
- && !moveIsCapture
- && !move_is_promotion(move)
+ && !captureOrPromotion
&& !move_is_castle(move)
&& !move_is_killer(move, ss[sp->ply]))
{
// 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 capture, bool check,
- bool singleReply, bool mateThreat, bool* dangerous) {
+ Depth extension(const Position& pos, Move m, bool pvNode, bool captureOrPromotion,
+ bool check, bool singleReply, bool mateThreat, bool* dangerous) {
assert(m != MOVE_NONE);
}
}
- if ( capture
+ if ( captureOrPromotion
&& pos.type_of_piece_on(move_to(m)) != PAWN
&& ( pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK)
- pos.midgame_value_of_piece_on(move_to(m)) == Value(0))
}
if ( pvNode
- && capture
+ && captureOrPromotion
&& pos.type_of_piece_on(move_to(m)) != PAWN
&& pos.see_sign(m) >= 0)
{
assert(move_is_ok(m));
assert(threat == MOVE_NONE || move_is_ok(threat));
- assert(!move_is_promotion(m));
assert(!pos.move_is_check(m));
- assert(!pos.move_is_capture(m));
+ assert(!pos.move_is_capture_or_promotion(m));
assert(!pos.move_is_passed_pawn_push(m));
assert(d >= OnePly);
}
- // ok_to_history() returns true if a move m can be stored
- // in history. Should be a non capturing move nor a promotion.
-
- bool ok_to_history(const Position& pos, Move m) {
-
- return !pos.move_is_capture(m) && !move_is_promotion(m);
- }
-
-
// update_history() registers a good move that produced a beta-cutoff
// in history and marks as failures all the other moves of that ply.
for (int i = 0; i < moveCount - 1; i++)
{
assert(m != movesSearched[i]);
- if (ok_to_history(pos, movesSearched[i]))
+ if (!pos.move_is_capture_or_promotion(movesSearched[i]))
H.failure(pos.piece_on(move_from(movesSearched[i])), move_to(movesSearched[i]));
}
}