#include "misc.h"
#include "pawns.h"
#include "thread.h"
+#include "timeman.h"
#include "uci.h"
#include "incbin/incbin.h"
}
}
+ /// NNUE::export_net() exports the currently loaded network to a file
+ void NNUE::export_net(const std::optional<std::string>& filename) {
+ std::string actualFilename;
+
+ if (filename.has_value())
+ actualFilename = filename.value();
+ else
+ {
+ if (eval_file_loaded != EvalFileDefaultName)
+ {
+ sync_cout << "Failed to export a net. A non-embedded net can only be saved if the filename is specified." << sync_endl;
+ return;
+ }
+ actualFilename = EvalFileDefaultName;
+ }
+
+ ofstream stream(actualFilename, std::ios_base::binary);
+
+ if (save_eval(stream))
+ sync_cout << "Network saved successfully to " << actualFilename << "." << sync_endl;
+ else
+ sync_cout << "Failed to export a net." << sync_endl;
+ }
+
/// NNUE::verify() verifies that the last net used was loaded successfully
void NNUE::verify() {
S(0, 0), S(3, 44), S(37, 68), S(42, 60), S(0, 39), S(58, 43)
};
+ constexpr Value CorneredBishop = Value(50);
+
// Assorted bonuses and penalties
constexpr Score UncontestedOutpost = S( 1, 10);
constexpr Score BishopOnKingRing = S( 24, 0);
constexpr Score BishopXRayPawns = S( 4, 5);
- constexpr Score CorneredBishop = S( 50, 50);
constexpr Score FlankAttacks = S( 8, 0);
constexpr Score Hanging = S( 69, 36);
constexpr Score KnightOnQueen = S( 16, 11);
{
Direction d = pawn_push(Us) + (file_of(s) == FILE_A ? EAST : WEST);
if (pos.piece_on(s + d) == make_piece(Us, PAWN))
- score -= !pos.empty(s + d + pawn_push(Us)) ? CorneredBishop * 4
- : pos.piece_on(s + d + d) == make_piece(Us, PAWN) ? CorneredBishop * 2
- : CorneredBishop;
+ score -= !pos.empty(s + d + pawn_push(Us)) ? 4 * make_score(CorneredBishop, CorneredBishop)
+ : 3 * make_score(CorneredBishop, CorneredBishop);
}
}
}
Color strongSide = eg > VALUE_DRAW ? WHITE : BLACK;
int sf = me->scale_factor(pos, strongSide);
- // If scale factor is not already specific, scale down via general heuristics
+ // If scale factor is not already specific, scale up/down via general heuristics
if (sf == SCALE_FACTOR_NORMAL)
{
if (pos.opposite_bishops())
if (!(pos.pieces(BISHOP) & Corners))
return VALUE_ZERO;
- constexpr int penalty1 = -209;
- constexpr int penalty2 = -136;
- constexpr int penalty3 = -148;
-
int correction = 0;
if ( pos.piece_on(SQ_A1) == W_BISHOP
&& pos.piece_on(SQ_B2) == W_PAWN)
- correction += !pos.empty(SQ_B3) ? penalty1
- : pos.piece_on(SQ_C3) == W_PAWN ? penalty2
- : penalty3;
+ correction += !pos.empty(SQ_B3) ? -CorneredBishop * 4
+ : -CorneredBishop * 3;
if ( pos.piece_on(SQ_H1) == W_BISHOP
&& pos.piece_on(SQ_G2) == W_PAWN)
- correction += !pos.empty(SQ_G3) ? penalty1
- : pos.piece_on(SQ_F3) == W_PAWN ? penalty2
- : penalty3;
+ correction += !pos.empty(SQ_G3) ? -CorneredBishop * 4
+ : -CorneredBishop * 3;
if ( pos.piece_on(SQ_A8) == B_BISHOP
&& pos.piece_on(SQ_B7) == B_PAWN)
- correction += !pos.empty(SQ_B6) ? -penalty1
- : pos.piece_on(SQ_C6) == B_PAWN ? -penalty2
- : -penalty3;
+ correction += !pos.empty(SQ_B6) ? CorneredBishop * 4
+ : CorneredBishop * 3;
if ( pos.piece_on(SQ_H8) == B_BISHOP
&& pos.piece_on(SQ_G7) == B_PAWN)
- correction += !pos.empty(SQ_G6) ? -penalty1
- : pos.piece_on(SQ_F6) == B_PAWN ? -penalty2
- : -penalty3;
+ correction += !pos.empty(SQ_G6) ? CorneredBishop * 4
+ : CorneredBishop * 3;
return pos.side_to_move() == WHITE ? Value(correction)
: -Value(correction);
// Scale and shift NNUE for compatibility with search and classical evaluation
auto adjusted_NNUE = [&]()
{
- int material = pos.non_pawn_material() + 2 * PawnValueMg * pos.count<PAWN>();
- int scale = 641
+ int material = pos.non_pawn_material() + 4 * PawnValueMg * pos.count<PAWN>();
+ int scale = 580
+ material / 32
- 4 * pos.rule50_count();
- Value nnue = NNUE::evaluate(pos) * scale / 1024 + Tempo;
+ Value nnue = NNUE::evaluate(pos) * scale / 1024 + Time.tempoNNUE;
if (pos.is_chess960())
nnue += fix_FRC(pos);
return nnue;
};
- // If there is PSQ imbalance use classical eval, with small probability if it is small
+ // If there is PSQ imbalance we use the classical eval. We also introduce
+ // a small probability of using the classical eval when PSQ imbalance is small.
Value psq = Value(abs(eg_value(pos.psq_score())));
int r50 = 16 + pos.rule50_count();
bool largePsq = psq * 16 > (NNUEThreshold1 + pos.non_pawn_material() / 64) * r50;
bool classical = largePsq || (psq > PawnValueMg / 4 && !(pos.this_thread()->nodes & 0xB));
// Use classical evaluation for really low piece endgames.
- // The most critical case is a bishop + A/H file pawn vs naked king draw.
- bool strongClassical = pos.non_pawn_material() < 2 * RookValueMg && pos.count<PAWN>() < 2;
+ // One critical case is the draw for bishop + A/H file pawn vs naked king.
+ bool lowPieceEndgame = pos.non_pawn_material() == BishopValueMg
+ || (pos.non_pawn_material() < 2 * RookValueMg && pos.count<PAWN>() < 2);
- v = classical || strongClassical ? Evaluation<NO_TRACE>(pos).value() : adjusted_NNUE();
+ v = classical || lowPieceEndgame ? Evaluation<NO_TRACE>(pos).value()
+ : adjusted_NNUE();
// If the classical eval is small and imbalance large, use NNUE nevertheless.
- // For the case of opposite colored bishops, switch to NNUE eval with
- // small probability if the classical eval is less than the threshold.
- if ( largePsq && !strongClassical
+ // For the case of opposite colored bishops, switch to NNUE eval with small
+ // probability if the classical eval is less than the threshold.
+ if ( largePsq
+ && !lowPieceEndgame
&& ( abs(v) * 16 < NNUEThreshold2 * r50
|| ( pos.opposite_bishops()
&& abs(v) * 16 < (NNUEThreshold1 + pos.non_pawn_material() / 64) * r50