Skip to content

Commit 4bc1198

Browse files
locutus2mcostalba
authored andcommitted
Introduce capture history table for capture move sorting
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
1 parent 486c817 commit 4bc1198

File tree

5 files changed

+79
-17
lines changed

5 files changed

+79
-17
lines changed

src/movepick.cpp

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,8 @@ namespace {
6868

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

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

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

8686
assert(d <= DEPTH_ZERO);
8787

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

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

113113
assert(!pos.checkers());
114114

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

137137
else if (Type == QUIETS)
138138
m.value = (*mainHistory)[pos.side_to_move()][from_to(m)]

src/movepick.h

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#define MOVEPICK_H_INCLUDED
2323

2424
#include <array>
25+
#include <limits>
2526

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

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

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

4647
assert(abs(entry) <= 32 * D);
4748
}
4849
};
4950

51+
/// StatCubes is a generic 3-dimensional array used to store various statistics
52+
template<int Size1, int Size2, int Size3, typename T = int16_t>
53+
struct StatCubes : public std::array<std::array<std::array<T, Size3>, Size2>, Size1> {
54+
55+
void fill(const T& v) {
56+
T* p = &(*this)[0][0][0];
57+
std::fill(p, p + sizeof(*this) / sizeof(*p), v);
58+
}
59+
60+
void update(T& entry, int bonus, const int D, const int W) {
61+
62+
assert(abs(bonus) <= D); // Ensure range is [-W * D, W * D]
63+
assert(abs(W * D) < (std::numeric_limits<T>::max)()); // Ensure we don't overflow
64+
65+
entry += bonus * W - entry * abs(bonus) / D;
66+
67+
assert(abs(entry) <= W * D);
68+
}
69+
};
70+
5071
/// ButterflyBoards are 2 tables (one for each color) indexed by the move's from
5172
/// and to squares, see chessprogramming.wikispaces.com/Butterfly+Boards
5273
typedef StatBoards<COLOR_NB, int(SQUARE_NB) * int(SQUARE_NB)> ButterflyBoards;
5374

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

78+
/// CapturePieceToBoards are addressed by a move's [piece][to][captured piece type] information
79+
typedef StatCubes<PIECE_NB, SQUARE_NB, PIECE_TYPE_NB> CapturePieceToBoards;
80+
5781
/// ButterflyHistory records how often quiet moves have been successful or
5882
/// unsuccessful during the current search, and is used for reduction and move
5983
/// ordering decisions. It uses ButterflyBoards as backing store.
@@ -72,6 +96,14 @@ struct PieceToHistory : public PieceToBoards {
7296
}
7397
};
7498

