@@ -93,6 +93,11 @@ constexpr int futility_move_count(bool improving, Depth depth) {
9393 return improving ? (3 + depth * depth) : (3 + depth * depth) / 2 ;
9494}
9595
96+ // Guarantee evaluation does not hit the tablebase range
97+ constexpr Value to_static_eval (const Value v) {
98+ return std::clamp (v, VALUE_TB_LOSS_IN_MAX_PLY + 1 , VALUE_TB_WIN_IN_MAX_PLY - 1 );
99+ }
100+
96101// History and stats update bonus, based on depth
97102int stat_bonus (Depth d) { return std::min (268 * d - 352 , 1153 ); }
98103
@@ -712,6 +717,8 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo
712717
713718 CapturePieceToHistory& captureHistory = thisThread->captureHistory ;
714719
720+ Value unadjustedStaticEval = VALUE_NONE;
721+
715722 // Step 6. Static evaluation of the position
716723 if (ss->inCheck )
717724 {
@@ -725,26 +732,40 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo
725732 // Providing the hint that this node's accumulator will be used often
726733 // brings significant Elo gain (~13 Elo).
727734 Eval::NNUE::hint_common_parent_position (pos);
728- eval = ss->staticEval ;
735+ unadjustedStaticEval = eval = ss->staticEval ;
729736 }
730737 else if (ss->ttHit )
731738 {
732739 // Never assume anything about values stored in TT
733- ss->staticEval = eval = tte->eval ();
740+ unadjustedStaticEval = ss->staticEval = eval = tte->eval ();
734741 if (eval == VALUE_NONE)
735- ss->staticEval = eval = evaluate (pos);
742+ unadjustedStaticEval = ss->staticEval = eval = evaluate (pos);
736743 else if (PvNode)
737744 Eval::NNUE::hint_common_parent_position (pos);
738745
746+ Value newEval =
747+ ss->staticEval
748+ + thisThread->correctionHistory [us][pawn_structure_index<Correction>(pos)] / 32 ;
749+
750+ ss->staticEval = eval = to_static_eval (newEval);
751+
739752 // ttValue can be used as a better position evaluation (~7 Elo)
740753 if (ttValue != VALUE_NONE && (tte->bound () & (ttValue > eval ? BOUND_LOWER : BOUND_UPPER)))
741754 eval = ttValue;
742755 }
743756 else
744757 {
745- ss->staticEval = eval = evaluate (pos);
746- // Save static evaluation into the transposition table
747- tte->save (posKey, VALUE_NONE, ss->ttPv , BOUND_NONE, DEPTH_NONE, MOVE_NONE, eval);
758+ unadjustedStaticEval = ss->staticEval = eval = evaluate (pos);
759+
760+ Value newEval =
761+ ss->staticEval
762+ + thisThread->correctionHistory [us][pawn_structure_index<Correction>(pos)] / 32 ;
763+
764+ ss->staticEval = eval = to_static_eval (newEval);
765+
766+ // Static evaluation is saved as it was before adjustment by correction history
767+ tte->save (posKey, VALUE_NONE, ss->ttPv , BOUND_NONE, DEPTH_NONE, MOVE_NONE,
768+ unadjustedStaticEval);
748769 }
749770
750771 // Use static evaluation difference to improve quiet move ordering (~9 Elo)
@@ -753,7 +774,8 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo
753774 int bonus = std::clamp (-13 * int ((ss - 1 )->staticEval + ss->staticEval ), -1652 , 1546 );
754775 thisThread->mainHistory [~us][from_to ((ss - 1 )->currentMove )] << bonus;
755776 if (type_of (pos.piece_on (prevSq)) != PAWN && type_of ((ss - 1 )->currentMove ) != PROMOTION)
756- thisThread->pawnHistory [pawn_structure (pos)][pos.piece_on (prevSq)][prevSq] << bonus / 4 ;
777+ thisThread->pawnHistory [pawn_structure_index (pos)][pos.piece_on (prevSq)][prevSq]
778+ << bonus / 4 ;
757779 }
758780
759781 // Set up the improving flag, which is true if current static evaluation is
@@ -888,7 +910,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo
888910 {
889911 // Save ProbCut data into transposition table
890912 tte->save (posKey, value_to_tt (value, ss->ply ), ss->ttPv , BOUND_LOWER, depth - 3 ,
891- move, ss-> staticEval );
913+ move, unadjustedStaticEval );
892914 return std::abs (value) < VALUE_TB_WIN_IN_MAX_PLY ? value - (probCutBeta - beta)
893915 : value;
894916 }
@@ -999,10 +1021,10 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo
9991021 }
10001022 else
10011023 {
1002- int history = (*contHist[ 0 ])[movedPiece][ to_sq (move)]
1003- + (*contHist[1 ])[movedPiece][to_sq (move)]
1004- + (*contHist[3 ])[movedPiece][to_sq (move)]
1005- + thisThread->pawnHistory [pawn_structure (pos)][movedPiece][to_sq (move)];
1024+ int history =
1025+ (*contHist[ 0 ])[movedPiece][ to_sq (move)] + (*contHist[1 ])[movedPiece][to_sq (move)]
1026+ + (*contHist[3 ])[movedPiece][to_sq (move)]
1027+ + thisThread->pawnHistory [pawn_structure_index (pos)][movedPiece][to_sq (move)];
10061028
10071029 // Continuation history based pruning (~2 Elo)
10081030 if (lmrDepth < 6 && history < -3752 * depth)
@@ -1364,12 +1386,23 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo
13641386 ss->ttPv = ss->ttPv || ((ss - 1 )->ttPv && depth > 3 );
13651387
13661388 // Write gathered information in transposition table
1389+ // Static evaluation is saved as it was before correction history
13671390 if (!excludedMove && !(rootNode && thisThread->pvIdx ))
13681391 tte->save (posKey, value_to_tt (bestValue, ss->ply ), ss->ttPv ,
13691392 bestValue >= beta ? BOUND_LOWER
13701393 : PvNode && bestMove ? BOUND_EXACT
13711394 : BOUND_UPPER,
1372- depth, bestMove, ss->staticEval );
1395+ depth, bestMove, unadjustedStaticEval);
1396+
1397+ // Adjust correction history
1398+ if (!ss->inCheck && (!bestMove || !pos.capture (bestMove))
1399+ && !(bestValue >= beta && bestValue <= ss->staticEval )
1400+ && !(!bestMove && bestValue >= ss->staticEval ))
1401+ {
1402+ auto bonus = std::clamp (int (bestValue - ss->staticEval ) * depth / 8 ,
1403+ -CORRECTION_HISTORY_LIMIT / 4 , CORRECTION_HISTORY_LIMIT / 4 );
1404+ thisThread->correctionHistory [us][pawn_structure_index<Correction>(pos)] << bonus;
1405+ }
13731406
13741407 assert (bestValue > -VALUE_INFINITE && bestValue < VALUE_INFINITE);
13751408
@@ -1450,6 +1483,8 @@ Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth) {
14501483 && (tte->bound () & (ttValue >= beta ? BOUND_LOWER : BOUND_UPPER)))
14511484 return ttValue;
14521485
1486+ Value unadjustedStaticEval = VALUE_NONE;
1487+
14531488 // Step 4. Static evaluation of the position
14541489 if (ss->inCheck )
14551490 bestValue = futilityBase = -VALUE_INFINITE;
@@ -1458,25 +1493,39 @@ Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth) {
14581493 if (ss->ttHit )
14591494 {
14601495 // Never assume anything about values stored in TT
1461- if ((ss->staticEval = bestValue = tte->eval ()) == VALUE_NONE)
1462- ss->staticEval = bestValue = evaluate (pos);
1496+ if ((unadjustedStaticEval = ss->staticEval = bestValue = tte->eval ()) == VALUE_NONE)
1497+ unadjustedStaticEval = ss->staticEval = bestValue = evaluate (pos);
1498+
1499+ Value newEval =
1500+ ss->staticEval
1501+ + thisThread->correctionHistory [us][pawn_structure_index<Correction>(pos)] / 32 ;
1502+
1503+ ss->staticEval = bestValue = to_static_eval (newEval);
14631504
14641505 // ttValue can be used as a better position evaluation (~13 Elo)
14651506 if (ttValue != VALUE_NONE
14661507 && (tte->bound () & (ttValue > bestValue ? BOUND_LOWER : BOUND_UPPER)))
14671508 bestValue = ttValue;
14681509 }
14691510 else
1511+ {
14701512 // In case of null move search, use previous static eval with a different sign
1471- ss->staticEval = bestValue =
1513+ unadjustedStaticEval = ss->staticEval = bestValue =
14721514 (ss - 1 )->currentMove != MOVE_NULL ? evaluate (pos) : -(ss - 1 )->staticEval ;
14731515
1516+ Value newEval =
1517+ ss->staticEval
1518+ + thisThread->correctionHistory [us][pawn_structure_index<Correction>(pos)] / 32 ;
1519+
1520+ ss->staticEval = bestValue = to_static_eval (newEval);
1521+ }
1522+
14741523 // Stand pat. Return immediately if static value is at least beta
14751524 if (bestValue >= beta)
14761525 {
14771526 if (!ss->ttHit )
14781527 tte->save (posKey, value_to_tt (bestValue, ss->ply ), false , BOUND_LOWER, DEPTH_NONE,
1479- MOVE_NONE, ss-> staticEval );
1528+ MOVE_NONE, unadjustedStaticEval );
14801529
14811530 return bestValue;
14821531 }
@@ -1620,8 +1669,10 @@ Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth) {
16201669 bestValue = (3 * bestValue + beta) / 4 ;
16211670
16221671 // Save gathered info in transposition table
1672+ // Static evaluation is saved as it was before adjustment by correction history
16231673 tte->save (posKey, value_to_tt (bestValue, ss->ply ), pvHit,
1624- bestValue >= beta ? BOUND_LOWER : BOUND_UPPER, ttDepth, bestMove, ss->staticEval );
1674+ bestValue >= beta ? BOUND_LOWER : BOUND_UPPER, ttDepth, bestMove,
1675+ unadjustedStaticEval);
16251676
16261677 assert (bestValue > -VALUE_INFINITE && bestValue < VALUE_INFINITE);
16271678
@@ -1720,15 +1771,17 @@ void update_all_stats(const Position& pos,
17201771
17211772 // Increase stats for the best move in case it was a quiet move
17221773 update_quiet_stats (pos, ss, bestMove, bestMoveBonus);
1723- thisThread->pawnHistory [pawn_structure (pos)][moved_piece][to_sq (bestMove)]
1724- << quietMoveBonus;
1774+
1775+ int pIndex = pawn_structure_index (pos);
1776+ thisThread->pawnHistory [pIndex][moved_piece][to_sq (bestMove)] << quietMoveBonus;
17251777
17261778 // Decrease stats for all non-best quiet moves
17271779 for (int i = 0 ; i < quietCount; ++i)
17281780 {
1729- thisThread-> pawnHistory [ pawn_structure (pos)][pos. moved_piece (quietsSearched[i])]
1730- [to_sq (quietsSearched[i])]
1781+ thisThread
1782+ -> pawnHistory [pIndex][pos. moved_piece (quietsSearched[i])] [to_sq (quietsSearched[i])]
17311783 << -quietMoveMalus;
1784+
17321785 thisThread->mainHistory [us][from_to (quietsSearched[i])] << -quietMoveMalus;
17331786 update_continuation_histories (ss, pos.moved_piece (quietsSearched[i]),
17341787 to_sq (quietsSearched[i]), -quietMoveMalus);
0 commit comments