void wait_for_stop_or_ponderhit();
void init_ss_array(SearchStack* ss, int size);
void print_pv_info(const Position& pos, Move pv[], Value alpha, Value beta, Value value);
+ void insert_pv_in_tt(const Position& pos, Move pv[]);
+ void extract_pv_from_tt(const Position& pos, Move bestMove, Move pv[]);
#if !defined(_MSC_VER)
void *init_thread(void *threadID);
// Init reductions array
for (hd = 1; hd < 64; hd++) for (mc = 1; mc < 64; mc++)
{
- double pvRed = log(double(hd)) * log(double(mc)) / 3.0;
- double nonPVRed = log(double(hd)) * log(double(mc)) / 1.5;
+ double pvRed = 0.33 + log(double(hd)) * log(double(mc)) / 4.5;
+ double nonPVRed = 0.33 + log(double(hd)) * log(double(mc)) / 2.25;
ReductionMatrix[PV][hd][mc] = (int8_t) ( pvRed >= 1.0 ? floor( pvRed * int(OnePly)) : 0);
ReductionMatrix[NonPV][hd][mc] = (int8_t) (nonPVRed >= 1.0 ? floor(nonPVRed * int(OnePly)) : 0);
}
void SearchStack::init() {
currentMove = threatMove = bestMove = MOVE_NONE;
- reduction = Depth(0);
- eval = VALUE_NONE;
}
// SearchStack::initKillers() initializes killers for a search stack entry
void SearchStack::initKillers() {
- mateKiller = MOVE_NONE;
- for (int i = 0; i < KILLER_MAX; i++)
- killers[i] = MOVE_NONE;
+ killers[0] = killers[1] = mateKiller = MOVE_NONE;
}
// Write PV to transposition table, in case the relevant entries have
// been overwritten during the search.
- TT.insert_pv(p, pv);
+ insert_pv_in_tt(p, pv);
if (AbortSearch)
break; // Value cannot be trusted. Break out immediately!
beta = *betaPtr;
isCheck = pos.is_check();
- // Step 1. Initialize node and poll (omitted at root, init_ss_array() has already initialized root node)
+ // Step 1. Initialize node (polling is omitted at root)
+ ss->init();
+
// Step 2. Check for aborted search (omitted at root)
// Step 3. Mate distance pruning (omitted at root)
// Step 4. Transposition table lookup (omitted at root)
// Step 5. Evaluate the position statically
// At root we do this only to get reference value for child nodes
- if (!isCheck)
- ss->eval = evaluate(pos, ei);
+ ss->eval = isCheck ? VALUE_NONE : evaluate(pos, ei);
// Step 6. Razoring (omitted at root)
// Step 7. Static null move pruning (omitted at root)
// the score before research in case we run out of time while researching.
rml.set_move_score(i, value);
ss->bestMove = move;
- TT.extract_pv(pos, move, pv, PLY_MAX);
+ extract_pv_from_tt(pos, move, pv);
rml.set_move_pv(i, pv);
// Print information to the standard output
// Update PV
rml.set_move_score(i, value);
ss->bestMove = move;
- TT.extract_pv(pos, move, pv, PLY_MAX);
+ extract_pv_from_tt(pos, move, pv);
rml.set_move_pv(i, pv);
if (MultiPV == 1)
isCheck = pos.is_check();
if (!isCheck)
{
- if (tte && tte->static_value() != VALUE_NONE)
+ if (tte)
{
+ assert(tte->static_value() != VALUE_NONE);
ss->eval = tte->static_value();
ei.kingDanger[pos.side_to_move()] = tte->king_danger();
}
else
+ {
ss->eval = evaluate(pos, ei);
+ TT.store(posKey, VALUE_NONE, VALUE_TYPE_NONE, DEPTH_NONE, MOVE_NONE, ss->eval, ei.kingDanger[pos.side_to_move()]);
+ }
refinedValue = refine_eval(tte, ss->eval, ply); // Enhance accuracy with TT value if possible
update_gains(pos, (ss-1)->currentMove, (ss-1)->eval, ss->eval);
}
+ else
+ ss->eval = VALUE_NONE;
// Step 6. Razoring (is omitted in PV nodes)
if ( !PvNode
&& !value_is_mate(beta)
&& !pos.has_pawn_on_7th(pos.side_to_move()))
{
- // Pass ss->eval to qsearch() and avoid an evaluate call
- if (!tte || tte->static_value() == VALUE_NONE)
- TT.store(posKey, ss->eval, VALUE_TYPE_EXACT, Depth(-127*OnePly), MOVE_NONE, ss->eval, ei.kingDanger[pos.side_to_move()]);
-
Value rbeta = beta - razor_margin(depth);
Value v = qsearch<NonPV>(pos, ss, rbeta-1, rbeta, Depth(0), ply);
if (v < rbeta)
TM.incrementNodeCounter(pos.thread());
ss->bestMove = ss->currentMove = MOVE_NONE;
- ss->eval = VALUE_NONE;
// Check for an instant draw or maximum ply reached
if (pos.is_draw() || ply >= PLY_MAX - 1)
if (isCheck)
{
bestValue = futilityBase = -VALUE_INFINITE;
+ ss->eval = VALUE_NONE;
deepChecks = enoughMaterial = false;
}
else
{
- if (tte && tte->static_value() != VALUE_NONE)
+ if (tte)
{
+ assert(tte->static_value() != VALUE_NONE);
ei.kingDanger[pos.side_to_move()] = tte->king_danger();
bestValue = tte->static_value();
}
if (bestValue >= beta)
{
if (!tte)
- TT.store(pos.get_key(), value_to_tt(bestValue, ply), VALUE_TYPE_LOWER, Depth(-127*OnePly), MOVE_NONE, ss->eval, ei.kingDanger[pos.side_to_move()]);
+ TT.store(pos.get_key(), value_to_tt(bestValue, ply), VALUE_TYPE_LOWER, DEPTH_NONE, MOVE_NONE, ss->eval, ei.kingDanger[pos.side_to_move()]);
return bestValue;
}
bool move_is_killer(Move m, SearchStack* ss) {
- const Move* k = ss->killers;
- for (int i = 0; i < KILLER_MAX; i++, k++)
- if (*k == m)
- return true;
+ if (ss->killers[0] == m || ss->killers[1] == m)
+ return true;
return false;
}
if (m == ss->killers[0])
return;
- for (int i = KILLER_MAX - 1; i > 0; i--)
- ss->killers[i] = ss->killers[i - 1];
-
+ ss->killers[1] = ss->killers[0];
ss->killers[0] = m;
}
{
ss->excludedMove = MOVE_NONE;
ss->skipNullMove = false;
+ ss->reduction = Depth(0);
if (i < 3)
- {
- ss->init();
ss->initKillers();
- }
}
}
}
+ // 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 insert_pv_in_tt(const Position& pos, Move pv[]) {
+
+ StateInfo st;
+ TTEntry* tte;
+ Position p(pos, pos.thread());
+ EvalInfo ei;
+ Value v;
+
+ for (int i = 0; pv[i] != MOVE_NONE; i++)
+ {
+ tte = TT.retrieve(p.get_key());
+ if (!tte || tte->move() != pv[i])
+ {
+ v = (p.is_check() ? VALUE_NONE : evaluate(p, ei));
+ TT.store(p.get_key(), VALUE_NONE, VALUE_TYPE_NONE, DEPTH_NONE, pv[i], v, ei.kingDanger[pos.side_to_move()]);
+ }
+ p.do_move(pv[i], st);
+ }
+ }
+
+
+ // 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 extract_pv_from_tt(const Position& pos, Move bestMove, Move pv[]) {
+
+ StateInfo st;
+ TTEntry* tte;
+ Position p(pos, pos.thread());
+ int ply = 0;
+
+ assert(bestMove != MOVE_NONE);
+
+ pv[ply] = bestMove;
+ p.do_move(pv[ply++], st);
+
+ while ( (tte = TT.retrieve(p.get_key())) != NULL
+ && tte->move() != MOVE_NONE
+ && move_is_legal(p, tte->move())
+ && ply < PLY_MAX
+ && (!p.is_draw() || ply < 2))
+ {
+ pv[ply] = tte->move();
+ p.do_move(pv[ply++], st);
+ }
+ pv[ply] = MOVE_NONE;
+ }
+
+
// init_thread() is the function which is called when a new thread is
// launched. It simply calls the idle_loop() function with the supplied
// threadID. There are two versions of this function; one for POSIX
StateInfo st;
bool includeAllMoves = (searchMoves[0] == MOVE_NONE);
+ // Initialize search stack
+ init_ss_array(ss, PLY_MAX_PLUS_2);
+ ss[0].init();
+ ss[0].eval = VALUE_NONE;
+
// Generate all legal moves
MoveStack* last = generate_moves(pos, mlist);
continue;
// Find a quick score for the move
- init_ss_array(ss, PLY_MAX_PLUS_2);
pos.do_move(cur->move, st);
+ ss[0].currentMove = cur->move;
moves[count].move = cur->move;
moves[count].score = -qsearch<PV>(pos, ss+1, -VALUE_INFINITE, VALUE_INFINITE, Depth(0), 1);
moves[count].pv[0] = cur->move;