Skip to content

Commit

Permalink
Introduce Various Correction histories
Browse files Browse the repository at this point in the history
This patch introduces three additional correction histories, namely,
Major Piece Correction History, Minor Piece Correction History, and
Non-Pawn Correction History.

Introduced by @mcthouacbb in Sirius
(https://github.com/mcthouacbb/Sirius) chess engine. The Major Piece
Correction History is indexed by side-to-move and the Zobrist key
representing the position of the King, Rook, and Queen of both sides.
Likewise, the Minor Piece Correction History is indexed by side-to-move
and the Zobrist key representing the position of the King, Knight, and
Bishop of both sides.

Also See:
mcthouacbb/Sirius@97b85bb
mcthouacbb/Sirius@3099cde

Introduced by @zzzzz151 in Starzix (https://github.com/zzzzz151/Starzix)
chess engine. Non-Pawn correction history consists of side-to-move, side
of Zobrist key, and a Zobrist key representing of the position of all
non-pawn pieces of **one side**. The non-pawn correction values for both
key sides are then summed.

Also See:
zzzzz151/Starzix@3491177
zzzzz151/Starzix@33e0df8

The weights on the final correction value of the above correction
histories, as well as existing correction histories, are then tuned in
two separate SPSA sessions, totaling 75k games.

SPSA1:
https://tests.stockfishchess.org/tests/view/66e5243886d5ee47d953a86b
(Stopped early due to some weights reaching the maximum value)

SPSA2:
https://tests.stockfishchess.org/tests/view/66e6a26f86d5ee47d953a965

Also thanks to @martinnovaak, (Motor
https://github.com/martinnovaak/motor author) for insights and
suggestions.

Passed STC:
LLR: 2.94 (-2.94,2.94) <0.00,2.00>
Total: 23328 W: 6197 L: 5901 D: 11230
Ptnml(0-2): 82, 2582, 6041, 2876, 83
https://tests.stockfishchess.org/tests/view/66e8787b86d5ee47d953ab6f

Passed LTC:
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 10626 W: 2826 L: 2560 D: 5240
Ptnml(0-2): 4, 1054, 2941, 1300, 14
https://tests.stockfishchess.org/tests/view/66e8ab2386d5ee47d953aba8

closes official-stockfish#5598

Bench: 1011161
  • Loading branch information
xu-shawn authored and Disservin committed Sep 17, 2024
1 parent 240a5b1 commit 60351b9
Show file tree
Hide file tree
Showing 7 changed files with 140 additions and 27 deletions.
4 changes: 2 additions & 2 deletions src/bitboard.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -140,8 +140,8 @@ Bitboard sliding_attack(PieceType pt, Square sq, Bitboard occupied) {

// Computes all rook and bishop attacks at startup. Magic
// bitboards are used to look up attacks of sliding pieces. As a reference see
// www.chessprogramming.org/Magic_Bitboards. In particular, here we use the so
// called "fancy" approach.
// https://www.chessprogramming.org/Magic_Bitboards. In particular, here we use
// the so called "fancy" approach.
void init_magics(PieceType pt, Bitboard table[], Magic magics[]) {

// Optimal PRNG seeds to pick the correct magics in the shortest time
Expand Down
40 changes: 34 additions & 6 deletions src/movepick.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,13 @@

namespace Stockfish {

constexpr int PAWN_HISTORY_SIZE = 512; // has to be a power of 2
constexpr int PAWN_CORRECTION_HISTORY_SIZE = 16384; // has to be a power of 2
constexpr int MATERIAL_CORRECTION_HISTORY_SIZE = 32768; // has to be a power of 2
constexpr int CORRECTION_HISTORY_LIMIT = 1024;
constexpr int PAWN_HISTORY_SIZE = 512; // has to be a power of 2
constexpr int PAWN_CORRECTION_HISTORY_SIZE = 16384; // has to be a power of 2
constexpr int MATERIAL_CORRECTION_HISTORY_SIZE = 32768; // has to be a power of 2
constexpr int MAJOR_PIECE_CORRECTION_HISTORY_SIZE = 32768; // has to be a power of 2
constexpr int MINOR_PIECE_CORRECTION_HISTORY_SIZE = 32768; // has to be a power of 2
constexpr int NON_PAWN_CORRECTION_HISTORY_SIZE = 32768; // has to be a power of 2
constexpr int CORRECTION_HISTORY_LIMIT = 1024;

static_assert((PAWN_HISTORY_SIZE & (PAWN_HISTORY_SIZE - 1)) == 0,
"PAWN_HISTORY_SIZE has to be a power of 2");
Expand All @@ -59,6 +62,19 @@ inline int material_index(const Position& pos) {
return pos.material_key() & (MATERIAL_CORRECTION_HISTORY_SIZE - 1);
}

inline int major_piece_index(const Position& pos) {
return pos.major_piece_key() & (MAJOR_PIECE_CORRECTION_HISTORY_SIZE - 1);
}

inline int minor_piece_index(const Position& pos) {
return pos.minor_piece_key() & (MINOR_PIECE_CORRECTION_HISTORY_SIZE - 1);
}

template<Color c>
inline int non_pawn_index(const Position& pos) {
return pos.non_pawn_key(c) & (NON_PAWN_CORRECTION_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 a naked value
// to directly call history update operator<<() on the entry so to use stats
Expand Down Expand Up @@ -120,7 +136,7 @@ enum StatsType {
// 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 2 tables (one for each color) indexed by the move's from and to squares,
// see www.chessprogramming.org/Butterfly_Boards (~11 elo)
// see https://www.chessprogramming.org/Butterfly_Boards (~11 elo)
using ButterflyHistory = Stats<int16_t, 7183, COLOR_NB, int(SQUARE_NB) * int(SQUARE_NB)>;

// CapturePieceToHistory is addressed by a move's [piece][to][captured piece type]
Expand All @@ -138,10 +154,10 @@ using ContinuationHistory = Stats<PieceToHistory, NOT_USED, PIECE_NB, SQUARE_NB>
// PawnHistory 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>;


// Correction histories record differences between the static evaluation of
// positions and their search score. It is used to improve the static evaluation
// used by some search heuristics.
// see https://www.chessprogramming.org/Static_Evaluation_Correction_History

// PawnCorrectionHistory is addressed by color and pawn structure
using PawnCorrectionHistory =
Expand All @@ -151,6 +167,18 @@ using PawnCorrectionHistory =
using MaterialCorrectionHistory =
Stats<int16_t, CORRECTION_HISTORY_LIMIT, COLOR_NB, MATERIAL_CORRECTION_HISTORY_SIZE>;

// MajorPieceCorrectionHistory is addressed by color and king/major piece (Queen, Rook) positions
using MajorPieceCorrectionHistory =
Stats<int16_t, CORRECTION_HISTORY_LIMIT, COLOR_NB, MAJOR_PIECE_CORRECTION_HISTORY_SIZE>;

// MinorPieceCorrectionHistory is addressed by color and king/minor piece (Knight, Bishop) positions
using MinorPieceCorrectionHistory =
Stats<int16_t, CORRECTION_HISTORY_LIMIT, COLOR_NB, MINOR_PIECE_CORRECTION_HISTORY_SIZE>;

// NonPawnCorrectionHistory is addressed by color and non-pawn material positions
using NonPawnCorrectionHistory =
Stats<int16_t, CORRECTION_HISTORY_LIMIT, COLOR_NB, NON_PAWN_CORRECTION_HISTORY_SIZE>;

// The 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 emits one
// new pseudo-legal move on every call, until there are no moves left, when
Expand Down
66 changes: 61 additions & 5 deletions src/position.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -334,8 +334,10 @@ void Position::set_check_info() const {
// The function is only used when a new position is set up
void Position::set_state() const {

st->key = st->materialKey = 0;
st->pawnKey = Zobrist::noPawns;
st->key = st->materialKey = 0;
st->majorPieceKey = st->minorPieceKey = 0;
st->nonPawnKey[WHITE] = st->nonPawnKey[BLACK] = 0;
st->pawnKey = Zobrist::noPawns;
st->nonPawnMaterial[WHITE] = st->nonPawnMaterial[BLACK] = VALUE_ZERO;
st->checkersBB = attackers_to(square<KING>(sideToMove)) & pieces(~sideToMove);

Expand All @@ -350,8 +352,27 @@ void Position::set_state() const {
if (type_of(pc) == PAWN)
st->pawnKey ^= Zobrist::psq[pc][s];

else if (type_of(pc) != KING)
st->nonPawnMaterial[color_of(pc)] += PieceValue[pc];
else
{
st->nonPawnKey[color_of(pc)] ^= Zobrist::psq[pc][s];

if (type_of(pc) != KING)
{
st->nonPawnMaterial[color_of(pc)] += PieceValue[pc];

if (type_of(pc) == QUEEN || type_of(pc) == ROOK)
st->majorPieceKey ^= Zobrist::psq[pc][s];

else
st->minorPieceKey ^= Zobrist::psq[pc][s];
}

else
{
st->majorPieceKey ^= Zobrist::psq[pc][s];
st->minorPieceKey ^= Zobrist::psq[pc][s];
}
}
}

if (st->epSquare != SQ_NONE)
Expand Down Expand Up @@ -707,6 +728,8 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
do_castling<true>(us, from, to, rfrom, rto);

k ^= Zobrist::psq[captured][rfrom] ^ Zobrist::psq[captured][rto];
st->majorPieceKey ^= Zobrist::psq[captured][rfrom] ^ Zobrist::psq[captured][rto];
st->nonPawnKey[us] ^= Zobrist::psq[captured][rfrom] ^ Zobrist::psq[captured][rto];
captured = NO_PIECE;
}

Expand All @@ -732,7 +755,16 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
st->pawnKey ^= Zobrist::psq[captured][capsq];
}
else
{
st->nonPawnMaterial[them] -= PieceValue[captured];
st->nonPawnKey[them] ^= Zobrist::psq[captured][capsq];

if (type_of(pc) == QUEEN || type_of(pc) == ROOK)
st->majorPieceKey ^= Zobrist::psq[captured][capsq];

else
st->minorPieceKey ^= Zobrist::psq[captured][capsq];
}

dp.dirty_num = 2; // 1 piece moved, 1 piece captured
dp.piece[1] = captured;
Expand Down Expand Up @@ -790,7 +822,8 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {

else if (m.type_of() == PROMOTION)
{
Piece promotion = make_piece(us, m.promotion_type());
Piece promotion = make_piece(us, m.promotion_type());
PieceType promotionType = type_of(promotion);

assert(relative_rank(us, to) == RANK_8);
assert(type_of(promotion) >= KNIGHT && type_of(promotion) <= QUEEN);
Expand All @@ -811,6 +844,12 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
st->materialKey ^=
Zobrist::psq[promotion][pieceCount[promotion] - 1] ^ Zobrist::psq[pc][pieceCount[pc]];

if (promotionType == QUEEN || promotionType == ROOK)
st->majorPieceKey ^= Zobrist::psq[promotion][to];

else
st->minorPieceKey ^= Zobrist::psq[promotion][to];

// Update material
st->nonPawnMaterial[us] += PieceValue[promotion];
}
Expand All @@ -822,6 +861,23 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
st->rule50 = 0;
}

else
{
st->nonPawnKey[us] ^= Zobrist::psq[pc][from] ^ Zobrist::psq[pc][to];

if (type_of(pc) == KING)
{
st->majorPieceKey ^= Zobrist::psq[pc][from] ^ Zobrist::psq[pc][to];
st->minorPieceKey ^= Zobrist::psq[pc][from] ^ Zobrist::psq[pc][to];
}

else if (type_of(pc) == QUEEN || type_of(pc) == ROOK)
st->majorPieceKey ^= Zobrist::psq[pc][from] ^ Zobrist::psq[pc][to];

else
st->minorPieceKey ^= Zobrist::psq[pc][from] ^ Zobrist::psq[pc][to];
}

// Set capture piece
st->capturedPiece = captured;

Expand Down
12 changes: 12 additions & 0 deletions src/position.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ struct StateInfo {
// Copied when making a move
Key materialKey;
Key pawnKey;
Key majorPieceKey;
Key minorPieceKey;
Key nonPawnKey[COLOR_NB];
Value nonPawnMaterial[COLOR_NB];
int castlingRights;
int rule50;
Expand Down Expand Up @@ -151,6 +154,9 @@ class Position {
Key key_after(Move m) const;
Key material_key() const;
Key pawn_key() const;
Key major_piece_key() const;
Key minor_piece_key() const;
Key non_pawn_key(Color c) const;

// Other properties of the position
Color side_to_move() const;
Expand Down Expand Up @@ -298,6 +304,12 @@ inline Key Position::pawn_key() const { return st->pawnKey; }

inline Key Position::material_key() const { return st->materialKey; }

inline Key Position::major_piece_key() const { return st->majorPieceKey; }

inline Key Position::minor_piece_key() const { return st->minorPieceKey; }

inline Key Position::non_pawn_key(Color c) const { return st->nonPawnKey[c]; }

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

inline Value Position::non_pawn_material() const {
Expand Down
24 changes: 18 additions & 6 deletions src/search.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@
#include "thread.h"
#include "timeman.h"
#include "tt.h"
#include "types.h"
#include "uci.h"
#include "ucioption.h"

Expand Down Expand Up @@ -81,11 +80,16 @@ constexpr int futility_move_count(bool improving, Depth depth) {
// Add correctionHistory value to raw staticEval and guarantee evaluation
// does not hit the tablebase range.
Value to_corrected_static_eval(Value v, const Worker& w, const Position& pos) {
const auto pcv =
w.pawnCorrectionHistory[pos.side_to_move()][pawn_structure_index<Correction>(pos)];
const auto mcv = w.materialCorrectionHistory[pos.side_to_move()][material_index(pos)];
const auto cv = (2 * pcv + mcv) / 3;
v += 74 * cv / 512;
const Color us = pos.side_to_move();
const auto pcv = w.pawnCorrectionHistory[us][pawn_structure_index<Correction>(pos)];
const auto mcv = w.materialCorrectionHistory[us][material_index(pos)];
const auto macv = w.majorPieceCorrectionHistory[us][major_piece_index(pos)];
const auto micv = w.minorPieceCorrectionHistory[us][minor_piece_index(pos)];
const auto wnpcv = w.nonPawnCorrectionHistory[WHITE][us][non_pawn_index<WHITE>(pos)];
const auto bnpcv = w.nonPawnCorrectionHistory[BLACK][us][non_pawn_index<BLACK>(pos)];
const auto cv =
(98198 * pcv + 68968 * mcv + 54353 * macv + 85174 * micv + 85581 * (wnpcv + bnpcv)) / 2097152;
v += cv;
return std::clamp(v, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1);
}

Expand Down Expand Up @@ -500,6 +504,10 @@ void Search::Worker::clear() {
pawnHistory.fill(-1152);
pawnCorrectionHistory.fill(0);
materialCorrectionHistory.fill(0);
majorPieceCorrectionHistory.fill(0);
minorPieceCorrectionHistory.fill(0);
nonPawnCorrectionHistory[WHITE].fill(0);
nonPawnCorrectionHistory[BLACK].fill(0);

for (bool inCheck : {false, true})
for (StatsType c : {NoCaptures, Captures})
Expand Down Expand Up @@ -1403,6 +1411,10 @@ Value Search::Worker::search(
-CORRECTION_HISTORY_LIMIT / 4, CORRECTION_HISTORY_LIMIT / 4);
thisThread->pawnCorrectionHistory[us][pawn_structure_index<Correction>(pos)] << bonus;
thisThread->materialCorrectionHistory[us][material_index(pos)] << bonus;
thisThread->majorPieceCorrectionHistory[us][major_piece_index(pos)] << bonus;
thisThread->minorPieceCorrectionHistory[us][minor_piece_index(pos)] << bonus;
thisThread->nonPawnCorrectionHistory[WHITE][us][non_pawn_index<WHITE>(pos)] << bonus;
thisThread->nonPawnCorrectionHistory[BLACK][us][non_pawn_index<BLACK>(pos)] << bonus;
}

assert(bestValue > -VALUE_INFINITE && bestValue < VALUE_INFINITE);
Expand Down
19 changes: 12 additions & 7 deletions src/search.h
Original file line number Diff line number Diff line change
Expand Up @@ -277,13 +277,18 @@ class Worker {
void ensure_network_replicated();

// Public because they need to be updatable by the stats
ButterflyHistory mainHistory;
ButterflyHistory rootHistory;
CapturePieceToHistory captureHistory;
ContinuationHistory continuationHistory[2][2];
PawnHistory pawnHistory;
PawnCorrectionHistory pawnCorrectionHistory;
MaterialCorrectionHistory materialCorrectionHistory;
ButterflyHistory mainHistory;
ButterflyHistory rootHistory;

CapturePieceToHistory captureHistory;
ContinuationHistory continuationHistory[2][2];
PawnHistory pawnHistory;

PawnCorrectionHistory pawnCorrectionHistory;
MaterialCorrectionHistory materialCorrectionHistory;
MajorPieceCorrectionHistory majorPieceCorrectionHistory;
MinorPieceCorrectionHistory minorPieceCorrectionHistory;
NonPawnCorrectionHistory nonPawnCorrectionHistory[COLOR_NB];

private:
void iterative_deepening();
Expand Down
2 changes: 1 addition & 1 deletion tests/perft.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#!/bin/bash
# verify perft numbers (positions from www.chessprogramming.org/Perft_Results)
# verify perft numbers (positions from https://www.chessprogramming.org/Perft_Results)

error()
{
Expand Down

0 comments on commit 60351b9

Please sign in to comment.