1
0
Fork 0
mirror of https://github.com/sockspls/badfish synced 2025-04-30 00:33:09 +00:00

Introduce Continuation Correction History

Continuation correction history uses last 2 move to correct static eval.

ContCorrHist first introduced by @martinnovaak in
Motor(https://github.com/martinnovaak/motor/pull/162). Earlier ideas
using last move to correct eval is introduced by @MinusKelvin in
Ice4(45daf7d9ea)

Passed STC:

LLR: 2.96 (-2.94,2.94) <0.00,2.00>
Total: 310144 W: 81267 L: 80538 D: 148339
Ptnml(0-2): 1160, 36607, 78834, 37286, 1185
https://tests.stockfishchess.org/tests/view/66f96cbc86d5ee47d953b7f7

Passed LTC:

LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 97470 W: 24892 L: 24447 D: 48131
Ptnml(0-2): 63, 10631, 26915, 11050, 76
https://tests.stockfishchess.org/tests/view/66fd59bc86d5ee47d953b9ea

closes https://github.com/official-stockfish/Stockfish/pull/5617

Bench: 1143382
This commit is contained in:
Ömer Faruk Tutkun 2024-10-04 17:46:47 +03:00 committed by Disservin
parent 81c1d31084
commit 6592b13d56
4 changed files with 63 additions and 28 deletions

View file

@ -176,6 +176,7 @@ Ofek Shochat (OfekShochat, ghostway)
Ondrej Mosnáček (WOnder93) Ondrej Mosnáček (WOnder93)
Ondřej Mišina (AndrovT) Ondřej Mišina (AndrovT)
Oskar Werkelin Ahlin Oskar Werkelin Ahlin
Ömer Faruk Tutkun (OmerFarukTutkun)
Pablo Vazquez Pablo Vazquez
Panthee Panthee
Pascal Romaret Pascal Romaret

View file

@ -145,6 +145,9 @@ using CapturePieceToHistory = Stats<int16_t, 10692, PIECE_NB, SQUARE_NB, PIECE_T
// PieceToHistory is like ButterflyHistory but is addressed by a move's [piece][to] // PieceToHistory is like ButterflyHistory but is addressed by a move's [piece][to]
using PieceToHistory = Stats<int16_t, 29952, PIECE_NB, SQUARE_NB>; using PieceToHistory = Stats<int16_t, 29952, PIECE_NB, SQUARE_NB>;
// PieceToCorrectionHistory is addressed by a move's [piece][to]
using PieceToCorrectionHistory = Stats<int16_t, CORRECTION_HISTORY_LIMIT, PIECE_NB, SQUARE_NB>;
// ContinuationHistory is the combined history of a given pair of moves, usually // ContinuationHistory is the combined history of a given pair of moves, usually
// the current one given a previous one. The nested history table is based on // the current one given a previous one. The nested history table is based on
// PieceToHistory instead of ButterflyBoards. // PieceToHistory instead of ButterflyBoards.
@ -179,6 +182,10 @@ using MinorPieceCorrectionHistory =
using NonPawnCorrectionHistory = using NonPawnCorrectionHistory =
Stats<int16_t, CORRECTION_HISTORY_LIMIT, COLOR_NB, CORRECTION_HISTORY_SIZE>; Stats<int16_t, CORRECTION_HISTORY_LIMIT, COLOR_NB, CORRECTION_HISTORY_SIZE>;
// ContinuationCorrectionHistory is the combined correction history of a given pair of moves
using ContinuationCorrectionHistory =
Stats<PieceToCorrectionHistory, NOT_USED, PIECE_NB, SQUARE_NB>;
// The MovePicker class is used to pick one pseudo-legal move at a time from the // 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 // 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 // new pseudo-legal move on every call, until there are no moves left, when

View file

@ -79,16 +79,23 @@ constexpr int futility_move_count(bool improving, Depth depth) {
// Add correctionHistory value to raw staticEval and guarantee evaluation // Add correctionHistory value to raw staticEval and guarantee evaluation
// does not hit the tablebase range. // does not hit the tablebase range.
Value to_corrected_static_eval(Value v, const Worker& w, const Position& pos) { Value to_corrected_static_eval(Value v, const Worker& w, const Position& pos, Stack* ss) {
const Color us = pos.side_to_move(); const Color us = pos.side_to_move();
const auto m = (ss - 1)->currentMove;
const auto pcv = w.pawnCorrectionHistory[us][pawn_structure_index<Correction>(pos)]; const auto pcv = w.pawnCorrectionHistory[us][pawn_structure_index<Correction>(pos)];
const auto mcv = w.materialCorrectionHistory[us][material_index(pos)]; const auto mcv = w.materialCorrectionHistory[us][material_index(pos)];
const auto macv = w.majorPieceCorrectionHistory[us][major_piece_index(pos)]; const auto macv = w.majorPieceCorrectionHistory[us][major_piece_index(pos)];
const auto micv = w.minorPieceCorrectionHistory[us][minor_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 wnpcv = w.nonPawnCorrectionHistory[WHITE][us][non_pawn_index<WHITE>(pos)];
const auto bnpcv = w.nonPawnCorrectionHistory[BLACK][us][non_pawn_index<BLACK>(pos)]; const auto bnpcv = w.nonPawnCorrectionHistory[BLACK][us][non_pawn_index<BLACK>(pos)];
int cntcv = 1;
if (m.is_ok())
cntcv = int((*(ss - 2)->continuationCorrectionHistory)[pos.piece_on(m.to_sq())][m.to_sq()]);
const auto cv = const auto cv =
(6245 * pcv + 3442 * mcv + 3471 * macv + 5958 * micv + 6566 * (wnpcv + bnpcv)) / 131072; (5932 * pcv + 2994 * mcv + 3269 * macv + 5660 * micv + 6237 * (wnpcv + bnpcv) + cntcv * 5555)
/ 131072;
v += cv; v += cv;
return std::clamp(v, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1); return std::clamp(v, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1);
} }
@ -240,6 +247,7 @@ void Search::Worker::iterative_deepening() {
{ {
(ss - i)->continuationHistory = (ss - i)->continuationHistory =
&this->continuationHistory[0][0][NO_PIECE][0]; // Use as a sentinel &this->continuationHistory[0][0][NO_PIECE][0]; // Use as a sentinel
(ss - i)->continuationCorrectionHistory = &this->continuationCorrectionHistory[NO_PIECE][0];
(ss - i)->staticEval = VALUE_NONE; (ss - i)->staticEval = VALUE_NONE;
} }
@ -504,6 +512,10 @@ void Search::Worker::clear() {
nonPawnCorrectionHistory[WHITE].fill(0); nonPawnCorrectionHistory[WHITE].fill(0);
nonPawnCorrectionHistory[BLACK].fill(0); nonPawnCorrectionHistory[BLACK].fill(0);
for (auto& to : continuationCorrectionHistory)
for (auto& h : to)
h->fill(0);
for (bool inCheck : {false, true}) for (bool inCheck : {false, true})
for (StatsType c : {NoCaptures, Captures}) for (StatsType c : {NoCaptures, Captures})
for (auto& to : continuationHistory[inCheck][c]) for (auto& to : continuationHistory[inCheck][c])
@ -727,7 +739,8 @@ Value Search::Worker::search(
else if (PvNode) else if (PvNode)
Eval::NNUE::hint_common_parent_position(pos, networks[numaAccessToken], refreshTable); Eval::NNUE::hint_common_parent_position(pos, networks[numaAccessToken], refreshTable);
ss->staticEval = eval = to_corrected_static_eval(unadjustedStaticEval, *thisThread, pos); ss->staticEval = eval =
to_corrected_static_eval(unadjustedStaticEval, *thisThread, pos, ss);
// ttValue can be used as a better position evaluation (~7 Elo) // ttValue can be used as a better position evaluation (~7 Elo)
if (ttData.value != VALUE_NONE if (ttData.value != VALUE_NONE
@ -738,7 +751,8 @@ Value Search::Worker::search(
{ {
unadjustedStaticEval = unadjustedStaticEval =
evaluate(networks[numaAccessToken], pos, refreshTable, thisThread->optimism[us]); evaluate(networks[numaAccessToken], pos, refreshTable, thisThread->optimism[us]);
ss->staticEval = eval = to_corrected_static_eval(unadjustedStaticEval, *thisThread, pos); ss->staticEval = eval =
to_corrected_static_eval(unadjustedStaticEval, *thisThread, pos, ss);
// Static evaluation is saved as it was before adjustment by correction history // Static evaluation is saved as it was before adjustment by correction history
ttWriter.write(posKey, VALUE_NONE, ss->ttPv, BOUND_NONE, DEPTH_UNSEARCHED, Move::none(), ttWriter.write(posKey, VALUE_NONE, ss->ttPv, BOUND_NONE, DEPTH_UNSEARCHED, Move::none(),
@ -795,6 +809,7 @@ Value Search::Worker::search(
ss->currentMove = Move::null(); ss->currentMove = Move::null();
ss->continuationHistory = &thisThread->continuationHistory[0][0][NO_PIECE][0]; ss->continuationHistory = &thisThread->continuationHistory[0][0][NO_PIECE][0];
ss->continuationCorrectionHistory = &thisThread->continuationCorrectionHistory[NO_PIECE][0];
pos.do_null_move(st, tt); pos.do_null_move(st, tt);
@ -876,6 +891,8 @@ Value Search::Worker::search(
ss->currentMove = move; ss->currentMove = move;
ss->continuationHistory = ss->continuationHistory =
&this->continuationHistory[ss->inCheck][true][pos.moved_piece(move)][move.to_sq()]; &this->continuationHistory[ss->inCheck][true][pos.moved_piece(move)][move.to_sq()];
ss->continuationCorrectionHistory =
&this->continuationCorrectionHistory[pos.moved_piece(move)][move.to_sq()];
thisThread->nodes.fetch_add(1, std::memory_order_relaxed); thisThread->nodes.fetch_add(1, std::memory_order_relaxed);
pos.do_move(move, st); pos.do_move(move, st);
@ -1124,7 +1141,8 @@ moves_loop: // When in check, search starts here
ss->currentMove = move; ss->currentMove = move;
ss->continuationHistory = ss->continuationHistory =
&thisThread->continuationHistory[ss->inCheck][capture][movedPiece][move.to_sq()]; &thisThread->continuationHistory[ss->inCheck][capture][movedPiece][move.to_sq()];
ss->continuationCorrectionHistory =
&thisThread->continuationCorrectionHistory[movedPiece][move.to_sq()];
uint64_t nodeCount = rootNode ? uint64_t(nodes) : 0; uint64_t nodeCount = rootNode ? uint64_t(nodes) : 0;
// Step 16. Make the move // Step 16. Make the move
@ -1401,6 +1419,8 @@ moves_loop: // When in check, search starts here
&& !(bestValue >= beta && bestValue <= ss->staticEval) && !(bestValue >= beta && bestValue <= ss->staticEval)
&& !(!bestMove && bestValue >= ss->staticEval)) && !(!bestMove && bestValue >= ss->staticEval))
{ {
const auto m = (ss - 1)->currentMove;
auto bonus = std::clamp(int(bestValue - ss->staticEval) * depth / 8, auto bonus = std::clamp(int(bestValue - ss->staticEval) * depth / 8,
-CORRECTION_HISTORY_LIMIT / 4, CORRECTION_HISTORY_LIMIT / 4); -CORRECTION_HISTORY_LIMIT / 4, CORRECTION_HISTORY_LIMIT / 4);
thisThread->pawnCorrectionHistory[us][pawn_structure_index<Correction>(pos)] thisThread->pawnCorrectionHistory[us][pawn_structure_index<Correction>(pos)]
@ -1412,6 +1432,9 @@ moves_loop: // When in check, search starts here
<< bonus * 123 / 128; << bonus * 123 / 128;
thisThread->nonPawnCorrectionHistory[BLACK][us][non_pawn_index<BLACK>(pos)] thisThread->nonPawnCorrectionHistory[BLACK][us][non_pawn_index<BLACK>(pos)]
<< bonus * 165 / 128; << bonus * 165 / 128;
if (m.is_ok())
(*(ss - 2)->continuationCorrectionHistory)[pos.piece_on(m.to_sq())][m.to_sq()] << bonus;
} }
assert(bestValue > -VALUE_INFINITE && bestValue < VALUE_INFINITE); assert(bestValue > -VALUE_INFINITE && bestValue < VALUE_INFINITE);
@ -1507,7 +1530,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta)
unadjustedStaticEval = unadjustedStaticEval =
evaluate(networks[numaAccessToken], pos, refreshTable, thisThread->optimism[us]); evaluate(networks[numaAccessToken], pos, refreshTable, thisThread->optimism[us]);
ss->staticEval = bestValue = ss->staticEval = bestValue =
to_corrected_static_eval(unadjustedStaticEval, *thisThread, pos); to_corrected_static_eval(unadjustedStaticEval, *thisThread, pos, ss);
// ttValue can be used as a better position evaluation (~13 Elo) // ttValue can be used as a better position evaluation (~13 Elo)
if (std::abs(ttData.value) < VALUE_TB_WIN_IN_MAX_PLY if (std::abs(ttData.value) < VALUE_TB_WIN_IN_MAX_PLY
@ -1522,7 +1545,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta)
? evaluate(networks[numaAccessToken], pos, refreshTable, thisThread->optimism[us]) ? evaluate(networks[numaAccessToken], pos, refreshTable, thisThread->optimism[us])
: -(ss - 1)->staticEval; : -(ss - 1)->staticEval;
ss->staticEval = bestValue = ss->staticEval = bestValue =
to_corrected_static_eval(unadjustedStaticEval, *thisThread, pos); to_corrected_static_eval(unadjustedStaticEval, *thisThread, pos, ss);
} }
// Stand pat. Return immediately if static value is at least beta // Stand pat. Return immediately if static value is at least beta
@ -1619,6 +1642,8 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta)
ss->continuationHistory = ss->continuationHistory =
&thisThread &thisThread
->continuationHistory[ss->inCheck][capture][pos.moved_piece(move)][move.to_sq()]; ->continuationHistory[ss->inCheck][capture][pos.moved_piece(move)][move.to_sq()];
ss->continuationCorrectionHistory =
&thisThread->continuationCorrectionHistory[pos.moved_piece(move)][move.to_sq()];
// Step 7. Make and search the move // Step 7. Make and search the move
thisThread->nodes.fetch_add(1, std::memory_order_relaxed); thisThread->nodes.fetch_add(1, std::memory_order_relaxed);

View file

@ -63,6 +63,7 @@ namespace Search {
struct Stack { struct Stack {
Move* pv; Move* pv;
PieceToHistory* continuationHistory; PieceToHistory* continuationHistory;
PieceToCorrectionHistory* continuationCorrectionHistory;
int ply; int ply;
Move currentMove; Move currentMove;
Move excludedMove; Move excludedMove;
@ -289,6 +290,7 @@ class Worker {
MajorPieceCorrectionHistory majorPieceCorrectionHistory; MajorPieceCorrectionHistory majorPieceCorrectionHistory;
MinorPieceCorrectionHistory minorPieceCorrectionHistory; MinorPieceCorrectionHistory minorPieceCorrectionHistory;
NonPawnCorrectionHistory nonPawnCorrectionHistory[COLOR_NB]; NonPawnCorrectionHistory nonPawnCorrectionHistory[COLOR_NB];
ContinuationCorrectionHistory continuationCorrectionHistory;
private: private:
void iterative_deepening(); void iterative_deepening();