Skip to content

Commit

Permalink
Introduce pawn structure based history
Browse files Browse the repository at this point in the history
Original idea by Seer chess engine https://github.com/connormcmonigle/seer-nnue,
coding done by Disservin, code refactoring done by locutus2 to match the style
of other histories.

This patch introduces pawn structure based history, which assings moves values
based on last digits of pawn structure hash and piece type of moved piece and
landing square of the move. Idea is that good places for pieces are quite often
determined by pawn structure of position. Used in 3 different places
- sorting of quiet moves, sorting of quiet check evasions and in history based
pruning in search.
  • Loading branch information
Vizvezdenec authored and PikaCat-OuO committed Oct 28, 2023
1 parent d07f17a commit 811d464
Show file tree
Hide file tree
Showing 7 changed files with 53 additions and 10 deletions.
13 changes: 11 additions & 2 deletions src/movepick.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,12 +82,14 @@ MovePicker::MovePicker(const Position& p,
const ButterflyHistory* mh,
const CapturePieceToHistory* cph,
const PieceToHistory** ch,
const PawnHistory& ph,
Move cm,
const Move* killers) :
pos(p),
mainHistory(mh),
captureHistory(cph),
continuationHistory(ch),
pawnHistory(ph),
ttMove(ttm),
refutations{{killers[0], 0}, {killers[1], 0}, {cm, 0}},
depth(d) {
Expand All @@ -103,11 +105,13 @@ MovePicker::MovePicker(const Position& p,
const ButterflyHistory* mh,
const CapturePieceToHistory* cph,
const PieceToHistory** ch,
const PawnHistory& ph,
Square rs) :
pos(p),
mainHistory(mh),
captureHistory(cph),
continuationHistory(ch),
pawnHistory(ph),
ttMove(ttm),
recaptureSquare(rs),
depth(d) {
Expand All @@ -118,9 +122,11 @@ MovePicker::MovePicker(const Position& p,

// Constructor for ProbCut: we generate captures with SEE greater
// than or equal to the given threshold.
MovePicker::MovePicker(const Position& p, Move ttm, Value th, const CapturePieceToHistory* cph) :
MovePicker::MovePicker(
const Position& p, Move ttm, Value th, const CapturePieceToHistory* cph, const PawnHistory& ph) :
pos(p),
captureHistory(cph),
pawnHistory(ph),
ttMove(ttm),
threshold(th) {
assert(!pos.checkers());
Expand Down Expand Up @@ -199,6 +205,8 @@ void MovePicker::score() {
: pt != PAWN ? bool(to & threatenedByPawn) * 15000
: 0)
: 0;

m.value += pawnHistory[pawn_structure(pos)][pc][to];
}

else // Type == EVASIONS
Expand All @@ -208,7 +216,8 @@ void MovePicker::score() {
+ (1 << 28);
else
m.value = (*mainHistory)[pos.side_to_move()][from_to(m)]
+ (*continuationHistory[0])[pos.moved_piece(m)][to_sq(m)];
+ (*continuationHistory[0])[pos.moved_piece(m)][to_sq(m)]
+ pawnHistory[pawn_structure(pos)][pos.moved_piece(m)][to_sq(m)];
}
}
}
Expand Down
13 changes: 11 additions & 2 deletions src/movepick.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,13 @@

#include "movegen.h"
#include "types.h"
#include "position.h"

namespace Stockfish {
class Position;

constexpr int PAWN_HISTORY_SIZE = 512;

inline int pawn_structure(const Position& pos) { return pos.pawn_key() & (PAWN_HISTORY_SIZE - 1); }

// StatsEntry stores the stat table value. It is usually a number but could
// be a move or even a nested history. We use a class instead of naked value
Expand Down Expand Up @@ -112,6 +116,8 @@ using PieceToHistory = Stats<int16_t, 29952, PIECE_NB, SQUARE_NB>;
// (~63 elo)
using ContinuationHistory = Stats<PieceToHistory, NOT_USED, PIECE_NB, SQUARE_NB>;

// PawnStructureHistory is addressed by the pawn structure and a move's [piece][to]
using PawnHistory = Stats<int16_t, 8192, PAWN_HISTORY_SIZE, PIECE_NB, SQUARE_NB>;

// MovePicker class is used to pick one pseudo-legal move at a time from the
// current position. The most important method is next_move(), which returns a
Expand All @@ -135,6 +141,7 @@ class MovePicker {
const ButterflyHistory*,
const CapturePieceToHistory*,
const PieceToHistory**,
const PawnHistory&,
Move,
const Move*);
MovePicker(const Position&,
Expand All @@ -143,8 +150,9 @@ class MovePicker {
const ButterflyHistory*,
const CapturePieceToHistory*,
const PieceToHistory**,
const PawnHistory&,
Square);
MovePicker(const Position&, Move, Value, const CapturePieceToHistory*);
MovePicker(const Position&, Move, Value, const CapturePieceToHistory*, const PawnHistory&);
Move next_move(bool skipQuiets = false);

private:
Expand All @@ -159,6 +167,7 @@ class MovePicker {
const ButterflyHistory* mainHistory;
const CapturePieceToHistory* captureHistory;
const PieceToHistory** continuationHistory;
const PawnHistory& pawnHistory;
Move ttMove;
ExtMove refutations[3], *cur, *endMoves, *endBadCaptures;
int stage;
Expand Down
16 changes: 14 additions & 2 deletions src/position.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ namespace Stockfish {
namespace Zobrist {

Key psq[PIECE_NB][SQUARE_NB];
Key side;
Key side, noPawns;
}

namespace {
Expand Down Expand Up @@ -89,7 +89,8 @@ void Position::init() {
for (Square s = SQ_A0; s <= SQ_I9; ++s)
Zobrist::psq[pc][s] = rng.rand<Key>();

Zobrist::side = rng.rand<Key>();
Zobrist::side = rng.rand<Key>();
Zobrist::noPawns = rng.rand<Key>();
}


Expand Down Expand Up @@ -201,6 +202,7 @@ void Position::set_check_info() const {
void Position::set_state() const {

st->key = 0;
st->pawnKey = Zobrist::noPawns;
st->material[WHITE] = st->material[BLACK] = VALUE_ZERO;
st->checkersBB = checkers_to(~sideToMove, square<KING>(sideToMove));
st->move = MOVE_NONE;
Expand All @@ -214,7 +216,11 @@ void Position::set_state() const {
st->key ^= Zobrist::psq[pc][s];

if (type_of(pc) != KING)
{
st->material[color_of(pc)] += PieceValue[pc];
if (type_of(pc) == PAWN)
st->pawnKey ^= Zobrist::psq[pc][s];
}
}

if (sideToMove == BLACK)
Expand Down Expand Up @@ -470,13 +476,19 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {

// Update hash key
k ^= Zobrist::psq[captured][capsq];
// If the captured piece is a pawn, update pawn hash key.
if (type_of(captured) == PAWN)
st->pawnKey ^= Zobrist::psq[captured][capsq];

// Reset rule 60 counter
st->check10[WHITE] = st->check10[BLACK] = st->rule60 = 0;
}

// Update hash key
k ^= Zobrist::psq[pc][from] ^ Zobrist::psq[pc][to];
// If the moving piece is a pawn, update pawn hash key.
if (type_of(pc) == PAWN)
st->pawnKey ^= Zobrist::psq[pc][to];

// Move the piece.
dp.piece[0] = pc;
Expand Down
4 changes: 4 additions & 0 deletions src/position.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ namespace Stockfish {
struct StateInfo {

// Copied when making a move
Key pawnKey;
Value material[COLOR_NB];
int16_t check10[COLOR_NB];
int rule60;
Expand Down Expand Up @@ -143,6 +144,7 @@ class Position {
// Accessing hash keys
Key key() const;
Key key_after(Move m) const;
Key pawn_key() const;

// Other properties of the position
Color side_to_move() const;
Expand Down Expand Up @@ -273,6 +275,8 @@ inline Key Position::adjust_key60(Key k) const {
return st->rule60 < 14 - AfterMove ? k : k ^ make_key((st->rule60 - (14 - AfterMove)) / 8);
}

inline Key Position::pawn_key() const { return st->pawnKey; }

inline Value Position::material(Color c) const { return st->material[c]; }

inline Value Position::material() const { return material(WHITE) + material(BLACK); }
Expand Down
15 changes: 11 additions & 4 deletions src/search.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -736,7 +736,8 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo
{
assert(probCutBeta < VALUE_INFINITE);

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

while ((move = mp.next_move()) != MOVE_NONE)
if (move != excludedMove && pos.legal(move))
Expand Down Expand Up @@ -792,7 +793,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo
prevSq != SQ_NONE ? thisThread->counterMoves[pos.piece_on(prevSq)][prevSq] : MOVE_NONE;

MovePicker mp(pos, ttMove, depth, &thisThread->mainHistory, &captureHistory, contHist,
countermove, ss->killers);
thisThread->pawnHistory, countermove, ss->killers);

value = bestValue;
moveCountPruning = singularQuietLMR = false;
Expand Down Expand Up @@ -871,7 +872,8 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo
{
int history = (*contHist[0])[movedPiece][to_sq(move)]
+ (*contHist[1])[movedPiece][to_sq(move)]
+ (*contHist[3])[movedPiece][to_sq(move)];
+ (*contHist[3])[movedPiece][to_sq(move)]
+ thisThread->pawnHistory[pawn_structure(pos)][movedPiece][to_sq(move)];

// Continuation history based pruning (~2 Elo)
if (lmrDepth < 6 && history < -3491 * depth)
Expand Down Expand Up @@ -1336,7 +1338,7 @@ Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth) {
// and other checks (only if depth >= DEPTH_QS_CHECKS) will be generated.
Square prevSq = is_ok((ss - 1)->currentMove) ? to_sq((ss - 1)->currentMove) : SQ_NONE;
MovePicker mp(pos, ttMove, depth, &thisThread->mainHistory, &thisThread->captureHistory,
contHist, prevSq);
contHist, thisThread->pawnHistory, prevSq);

int quietCheckEvasions = 0;

Expand Down Expand Up @@ -1532,10 +1534,15 @@ void update_all_stats(const Position& pos,

// Increase stats for the best move in case it was a quiet move
update_quiet_stats(pos, ss, bestMove, bestMoveBonus);
thisThread->pawnHistory[pawn_structure(pos)][moved_piece][to_sq(bestMove)]
<< quietMoveBonus;

// Decrease stats for all non-best quiet moves
for (int i = 0; i < quietCount; ++i)
{
thisThread->pawnHistory[pawn_structure(pos)][pos.moved_piece(quietsSearched[i])]
[to_sq(quietsSearched[i])]
<< -bestMoveBonus;
thisThread->mainHistory[us][from_to(quietsSearched[i])] << -bestMoveBonus;
update_continuation_histories(ss, pos.moved_piece(quietsSearched[i]),
to_sq(quietsSearched[i]), -bestMoveBonus);
Expand Down
1 change: 1 addition & 0 deletions src/thread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ void Thread::clear() {
counterMoves.fill(MOVE_NONE);
mainHistory.fill(0);
captureHistory.fill(0);
pawnHistory.fill(0);

for (bool inCheck : {false, true})
for (StatsType c : {NoCaptures, Captures})
Expand Down
1 change: 1 addition & 0 deletions src/thread.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ class Thread {
ButterflyHistory mainHistory;
CapturePieceToHistory captureHistory;
ContinuationHistory continuationHistory[2][2];
PawnHistory pawnHistory;
};


Expand Down

0 comments on commit 811d464

Please sign in to comment.