Skip to content

Commit

Permalink
Introduce capture history table for capture move sorting
Browse files Browse the repository at this point in the history
Introduce capture move history table indexed by moved piece,
target square and captured piece type for sorting capture moves.

STC:
LLR: 2.95 (-2.94,2.94) [0.00,5.00]
Total: 11374 W: 2096 L: 1924 D: 7354
http://tests.stockfishchess.org/tests/view/59fac8dc0ebc590ccbb89fc5

LTC:
LLR: 2.95 (-2.94,2.94) [0.00,5.00]
Total: 24791 W: 3196 L: 3001 D: 18594
http://tests.stockfishchess.org/tests/view/59fae4d20ebc590ccbb89fd9

Bench: 5536775
  • Loading branch information
locutus2 authored and mcostalba committed Nov 3, 2017
1 parent 486c817 commit 4bc1198
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 17 deletions.
14 changes: 7 additions & 7 deletions src/movepick.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,8 @@ namespace {

/// MovePicker constructor for the main search
MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHistory* mh,
const PieceToHistory** ch, Move cm, Move* killers_p)
: pos(p), mainHistory(mh), contHistory(ch), countermove(cm),
const CapturePieceToHistory* cph, const PieceToHistory** ch, Move cm, Move* killers_p)
: pos(p), mainHistory(mh), captureHistory(cph), contHistory(ch), countermove(cm),
killers{killers_p[0], killers_p[1]}, depth(d){

assert(d > DEPTH_ZERO);
Expand All @@ -80,8 +80,8 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHist
}

