1
0
Fork 0
mirror of https://github.com/sockspls/badfish synced 2025-04-30 08:43:09 +00:00
BadFish/src/nnue/nnue_misc.cpp
cj5716 8ee9905d8b Remove PSQT-only mode
Passed STC:
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 94208 W: 24270 L: 24112 D: 45826
Ptnml(0-2): 286, 11186, 24009, 11330, 293
https://tests.stockfishchess.org/tests/view/6635ddd773559a8aa8582826

Passed LTC:
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 114960 W: 29107 L: 28982 D: 56871
Ptnml(0-2): 37, 12683, 31924, 12790, 46
https://tests.stockfishchess.org/tests/view/663604a973559a8aa85881ed

closes #5214

Bench 1653939
2024-05-05 12:36:20 +02:00

202 lines
6.4 KiB
C++

/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// Code for calculating NNUE evaluation function
#include "nnue_misc.h"
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iosfwd>
#include <iostream>
#include <sstream>
#include <string_view>
#include "../evaluate.h"
#include "../position.h"
#include "../types.h"
#include "../uci.h"
#include "network.h"
#include "nnue_accumulator.h"
namespace Stockfish::Eval::NNUE {
constexpr std::string_view PieceToChar(" PNBRQK pnbrqk");
void hint_common_parent_position(const Position& pos,
const Networks& networks,
AccumulatorCaches& caches) {
int simpleEvalAbs = std::abs(simple_eval(pos, pos.side_to_move()));
if (simpleEvalAbs > Eval::SmallNetThreshold)
networks.small.hint_common_access(pos, &caches.small);
else
networks.big.hint_common_access(pos, &caches.big);
}
namespace {
// Converts a Value into (centi)pawns and writes it in a buffer.
// The buffer must have capacity for at least 5 chars.
void format_cp_compact(Value v, char* buffer, const Position& pos) {
buffer[0] = (v < 0 ? '-' : v > 0 ? '+' : ' ');
int cp = std::abs(UCIEngine::to_cp(v, pos));
if (cp >= 10000)
{
buffer[1] = '0' + cp / 10000;
cp %= 10000;
buffer[2] = '0' + cp / 1000;
cp %= 1000;
buffer[3] = '0' + cp / 100;
buffer[4] = ' ';
}
else if (cp >= 1000)
{
buffer[1] = '0' + cp / 1000;
cp %= 1000;
buffer[2] = '0' + cp / 100;
cp %= 100;
buffer[3] = '.';
buffer[4] = '0' + cp / 10;
}
else
{
buffer[1] = '0' + cp / 100;
cp %= 100;
buffer[2] = '.';
buffer[3] = '0' + cp / 10;
cp %= 10;
buffer[4] = '0' + cp / 1;
}
}
// Converts a Value into pawns, always keeping two decimals
void format_cp_aligned_dot(Value v, std::stringstream& stream, const Position& pos) {
const double pawns = std::abs(0.01 * UCIEngine::to_cp(v, pos));
stream << (v < 0 ? '-'
: v > 0 ? '+'
: ' ')
<< std::setiosflags(std::ios::fixed) << std::setw(6) << std::setprecision(2) << pawns;
}
}
// Returns a string with the value of each piece on a board,
// and a table for (PSQT, Layers) values bucket by bucket.
std::string
trace(Position& pos, const Eval::NNUE::Networks& networks, Eval::NNUE::AccumulatorCaches& caches) {
std::stringstream ss;
char board[3 * 8 + 1][8 * 8 + 2];
std::memset(board, ' ', sizeof(board));
for (int row = 0; row < 3 * 8 + 1; ++row)
board[row][8 * 8 + 1] = '\0';
// A lambda to output one box of the board
auto writeSquare = [&board, &pos](File file, Rank rank, Piece pc, Value value) {
const int x = int(file) * 8;
const int y = (7 - int(rank)) * 3;
for (int i = 1; i < 8; ++i)
board[y][x + i] = board[y + 3][x + i] = '-';
for (int i = 1; i < 3; ++i)
board[y + i][x] = board[y + i][x + 8] = '|';
board[y][x] = board[y][x + 8] = board[y + 3][x + 8] = board[y + 3][x] = '+';
if (pc != NO_PIECE)
board[y + 1][x + 4] = PieceToChar[pc];
if (value != VALUE_NONE)
format_cp_compact(value, &board[y + 2][x + 2], pos);
};
// We estimate the value of each piece by doing a differential evaluation from
// the current base eval, simulating the removal of the piece from its square.
Value base = networks.big.evaluate(pos, &caches.big);
base = pos.side_to_move() == WHITE ? base : -base;
for (File f = FILE_A; f <= FILE_H; ++f)
for (Rank r = RANK_1; r <= RANK_8; ++r)
{
Square sq = make_square(f, r);
Piece pc = pos.piece_on(sq);
Value v = VALUE_NONE;
if (pc != NO_PIECE && type_of(pc) != KING)
{
auto st = pos.state();
pos.remove_piece(sq);
st->accumulatorBig.computed[WHITE] = st->accumulatorBig.computed[BLACK] = false;
Value eval = networks.big.evaluate(pos, &caches.big);
eval = pos.side_to_move() == WHITE ? eval : -eval;
v = base - eval;
pos.put_piece(pc, sq);
st->accumulatorBig.computed[WHITE] = st->accumulatorBig.computed[BLACK] = false;
}
writeSquare(f, r, pc, v);
}
ss << " NNUE derived piece values:\n";
for (int row = 0; row < 3 * 8 + 1; ++row)
ss << board[row] << '\n';
ss << '\n';
auto t = networks.big.trace_evaluate(pos, &caches.big);
ss << " NNUE network contributions "
<< (pos.side_to_move() == WHITE ? "(White to move)" : "(Black to move)") << std::endl
<< "+------------+------------+------------+------------+\n"
<< "| Bucket | Material | Positional | Total |\n"
<< "| | (PSQT) | (Layers) | |\n"
<< "+------------+------------+------------+------------+\n";
for (std::size_t bucket = 0; bucket < LayerStacks; ++bucket)
{
ss << "| " << bucket << " ";
ss << " | ";
format_cp_aligned_dot(t.psqt[bucket], ss, pos);
ss << " "
<< " | ";
format_cp_aligned_dot(t.positional[bucket], ss, pos);
ss << " "
<< " | ";
format_cp_aligned_dot(t.psqt[bucket] + t.positional[bucket], ss, pos);
ss << " "
<< " |";
if (bucket == t.correctBucket)
ss << " <-- this bucket is used";
ss << '\n';
}
ss << "+------------+------------+------------+------------+\n";
return ss.str();
}
} // namespace Stockfish::Eval::NNUE