Move ttMove, move, excludedMove, bestMove;
Depth extension, newDepth;
Value bestValue, value, ttValue, eval, maxValue, pureStaticEval;
- bool ttHit, inCheck, givesCheck, improving;
+ bool ttHit, pvHit, inCheck, givesCheck, improving;
bool captureOrPromotion, doFullDepthSearch, moveCountPruning, skipQuiets, ttCapture, pvExact;
Piece movedPiece;
int moveCount, captureCount, quietCount;
ttValue = ttHit ? value_from_tt(tte->value(), ss->ply) : VALUE_NONE;
ttMove = rootNode ? thisThread->rootMoves[thisThread->pvIdx].pv[0]
: ttHit ? tte->move() : MOVE_NONE;
+ pvHit = ttHit ? tte->pv_hit() : false;
// At non-PV nodes we check for an early TT cutoff
if ( !PvNode
return ttValue;
}
+ if ( depth > 6 * ONE_PLY
+ && !excludedMove
+ && PvNode)
+ pvHit = true;
+
// Step 5. Tablebases probe
if (!rootNode && TB::Cardinality)
{
if ( b == BOUND_EXACT
|| (b == BOUND_LOWER ? value >= beta : value <= alpha))
{
- tte->save(posKey, value_to_tt(value, ss->ply), b,
+ tte->save(posKey, value_to_tt(value, ss->ply), pvHit, b,
std::min(DEPTH_MAX - ONE_PLY, depth + 6 * ONE_PLY),
MOVE_NONE, VALUE_NONE);
else
ss->staticEval = eval = pureStaticEval = -(ss-1)->staticEval + 2 * Eval::Tempo;
- tte->save(posKey, VALUE_NONE, BOUND_NONE, DEPTH_NONE, MOVE_NONE, pureStaticEval);
+ tte->save(posKey, VALUE_NONE, pvHit, BOUND_NONE, DEPTH_NONE, MOVE_NONE, pureStaticEval);
}
// Step 7. Razoring (~2 Elo)
tte = TT.probe(posKey, ttHit);
ttValue = ttHit ? value_from_tt(tte->value(), ss->ply) : VALUE_NONE;
ttMove = ttHit ? tte->move() : MOVE_NONE;
+ pvHit = ttHit ? tte->pv_hit() : false;
}
moves_loop: // When in check, search starts from here
{
Depth r = reduction<PvNode>(improving, depth, moveCount);
+ // Decrease reduction if position is or has been on the PV
+ if (pvHit && !PvNode)
+ r -= ONE_PLY;
+
// Decrease reduction if opponent's move count is high (~10 Elo)
if ((ss-1)->moveCount > 15)
r -= ONE_PLY;
bestValue = std::min(bestValue, maxValue);
if (!excludedMove)
- tte->save(posKey, value_to_tt(bestValue, ss->ply),
+ tte->save(posKey, value_to_tt(bestValue, ss->ply), pvHit,
bestValue >= beta ? BOUND_LOWER :
PvNode && bestMove ? BOUND_EXACT : BOUND_UPPER,
depth, bestMove, pureStaticEval);
Move ttMove, move, bestMove;
Depth ttDepth;
Value bestValue, value, ttValue, futilityValue, futilityBase, oldAlpha;
- bool ttHit, inCheck, givesCheck, evasionPrunable;
+ bool ttHit, pvHit, inCheck, givesCheck, evasionPrunable;
int moveCount;
if (PvNode)
tte = TT.probe(posKey, ttHit);
ttValue = ttHit ? value_from_tt(tte->value(), ss->ply) : VALUE_NONE;
ttMove = ttHit ? tte->move() : MOVE_NONE;
+ pvHit = ttHit ? tte->pv_hit() : false;
if ( !PvNode
&& ttHit
if (bestValue >= beta)
{
if (!ttHit)
- tte->save(posKey, value_to_tt(bestValue, ss->ply), BOUND_LOWER,
+ tte->save(posKey, value_to_tt(bestValue, ss->ply), pvHit, BOUND_LOWER,
DEPTH_NONE, MOVE_NONE, ss->staticEval);
return bestValue;
if (inCheck && bestValue == -VALUE_INFINITE)
return mated_in(ss->ply); // Plies to mate from the root
- tte->save(posKey, value_to_tt(bestValue, ss->ply),
+ tte->save(posKey, value_to_tt(bestValue, ss->ply), pvHit,
bestValue >= beta ? BOUND_LOWER :
PvNode && bestValue > oldAlpha ? BOUND_EXACT : BOUND_UPPER,
ttDepth, bestMove, ss->staticEval);
TranspositionTable TT; // Our global transposition table
/// TTEntry::save saves a TTEntry
-void TTEntry::save(Key k, Value v, Bound b, Depth d, Move m, Value ev) {
+void TTEntry::save(Key k, Value v, bool PvNode, Bound b, Depth d, Move m, Value ev) {
assert(d / ONE_PLY * ONE_PLY == d);
key16 = (uint16_t)(k >> 48);
value16 = (int16_t)v;
eval16 = (int16_t)ev;
- genBound8 = (uint8_t)(TT.generation8 | b);
+ genBound8 = (uint8_t)(TT.generation8 | PvNode << 2 | b);
depth8 = (int8_t)(d / ONE_PLY);
}
}
for (int i = 0; i < ClusterSize; ++i)
if (!tte[i].key16 || tte[i].key16 == key16)
{
- tte[i].genBound8 = uint8_t(generation8 | tte[i].bound()); // Refresh
+ tte[i].genBound8 = uint8_t(generation8 | tte[i].pv_hit() << 2 | tte[i].bound()); // Refresh
return found = (bool)tte[i].key16, &tte[i];
}
TTEntry* replace = tte;
for (int i = 1; i < ClusterSize; ++i)
// Due to our packed storage format for generation and its cyclic
- // nature we add 259 (256 is the modulus plus 3 to keep the lowest
+ // nature we add 263 (263 is the modulus plus 7 to keep the lowest
// two bound bits from affecting the result) to calculate the entry
// age correctly even after generation8 overflows into the next cycle.
- if ( replace->depth8 - ((259 + generation8 - replace->genBound8) & 0xFC) * 2
- > tte[i].depth8 - ((259 + generation8 - tte[i].genBound8) & 0xFC) * 2)
+ if ( replace->depth8 - ((263 + generation8 - replace->genBound8) & 0xF8)
+ > tte[i].depth8 - ((263 + generation8 - tte[i].genBound8) & 0xF8))
replace = &tte[i];
return found = false, replace;
/// move 16 bit
/// value 16 bit
/// eval value 16 bit
-/// generation 6 bit
+/// generation 5 bit
+/// PvNode 1 bit
/// bound type 2 bit
/// depth 8 bit
Value value() const { return (Value)value16; }
Value eval() const { return (Value)eval16; }
Depth depth() const { return (Depth)(depth8 * int(ONE_PLY)); }
+ bool pv_hit() const { return (bool)(genBound8 & 0x4); }
Bound bound() const { return (Bound)(genBound8 & 0x3); }
- void save(Key k, Value v, Bound b, Depth d, Move m, Value ev);
+ void save(Key k, Value v, bool PvNode, Bound b, Depth d, Move m, Value ev);
private:
friend class TranspositionTable;
public:
~TranspositionTable() { free(mem); }
- void new_search() { generation8 += 4; } // Lower 2 bits are used by Bound
+ void new_search() { generation8 += 8; } // Lower 3 bits are used by PV flag and Bound
TTEntry* probe(const Key key, bool& found) const;
int hashfull() const;
void resize(size_t mbSize);