#include <iomanip>
#include <iostream>
#include <sstream>
-#include <vector>
#include "book.h"
#include "evaluate.h"
volatile SignalsType Signals;
LimitsType Limits;
- std::vector<Move> SearchMoves;
+ std::vector<RootMove> RootMoves;
Position RootPosition;
}
// Different node types, used as template parameter
enum NodeType { Root, PV, NonPV, SplitPointRoot, SplitPointPV, SplitPointNonPV };
- // RootMove struct is used for moves at the root of the tree. For each root
- // move we store a score, a node count, and a PV (really a refutation in the
- // case of moves which fail low). Score is normally set at -VALUE_INFINITE for
- // all non-pv moves.
- struct RootMove {
-
- RootMove(){}
- RootMove(Move m) {
- score = prevScore = -VALUE_INFINITE;
- pv.push_back(m);
- pv.push_back(MOVE_NONE);
- }
-
- bool operator<(const RootMove& m) const { return score < m.score; }
- bool operator==(const Move& m) const { return pv[0] == m; }
-
- void extract_pv_from_tt(Position& pos);
- void insert_pv_in_tt(Position& pos);
-
- Value score;
- Value prevScore;
- std::vector<Move> pv;
- };
-
-
- /// Constants
-
// Lookup table to check if a Piece is a slider and its access function
const bool Slidings[18] = { 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1 };
inline bool piece_is_slider(Piece p) { return Slidings[p]; }
return (Depth) Reductions[PvNode][std::min(int(d) / ONE_PLY, 63)][std::min(mn, 63)];
}
- // Easy move margin. An easy move candidate must be at least this much
- // better than the second best move.
+ // Easy move margin. An easy move candidate must be at least this much better
+ // than the second best move.
const Value EasyMoveMargin = Value(0x150);
+ // This is the minimum interval in msec between two check_time() calls
+ const int TimerResolution = 5;
- /// Namespace variables
- std::vector<RootMove> RootMoves;
size_t MultiPV, UCIMultiPV, PVIdx;
TimeManager TimeMgr;
int BestMoveChanges;
History H;
- /// Local functions
-
template <NodeType NT>
Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth);
FORCE_INLINE bool is_dangerous(const Position& pos, Move m, bool captureOrPromotion) {
// Test for a pawn pushed to 7th or a passed pawn move
- if (type_of(pos.piece_on(move_from(m))) == PAWN)
+ if (type_of(pos.piece_moved(m)) == PAWN)
{
Color c = pos.side_to_move();
- if ( relative_rank(c, move_to(m)) == RANK_7
- || pos.pawn_is_passed(c, move_to(m)))
+ if ( relative_rank(c, to_sq(m)) == RANK_7
+ || pos.pawn_is_passed(c, to_sq(m)))
return true;
}
// Test for a capture that triggers a pawn endgame
if ( captureOrPromotion
- && type_of(pos.piece_on(move_to(m))) != PAWN
+ && type_of(pos.piece_on(to_sq(m))) != PAWN
&& ( pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK)
- - PieceValueMidgame[pos.piece_on(move_to(m))] == VALUE_ZERO)
+ - PieceValueMidgame[pos.piece_on(to_sq(m))] == VALUE_ZERO)
&& !is_special(m))
return true;
TimeMgr.init(Limits, pos.startpos_ply_counter());
TT.new_search();
H.clear();
- RootMoves.clear();
- // Populate RootMoves with all the legal moves (default) or, if a SearchMoves
- // is given, with the subset of legal moves to search.
- for (MoveList<MV_LEGAL> ml(pos); !ml.end(); ++ml)
- if ( SearchMoves.empty()
- || count(SearchMoves.begin(), SearchMoves.end(), ml.move()))
- RootMoves.push_back(RootMove(ml.move()));
+ if (RootMoves.empty())
+ {
+ cout << "info depth 0 score "
+ << score_to_uci(pos.in_check() ? -VALUE_MATE : VALUE_DRAW) << endl;
+
+ RootMoves.push_back(MOVE_NONE);
+ goto finalize;
+ }
if (Options["OwnBook"])
{
Move bookMove = book.probe(pos, Options["Book File"], Options["Best Book Move"]);
- if ( bookMove != MOVE_NONE
- && count(RootMoves.begin(), RootMoves.end(), bookMove))
+ if (bookMove && count(RootMoves.begin(), RootMoves.end(), bookMove))
{
std::swap(RootMoves[0], *find(RootMoves.begin(), RootMoves.end(), bookMove));
- goto finish;
+ goto finalize;
}
}
// Set best timer interval to avoid lagging under time pressure. Timer is
// used to check for remaining available thinking time.
- if (TimeMgr.available_time())
- Threads.set_timer(std::min(100, std::max(TimeMgr.available_time() / 8, 20)));
+ if (Limits.use_time_management())
+ Threads.set_timer(std::min(100, std::max(TimeMgr.available_time() / 16, TimerResolution)));
else
Threads.set_timer(100);
pos.undo_move(RootMoves[0].pv[0]);
}
-finish:
+finalize:
- // When we reach max depth we arrive here even without a StopRequest, but if
- // we are pondering or in infinite search, we shouldn't print the best move
- // before we are told to do so.
+ // When we reach max depth we arrive here even without Signals.stop is raised,
+ // but if we are pondering or in infinite search, we shouldn't print the best
+ // move before we are told to do so.
if (!Signals.stop && (Limits.ponder || Limits.infinite))
Threads.wait_for_stop_or_ponderhit();
bestValue = delta = -VALUE_INFINITE;
ss->currentMove = MOVE_NULL; // Hack to skip update gains
- // Handle the special case of a mated/stalemate position
- if (RootMoves.empty())
- {
- cout << "info depth 0 score "
- << score_to_uci(pos.in_check() ? -VALUE_MATE : VALUE_DRAW) << endl;
-
- RootMoves.push_back(MOVE_NONE);
- return;
- }
-
// Iterative deepening loop until requested to stop or target depth reached
while (!Signals.stop && ++depth <= MAX_PLY && (!Limits.maxDepth || depth <= Limits.maxDepth))
{
bestMoveNeverChanged = false;
// Do we have time for the next iteration? Can we stop searching now?
- if (!Signals.stop && !Signals.stopOnPonderhit && Limits.useTimeManagement())
+ if (!Signals.stop && !Signals.stopOnPonderhit && Limits.use_time_management())
{
bool stop = false; // Local variable, not the volatile Signals.stop
stop = true;
// Stop search early if one move seems to be much better than others
- if ( depth >= 10
+ if ( depth >= 12
&& !stop
- && ( bestMoveNeverChanged
+ && ( (bestMoveNeverChanged && pos.captured_piece_type())
|| elapsed_time() > (TimeMgr.available_time() * 40) / 100))
{
Value rBeta = bestValue - EasyMoveMargin;
(ss+1)->excludedMove = RootMoves[0].pv[0];
(ss+1)->skipNullMove = true;
- Value v = search<NonPV>(pos, ss+1, rBeta - 1, rBeta, (depth * ONE_PLY) / 2);
+ Value v = search<NonPV>(pos, ss+1, rBeta - 1, rBeta, (depth - 3) * ONE_PLY);
(ss+1)->skipNullMove = false;
(ss+1)->excludedMove = MOVE_NONE;
if ( (move = (ss-1)->currentMove) != MOVE_NULL
&& (ss-1)->eval != VALUE_NONE
&& ss->eval != VALUE_NONE
- && pos.captured_piece_type() == NO_PIECE_TYPE
+ && !pos.captured_piece_type()
&& !is_special(move))
{
- Square to = move_to(move);
+ Square to = to_sq(move);
H.update_gain(pos.piece_on(to), to, -(ss-1)->eval - ss->eval);
}
// Loop through all pseudo-legal moves until no moves remain or a beta cutoff occurs
while ( bestValue < beta
&& (move = mp.next_move()) != MOVE_NONE
- && !thread.cutoff_occurred())
+ && !thread.cutoff_occurred()
+ && !Signals.stop)
{
assert(is_ok(move));
// but fixing this made program slightly weaker.
Depth predictedDepth = newDepth - reduction<PvNode>(depth, moveCount);
futilityValue = futilityBase + futility_margin(predictedDepth, moveCount)
- + H.gain(pos.piece_on(move_from(move)), move_to(move));
+ + H.gain(pos.piece_moved(move), to_sq(move));
if (futilityValue < beta)
{
// Step 15. Reduced depth search (LMR). If the move fails high will be
// re-searched at full depth.
- if ( depth > 3 * ONE_PLY
+ if ( depth > 4 * ONE_PLY
&& !isPvMove
&& !captureOrPromotion
&& !dangerous
&& ss->killers[1] != move)
{
ss->reduction = reduction<PvNode>(depth, moveCount);
- Depth d = newDepth - ss->reduction;
+ Depth d = std::max(newDepth - ss->reduction, ONE_PLY);
alpha = SpNode ? sp->alpha : alpha;
- value = d < ONE_PLY ? -qsearch<NonPV>(pos, ss+1, -(alpha+1), -alpha, DEPTH_ZERO)
- : - search<NonPV>(pos, ss+1, -(alpha+1), -alpha, d);
+ value = -search<NonPV>(pos, ss+1, -(alpha+1), -alpha, d);
doFullDepthSearch = (value > alpha && ss->reduction != DEPTH_ZERO);
ss->reduction = DEPTH_ZERO;
// Increase history value of the cut-off move
Value bonus = Value(int(depth) * int(depth));
- H.add(pos.piece_on(move_from(move)), move_to(move), bonus);
+ H.add(pos.piece_moved(move), to_sq(move), bonus);
// Decrease history of all the other played non-capture moves
for (int i = 0; i < playedMoveCount - 1; i++)
{
Move m = movesSearched[i];
- H.add(pos.piece_on(move_from(m)), move_to(m), -bonus);
+ H.add(pos.piece_moved(m), to_sq(m), -bonus);
}
}
}
// 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.
- MovePicker mp(pos, ttMove, depth, H, move_to((ss-1)->currentMove));
+ MovePicker mp(pos, ttMove, depth, H, to_sq((ss-1)->currentMove));
CheckInfo ci(pos);
// Loop through the moves until no moves remain or a beta cutoff occurs
&& !pos.is_passed_pawn_push(move))
{
futilityValue = futilityBase
- + PieceValueEndgame[pos.piece_on(move_to(move))]
+ + PieceValueEndgame[pos.piece_on(to_sq(move))]
+ (is_enpassant(move) ? PawnValueEndgame : VALUE_ZERO);
if (futilityValue < beta)
Color them;
Value futilityValue, bv = *bestValue;
- from = move_from(move);
- to = move_to(move);
- them = flip(pos.side_to_move());
+ from = from_sq(move);
+ to = to_sq(move);
+ them = ~pos.side_to_move();
ksq = pos.king_square(them);
kingAtt = pos.attacks_from<KING>(ksq);
pc = pos.piece_on(from);
assert(is_ok(m2));
// Case 1: The moving piece is the same in both moves
- f2 = move_from(m2);
- t1 = move_to(m1);
+ f2 = from_sq(m2);
+ t1 = to_sq(m1);
if (f2 == t1)
return true;
// Case 2: The destination square for m2 was vacated by m1
- t2 = move_to(m2);
- f1 = move_from(m1);
+ t2 = to_sq(m2);
+ f1 = from_sq(m1);
if (t2 == f1)
return true;
Square mfrom, mto, tfrom, tto;
- mfrom = move_from(m);
- mto = move_to(m);
- tfrom = move_from(threat);
- tto = move_to(threat);
+ mfrom = from_sq(m);
+ mto = to_sq(m);
+ tfrom = from_sq(threat);
+ tto = to_sq(threat);
// Case 1: Don't prune moves which move the threatened piece
if (mfrom == tto)
return best;
}
+} // namespace
- // extract_pv_from_tt() builds a PV by adding moves from the transposition table.
- // We consider also failing high nodes and not only VALUE_TYPE_EXACT nodes. This
- // allow to always have a ponder move even when we fail high at root and also a
- // long PV to print that is important for position analysis.
- void RootMove::extract_pv_from_tt(Position& pos) {
+/// RootMove::extract_pv_from_tt() builds a PV by adding moves from the TT table.
+/// We consider also failing high nodes and not only VALUE_TYPE_EXACT nodes so
+/// to allow to always have a ponder move even when we fail high at root, and
+/// a long PV to print that is important for position analysis.
- StateInfo state[MAX_PLY_PLUS_2], *st = state;
- TTEntry* tte;
- int ply = 1;
- Move m = pv[0];
-
- assert(m != MOVE_NONE && pos.is_pseudo_legal(m));
-
- pv.clear();
- pv.push_back(m);
- pos.do_move(m, *st++);
-
- while ( (tte = TT.probe(pos.key())) != NULL
- && tte->move() != MOVE_NONE
- && pos.is_pseudo_legal(tte->move())
- && pos.pl_move_is_legal(tte->move(), pos.pinned_pieces())
- && ply < MAX_PLY
- && (!pos.is_draw<false>() || ply < 2))
- {
- pv.push_back(tte->move());
- pos.do_move(tte->move(), *st++);
- ply++;
- }
- pv.push_back(MOVE_NONE);
+void RootMove::extract_pv_from_tt(Position& pos) {
+
+ StateInfo state[MAX_PLY_PLUS_2], *st = state;
+ TTEntry* tte;
+ int ply = 1;
+ Move m = pv[0];
- do pos.undo_move(pv[--ply]); while (ply);
+ assert(m != MOVE_NONE && pos.is_pseudo_legal(m));
+
+ pv.clear();
+ pv.push_back(m);
+ pos.do_move(m, *st++);
+
+ while ( (tte = TT.probe(pos.key())) != NULL
+ && tte->move() != MOVE_NONE
+ && pos.is_pseudo_legal(tte->move())
+ && pos.pl_move_is_legal(tte->move(), pos.pinned_pieces())
+ && ply < MAX_PLY
+ && (!pos.is_draw<false>() || ply < 2))
+ {
+ pv.push_back(tte->move());
+ pos.do_move(tte->move(), *st++);
+ ply++;
}
+ pv.push_back(MOVE_NONE);
+ do pos.undo_move(pv[--ply]); while (ply);
+}
- // insert_pv_in_tt() is called at the end of a search iteration, and inserts
- // the PV back into the TT. This makes sure the old PV moves are searched
- // first, even if the old TT entries have been overwritten.
- void RootMove::insert_pv_in_tt(Position& pos) {
+/// RootMove::insert_pv_in_tt() is called at the end of a search iteration, and
+/// inserts the PV back into the TT. This makes sure the old PV moves are searched
+/// first, even if the old TT entries have been overwritten.
- StateInfo state[MAX_PLY_PLUS_2], *st = state;
- TTEntry* tte;
- Key k;
- Value v, m = VALUE_NONE;
- int ply = 0;
+void RootMove::insert_pv_in_tt(Position& pos) {
- assert(pv[ply] != MOVE_NONE && pos.is_pseudo_legal(pv[ply]));
+ StateInfo state[MAX_PLY_PLUS_2], *st = state;
+ TTEntry* tte;
+ Key k;
+ Value v, m = VALUE_NONE;
+ int ply = 0;
- do {
- k = pos.key();
- tte = TT.probe(k);
+ assert(pv[ply] != MOVE_NONE && pos.is_pseudo_legal(pv[ply]));
- // Don't overwrite existing correct entries
- if (!tte || tte->move() != pv[ply])
- {
- v = (pos.in_check() ? VALUE_NONE : evaluate(pos, m));
- TT.store(k, VALUE_NONE, VALUE_TYPE_NONE, DEPTH_NONE, pv[ply], v, m);
- }
- pos.do_move(pv[ply], *st++);
+ do {
+ k = pos.key();
+ tte = TT.probe(k);
- } while (pv[++ply] != MOVE_NONE);
+ // Don't overwrite existing correct entries
+ if (!tte || tte->move() != pv[ply])
+ {
+ v = (pos.in_check() ? VALUE_NONE : evaluate(pos, m));
+ TT.store(k, VALUE_NONE, VALUE_TYPE_NONE, DEPTH_NONE, pv[ply], v, m);
+ }
+ pos.do_move(pv[ply], *st++);
- do pos.undo_move(pv[--ply]); while (ply);
- }
+ } while (pv[++ply] != MOVE_NONE);
-} // namespace
+ do pos.undo_move(pv[--ply]); while (ply);
+}
/// Thread::idle_loop() is where the thread is parked when it has no work to do.
}
-/// do_timer_event() is called by the timer thread when the timer triggers. It
-/// is used to print debug info and, more important, to detect when we are out of
+/// check_time() is called by the timer thread when the timer triggers. It is
+/// used to print debug info and, more important, to detect when we are out of
/// available time and so stop the search.
-void do_timer_event() {
+void check_time() {
static int lastInfoTime;
int e = elapsed_time();
&& !Signals.failedLowAtRoot
&& e > TimeMgr.available_time();
- bool noMoreTime = e > TimeMgr.maximum_time()
+ bool noMoreTime = e > TimeMgr.maximum_time() - 2 * TimerResolution
|| stillAtFirstMove;
- if ( (Limits.useTimeManagement() && noMoreTime)
+ if ( (Limits.use_time_management() && noMoreTime)
|| (Limits.maxTime && e >= Limits.maxTime)
/* missing nodes limit */ ) // FIXME
Signals.stop = true;