99+
/// CapturePieceToHistory is like PieceToHistory, but is based on CapturePieceToBoards
100+
struct CapturePieceToHistory : public CapturePieceToBoards {
101+
102+
void update(Piece pc, Square to, PieceType captured, int bonus) {
103+
StatCubes::update((*this)[pc][to][captured], bonus, 324, 2);
104+
}
105+
};
106+
75107
/// CounterMoveHistory stores counter moves indexed by [piece][to] of the previous
76108
/// move, see chessprogramming.wikispaces.com/Countermove+Heuristic
77109
typedef StatBoards<PIECE_NB, SQUARE_NB, Move> CounterMoveHistory;
@@ -93,9 +125,9 @@ class MovePicker {
93125
public:
94126
MovePicker(const MovePicker&) = delete;
95127
MovePicker& operator=(const MovePicker&) = delete;
96-
MovePicker(const Position&, Move, Value);
97-
MovePicker(const Position&, Move, Depth, const ButterflyHistory*, Square);
98-
MovePicker(const Position&, Move, Depth, const ButterflyHistory*, const PieceToHistory**, Move, Move*);
128+
MovePicker(const Position&, Move, Value, const CapturePieceToHistory*);
129+
MovePicker(const Position&, Move, Depth, const ButterflyHistory*, const CapturePieceToHistory*, Square);
130+
MovePicker(const Position&, Move, Depth, const ButterflyHistory*, const CapturePieceToHistory*, const PieceToHistory**, Move, Move*);
99131
Move next_move(bool skipQuiets = false);
100132

101133
private:
@@ -105,6 +137,7 @@ class MovePicker {
105137

106138
const Position& pos;
107139
const ButterflyHistory* mainHistory;
140+
const CapturePieceToHistory* captureHistory;
108141
const PieceToHistory** contHistory;
109142
Move ttMove, countermove, killers[2];
110143
ExtMove *cur, *endMoves, *endBadCaptures;

src/search.cpp

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ namespace {
109109
void update_pv(Move* pv, Move move, Move* childPv);
110110
void update_continuation_histories(Stack* ss, Piece pc, Square to, int bonus);
111111
void update_stats(const Position& pos, Stack* ss, Move move, Move* quiets, int quietsCnt, int bonus);
112+
void update_capture_stats(const Position& pos, Move move, Move* captures, int captureCnt, int bonus);
112113
bool pv_is_draw(Position& pos);
113114

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

503-
Move pv[MAX_PLY+1], quietsSearched[64];
504+
Move pv[MAX_PLY+1], capturesSearched[32], quietsSearched[64];
504505
StateInfo st;
505506
TTEntry* tte;
506507
Key posKey;
@@ -510,12 +511,12 @@ namespace {
510511
bool ttHit, inCheck, givesCheck, singularExtensionNode, improving;
511512
bool captureOrPromotion, doFullDepthSearch, moveCountPruning, skipQuiets, ttCapture, pvExact;
512513
Piece movedPiece;
513-
int moveCount, quietCount;
514+
int moveCount, captureCount, quietCount;
514515

515516
// Step 1. Initialize node
516517
Thread* thisThread = pos.this_thread();
517518
inCheck = pos.checkers();
518-
moveCount = quietCount = ss->moveCount = 0;
519+
moveCount = captureCount = quietCount = ss->moveCount = 0;
519520
ss->statScore = 0;
520521
bestValue = -VALUE_INFINITE;
521522

@@ -579,6 +580,8 @@ namespace {
579580
{
580581
if (!pos.capture_or_promotion(ttMove))
581582
update_stats(pos, ss, ttMove, nullptr, 0, stat_bonus(depth));
583+
else
584+
update_capture_stats(pos, ttMove, nullptr, 0, stat_bonus(depth));
582585

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

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

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

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

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

10551058
if (!captureOrPromotion && move != bestMove && quietCount < 64)
10561059
quietsSearched[quietCount++] = move;
1060+
else if (captureOrPromotion && move != bestMove && captureCount < 32)
1061+
capturesSearched[captureCount++] = move;
10571062
}
10581063

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

10831090
// Extra penalty for a quiet TT move in previous ply when it gets refuted
10841091
if ((ss-1)->moveCount == 1 && !pos.captured_piece())
@@ -1207,7 +1214,7 @@ namespace {
12071214
// to search the moves. Because the depth is <= 0 here, only captures,
12081215
// queen promotions and checks (only if depth >= DEPTH_QS_CHECKS) will
12091216
// be generated.
1210-
MovePicker mp(pos, ttMove, depth, &pos.this_thread()->mainHistory, to_sq((ss-1)->currentMove));
1217+
MovePicker mp(pos, ttMove, depth, &pos.this_thread()->mainHistory, &pos.this_thread()->captureHistory, to_sq((ss-1)->currentMove));
12111218

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

13641371

1372+
// update_capture_stats() updates move sorting heuristics when a new capture best move is found
1373+
1374+
void update_capture_stats(const Position& pos, Move move,
1375+
Move* captures, int captureCnt, int bonus) {
1376+
1377+
CapturePieceToHistory& captureHistory = pos.this_thread()->captureHistory;
1378+
Piece moved_piece = pos.moved_piece(move);
1379+
PieceType captured = type_of(pos.piece_on(to_sq(move)));
1380+
captureHistory.update(moved_piece,to_sq(move), captured, bonus);
1381+
1382+
// Decrease all the other played capture moves
1383+
for (int i = 0; i < captureCnt; ++i)
1384+
{
1385+
moved_piece = pos.moved_piece(captures[i]);
1386+
captured = type_of(pos.piece_on(to_sq(captures[i])));
1387+
captureHistory.update(moved_piece, to_sq(captures[i]), captured, -bonus);
1388+
}
1389+
}
1390+
1391+
13651392
// update_stats() updates move sorting heuristics when a new quiet best move is found
13661393

13671394
void update_stats(const Position& pos, Stack* ss, Move move,

src/thread.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ void Thread::clear() {
5858

5959
counterMoves.fill(MOVE_NONE);
6060
mainHistory.fill(0);
61+
captureHistory.fill(0);
6162

6263
for (auto& to : contHistory)
6364
for (auto& h : to)

src/thread.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ class Thread {
6969
Depth rootDepth, completedDepth;
7070
CounterMoveHistory counterMoves;
7171
ButterflyHistory mainHistory;
72+
CapturePieceToHistory captureHistory;
7273
ContinuationHistory contHistory;
7374
};
7475

0 commit comments

Comments
 (0)