diff --git a/src/bitboard.cpp b/src/bitboard.cpp index c842ca1271e..a8b4e5f4464 100644 --- a/src/bitboard.cpp +++ b/src/bitboard.cpp @@ -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 diff --git a/src/movepick.h b/src/movepick.h index f66cdadf5bb..13b9635bb2a 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -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"); @@ -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 +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 @@ -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; // CapturePieceToHistory is addressed by a move's [piece][to][captured piece type] @@ -138,10 +154,10 @@ using ContinuationHistory = Stats // PawnHistory is addressed by the pawn structure and a move's [piece][to] using PawnHistory = Stats; - // 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 = @@ -151,6 +167,18 @@ using PawnCorrectionHistory = using MaterialCorrectionHistory = Stats; +// MajorPieceCorrectionHistory is addressed by color and king/major piece (Queen, Rook) positions +using MajorPieceCorrectionHistory = + Stats; + +// MinorPieceCorrectionHistory is addressed by color and king/minor piece (Knight, Bishop) positions +using MinorPieceCorrectionHistory = + Stats; + +// NonPawnCorrectionHistory is addressed by color and non-pawn material positions +using NonPawnCorrectionHistory = + Stats; + // 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 diff --git a/src/position.cpp b/src/position.cpp index df95ffef380..f596b015355 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -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(sideToMove)) & pieces(~sideToMove); @@ -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) @@ -707,6 +728,8 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { do_castling(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; } @@ -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; @@ -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); @@ -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]; } @@ -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; diff --git a/src/position.h b/src/position.h index 6cac1731951..888612da78d 100644 --- a/src/position.h +++ b/src/position.h @@ -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; @@ -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; @@ -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 { diff --git a/src/search.cpp b/src/search.cpp index 3c6da163b89..199b9355403 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -46,7 +46,6 @@ #include "thread.h" #include "timeman.h" #include "tt.h" -#include "types.h" #include "uci.h" #include "ucioption.h" @@ -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(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(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(pos)]; + const auto bnpcv = w.nonPawnCorrectionHistory[BLACK][us][non_pawn_index(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); } @@ -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}) @@ -1403,6 +1411,10 @@ Value Search::Worker::search( -CORRECTION_HISTORY_LIMIT / 4, CORRECTION_HISTORY_LIMIT / 4); thisThread->pawnCorrectionHistory[us][pawn_structure_index(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(pos)] << bonus; + thisThread->nonPawnCorrectionHistory[BLACK][us][non_pawn_index(pos)] << bonus; } assert(bestValue > -VALUE_INFINITE && bestValue < VALUE_INFINITE); diff --git a/src/search.h b/src/search.h index b06c7c9484b..d7a909a82a8 100644 --- a/src/search.h +++ b/src/search.h @@ -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(); diff --git a/tests/perft.sh b/tests/perft.sh index 545e750fec0..c1532c20c19 100755 --- a/tests/perft.sh +++ b/tests/perft.sh @@ -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() {