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:
parent
81c1d31084
commit
6592b13d56
4 changed files with 63 additions and 28 deletions
1
AUTHORS
1
AUTHORS
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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();
|
||||||
|
|
Loading…
Add table
Reference in a new issue