/// MovePicker constructor for quiescence search
MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHistory* mh, Square s)
: pos(p), mainHistory(mh) {
MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHistory* mh, const CapturePieceToHistory* cph, Square s)
: pos(p), mainHistory(mh), captureHistory(cph) {

assert(d <= DEPTH_ZERO);

Expand All @@ -107,8 +107,8 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHist

/// MovePicker constructor for ProbCut: we generate captures with SEE higher
/// than or equal to the given threshold.
MovePicker::MovePicker(const Position& p, Move ttm, Value th)
: pos(p), threshold(th) {
MovePicker::MovePicker(const Position& p, Move ttm, Value th, const CapturePieceToHistory* cph)
: pos(p), captureHistory(cph), threshold(th) {

assert(!pos.checkers());

Expand All @@ -132,7 +132,7 @@ void MovePicker::score() {
for (auto& m : *this)
if (Type == CAPTURES)
m.value = PieceValue[MG][pos.piece_on(to_sq(m))]
- Value(200 * relative_rank(pos.side_to_move(), to_sq(m)));
+ Value((*captureHistory)[pos.moved_piece(m)][to_sq(m)][type_of(pos.piece_on(to_sq(m)))]);

else if (Type == QUIETS)
m.value = (*mainHistory)[pos.side_to_move()][from_to(m)]
Expand Down
41 changes: 37 additions & 4 deletions src/movepick.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#define MOVEPICK_H_INCLUDED

#include <array>
#include <limits>

#include "movegen.h"
#include "position.h"
Expand All @@ -39,21 +40,44 @@ struct StatBoards : public std::array<std::array<T, Size2>, Size1> {
void update(T& entry, int bonus, const int D) {

assert(abs(bonus) <= D); // Ensure range is [-32 * D, 32 * D]
assert(abs(32 * D) < INT16_MAX); // Ensure we don't overflow
assert(abs(32 * D) < (std::numeric_limits<T>::max)()); // Ensure we don't overflow

entry += bonus * 32 - entry * abs(bonus) / D;

assert(abs(entry) <= 32 * D);
}
};

/// StatCubes is a generic 3-dimensional array used to store various statistics
template<int Size1, int Size2, int Size3, typename T = int16_t>
struct StatCubes : public std::array<std::array<std::array<T, Size3>, Size2>, Size1> {

void fill(const T& v) {
T* p = &(*this)[0][0][0];
std::fill(p, p + sizeof(*this) / sizeof(*p), v);
}

void update(T& entry, int bonus, const int D, const int W) {

assert(abs(bonus) <= D); // Ensure range is [-W * D, W * D]
assert(abs(W * D) < (std::numeric_limits<T>::max)()); // Ensure we don't overflow

entry += bonus * W - entry * abs(bonus) / D;

assert(abs(entry) <= W * D);
}
};

/// ButterflyBoards are 2 tables (one for each color) indexed by the move's from
/// and to squares, see chessprogramming.wikispaces.com/Butterfly+Boards
typedef StatBoards<COLOR_NB, int(SQUARE_NB) * int(SQUARE_NB)> ButterflyBoards;

/// PieceToBoards are addressed by a move's [piece][to] information
typedef StatBoards<PIECE_NB, SQUARE_NB> PieceToBoards;

/// CapturePieceToBoards are addressed by a move's [piece][to][captured piece type] information
typedef StatCubes<PIECE_NB, SQUARE_NB, PIECE_TYPE_NB> CapturePieceToBoards;

/// ButterflyHistory records how often quiet moves have been successful or
/// unsuccessful during the current search, and is used for reduction and move
/// ordering decisions. It uses ButterflyBoards as backing store.
Expand All @@ -72,6 +96,14 @@ struct PieceToHistory : public PieceToBoards {
}
};

/// CapturePieceToHistory is like PieceToHistory, but is based on CapturePieceToBoards
struct CapturePieceToHistory : public CapturePieceToBoards {

void update(Piece pc, Square to, PieceType captured, int bonus) {
StatCubes::update((*this)[pc][to][captured], bonus, 324, 2);
}
};

/// CounterMoveHistory stores counter moves indexed by [piece][to] of the previous
/// move, see chessprogramming.wikispaces.com/Countermove+Heuristic
typedef StatBoards<PIECE_NB, SQUARE_NB, Move> CounterMoveHistory;
Expand All @@ -93,9 +125,9 @@ class MovePicker {
public:
MovePicker(const MovePicker&) = delete;
MovePicker& operator=(const MovePicker&) = delete;
MovePicker(const Position&, Move, Value);
MovePicker(const Position&, Move, Depth, const ButterflyHistory*, Square);
MovePicker(const Position&, Move, Depth, const ButterflyHistory*, const PieceToHistory**, Move, Move*);
MovePicker(const Position&, Move, Value, const CapturePieceToHistory*);
MovePicker(const Position&, Move, Depth, const ButterflyHistory*, const CapturePieceToHistory*, Square);
MovePicker(const Position&, Move, Depth, const ButterflyHistory*, const CapturePieceToHistory*, const PieceToHistory**, Move, Move*);
Move next_move(bool skipQuiets = false);

private:
Expand All @@ -105,6 +137,7 @@ class MovePicker {

const Position& pos;
const ButterflyHistory* mainHistory;
const CapturePieceToHistory* captureHistory;
const PieceToHistory** contHistory;
Move ttMove, countermove, killers[2];
ExtMove *cur, *endMoves, *endBadCaptures;
Expand Down
39 changes: 33 additions & 6 deletions src/search.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ namespace {
void update_pv(Move* pv, Move move, Move* childPv);
void update_continuation_histories(Stack* ss, Piece pc, Square to, int bonus);
void update_stats(const Position& pos, Stack* ss, Move move, Move* quiets, int quietsCnt, int bonus);
void update_capture_stats(const Position& pos, Move move, Move* captures, int captureCnt, int bonus);
bool pv_is_draw(Position& pos);

// perft() is our utility to verify move generation. All the leaf nodes up
Expand Down Expand Up @@ -500,7 +501,7 @@ namespace {
assert(!(PvNode && cutNode));
assert(depth / ONE_PLY * ONE_PLY == depth);

Move pv[MAX_PLY+1], quietsSearched[64];
Move pv[MAX_PLY+1], capturesSearched[32], quietsSearched[64];
StateInfo st;
TTEntry* tte;
Key posKey;
Expand All @@ -510,12 +511,12 @@ namespace {
bool ttHit, inCheck, givesCheck, singularExtensionNode, improving;
bool captureOrPromotion, doFullDepthSearch, moveCountPruning, skipQuiets, ttCapture, pvExact;
Piece movedPiece;
int moveCount, quietCount;
int moveCount, captureCount, quietCount;

// Step 1. Initialize node
Thread* thisThread = pos.this_thread();
inCheck = pos.checkers();
moveCount = quietCount = ss->moveCount = 0;
moveCount = captureCount = quietCount = ss->moveCount = 0;
ss->statScore = 0;
bestValue = -VALUE_INFINITE;

Expand Down Expand Up @@ -579,6 +580,8 @@ namespace {
{
if (!pos.capture_or_promotion(ttMove))
update_stats(pos, ss, ttMove, nullptr, 0, stat_bonus(depth));
else
update_capture_stats(pos, ttMove, nullptr, 0, stat_bonus(depth));

// Extra penalty for a quiet TT move in previous ply when it gets refuted
if ((ss-1)->moveCount == 1 && !pos.captured_piece())
Expand Down Expand Up @@ -729,7 +732,7 @@ namespace {

assert(is_ok((ss-1)->currentMove));

MovePicker mp(pos, ttMove, rbeta - ss->staticEval);
MovePicker mp(pos, ttMove, rbeta - ss->staticEval, &thisThread->captureHistory);

while ((move = mp.next_move()) != MOVE_NONE)
if (pos.legal(move))
Expand Down Expand Up @@ -763,7 +766,7 @@ namespace {
const PieceToHistory* contHist[] = { (ss-1)->contHistory, (ss-2)->contHistory, nullptr, (ss-4)->contHistory };
Move countermove = thisThread->counterMoves[pos.piece_on(prevSq)][prevSq];

MovePicker mp(pos, ttMove, depth, &thisThread->mainHistory, contHist, countermove, ss->killers);
MovePicker mp(pos, ttMove, depth, &thisThread->mainHistory, &thisThread->captureHistory, contHist, countermove, ss->killers);
value = bestValue; // Workaround a bogus 'uninitialized' warning under gcc
improving = ss->staticEval >= (ss-2)->staticEval
/* || ss->staticEval == VALUE_NONE Already implicit in the previous condition */
Expand Down Expand Up @@ -1054,6 +1057,8 @@ namespace {

if (!captureOrPromotion && move != bestMove && quietCount < 64)
quietsSearched[quietCount++] = move;
else if (captureOrPromotion && move != bestMove && captureCount < 32)
capturesSearched[captureCount++] = move;
}

// The following condition would detect a stop only after move loop has been
Expand All @@ -1079,6 +1084,8 @@ namespace {
// Quiet best move: update move sorting heuristics
if (!pos.capture_or_promotion(bestMove))
update_stats(pos, ss, bestMove, quietsSearched, quietCount, stat_bonus(depth));
else
update_capture_stats(pos, bestMove, capturesSearched, captureCount, stat_bonus(depth));

// Extra penalty for a quiet TT move in previous ply when it gets refuted
if ((ss-1)->moveCount == 1 && !pos.captured_piece())
Expand Down Expand Up @@ -1207,7 +1214,7 @@ namespace {
// 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, &pos.this_thread()->mainHistory, to_sq((ss-1)->currentMove));
MovePicker mp(pos, ttMove, depth, &pos.this_thread()->mainHistory, &pos.this_thread()->captureHistory, to_sq((ss-1)->currentMove));

// Loop through the moves until no moves remain or a beta cutoff occurs
while ((move = mp.next_move()) != MOVE_NONE)
Expand Down Expand Up @@ -1362,6 +1369,26 @@ namespace {
}


// update_capture_stats() updates move sorting heuristics when a new capture best move is found

void update_capture_stats(const Position& pos, Move move,
Move* captures, int captureCnt, int bonus) {

CapturePieceToHistory& captureHistory = pos.this_thread()->captureHistory;
Piece moved_piece = pos.moved_piece(move);
PieceType captured = type_of(pos.piece_on(to_sq(move)));
captureHistory.update(moved_piece,to_sq(move), captured, bonus);

// Decrease all the other played capture moves
for (int i = 0; i < captureCnt; ++i)
{
moved_piece = pos.moved_piece(captures[i]);
captured = type_of(pos.piece_on(to_sq(captures[i])));
captureHistory.update(moved_piece, to_sq(captures[i]), captured, -bonus);
}
}


// update_stats() updates move sorting heuristics when a new quiet best move is found

void update_stats(const Position& pos, Stack* ss, Move move,
Expand Down
1 change: 1 addition & 0 deletions src/thread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ void Thread::clear() {

counterMoves.fill(MOVE_NONE);
mainHistory.fill(0);
captureHistory.fill(0);

for (auto& to : contHistory)
for (auto& h : to)
Expand Down
1 change: 1 addition & 0 deletions src/thread.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ class Thread {
Depth rootDepth, completedDepth;
CounterMoveHistory counterMoves;
ButterflyHistory mainHistory;
CapturePieceToHistory captureHistory;
ContinuationHistory contHistory;
};

Expand Down

0 comments on commit 4bc1198

Please sign in to comment.