1
0
Fork 0
mirror of https://github.com/sockspls/badfish synced 2025-07-11 19:49:14 +00:00

Add evaluation tracing code

This patch is based on Justin Blanchard's original
work and allows to breakdown evaluation in its sub terms and
show to the user.

Tracing code has zero speed impact when not used.

Note that tracing code is not thread-safe, but this
should not be a problem given the typical usage scenario.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
This commit is contained in:
Marco Costalba 2011-02-26 14:09:58 +01:00
parent dd718d92a7
commit bd33766da0
3 changed files with 150 additions and 23 deletions

View file

@ -23,6 +23,9 @@
//// ////
#include <cassert> #include <cassert>
#include <iostream>
#include <iomanip>
#include <sstream>
#include "bitcount.h" #include "bitcount.h"
#include "evaluate.h" #include "evaluate.h"
@ -219,22 +222,32 @@ namespace {
// weighted scores, indexed by color and by a calculated integer number. // weighted scores, indexed by color and by a calculated integer number.
Score KingDangerTable[2][128]; Score KingDangerTable[2][128];
// TracedTerms[Color][PieceType || TracedType] contains a breakdown of the
// evaluation terms, used when tracing.
Score TracedTerms[2][16];
std::stringstream TraceStream;
enum TracedType {
PST = 8, IMBALANCE = 9, MOBILITY = 10, THREAT = 11,
PASSED = 12, UNSTOPPABLE = 13, SPACE = 14, TOTAL = 15
};
// Pawn and material hash tables, indexed by the current thread id. // Pawn and material hash tables, indexed by the current thread id.
// Note that they will be initialized at 0 being global variables. // Note that they will be initialized at 0 being global variables.
MaterialInfoTable* MaterialTable[MAX_THREADS]; MaterialInfoTable* MaterialTable[MAX_THREADS];
PawnInfoTable* PawnTable[MAX_THREADS]; PawnInfoTable* PawnTable[MAX_THREADS];
// Function prototypes // Function prototypes
template<bool HasPopCnt> template<bool HasPopCnt, bool Trace>
Value do_evaluate(const Position& pos, Value& margin); Value do_evaluate(const Position& pos, Value& margin);
template<Color Us, bool HasPopCnt> template<Color Us, bool HasPopCnt>
void init_eval_info(const Position& pos, EvalInfo& ei); void init_eval_info(const Position& pos, EvalInfo& ei);
template<Color Us, bool HasPopCnt> template<Color Us, bool HasPopCnt, bool Trace>
Score evaluate_pieces_of_color(const Position& pos, EvalInfo& ei, Score& mobility); Score evaluate_pieces_of_color(const Position& pos, EvalInfo& ei, Score& mobility);
template<Color Us, bool HasPopCnt> template<Color Us, bool HasPopCnt, bool Trace>
Score evaluate_king(const Position& pos, EvalInfo& ei, Value margins[]); Score evaluate_king(const Position& pos, EvalInfo& ei, Value margins[]);
template<Color Us> template<Color Us>
@ -274,13 +287,21 @@ void prefetchPawn(Key key, int threadID) {
/// between them based on the remaining material. /// between them based on the remaining material.
Value evaluate(const Position& pos, Value& margin) { Value evaluate(const Position& pos, Value& margin) {
return CpuHasPOPCNT ? do_evaluate<true>(pos, margin) return CpuHasPOPCNT ? do_evaluate<true, false>(pos, margin)
: do_evaluate<false>(pos, margin); : do_evaluate<false, false>(pos, margin);
} }
namespace { namespace {
template<bool HasPopCnt> double to_cp(Value v) { return double(v) / double(PawnValueMidgame); }
void trace_add(int idx, Score term_w, Score term_b = Score(0)) {
TracedTerms[WHITE][idx] = term_w;
TracedTerms[BLACK][idx] = term_b;
}
template<bool HasPopCnt, bool Trace>
Value do_evaluate(const Position& pos, Value& margin) { Value do_evaluate(const Position& pos, Value& margin) {
EvalInfo ei; EvalInfo ei;
@ -320,15 +341,15 @@ Value do_evaluate(const Position& pos, Value& margin) {
init_eval_info<BLACK, HasPopCnt>(pos, ei); init_eval_info<BLACK, HasPopCnt>(pos, ei);
// Evaluate pieces and mobility // Evaluate pieces and mobility
bonus += evaluate_pieces_of_color<WHITE, HasPopCnt>(pos, ei, mobilityWhite) bonus += evaluate_pieces_of_color<WHITE, HasPopCnt, Trace>(pos, ei, mobilityWhite)
- evaluate_pieces_of_color<BLACK, HasPopCnt>(pos, ei, mobilityBlack); - evaluate_pieces_of_color<BLACK, HasPopCnt, Trace>(pos, ei, mobilityBlack);
bonus += apply_weight(mobilityWhite - mobilityBlack, Weights[Mobility]); bonus += apply_weight(mobilityWhite - mobilityBlack, Weights[Mobility]);
// Evaluate kings after all other pieces because we need complete attack // Evaluate kings after all other pieces because we need complete attack
// information when computing the king safety evaluation. // information when computing the king safety evaluation.
bonus += evaluate_king<WHITE, HasPopCnt>(pos, ei, margins) bonus += evaluate_king<WHITE, HasPopCnt, Trace>(pos, ei, margins)
- evaluate_king<BLACK, HasPopCnt>(pos, ei, margins); - evaluate_king<BLACK, HasPopCnt, Trace>(pos, ei, margins);
// Evaluate tactical threats, we need full attack information including king // Evaluate tactical threats, we need full attack information including king
bonus += evaluate_threats<WHITE>(pos, ei) bonus += evaluate_threats<WHITE>(pos, ei)
@ -340,13 +361,23 @@ Value do_evaluate(const Position& pos, Value& margin) {
// If one side has only a king, check whether exists any unstoppable passed pawn // If one side has only a king, check whether exists any unstoppable passed pawn
if (!pos.non_pawn_material(WHITE) || !pos.non_pawn_material(BLACK)) if (!pos.non_pawn_material(WHITE) || !pos.non_pawn_material(BLACK))
{
bonus += evaluate_unstoppable_pawns<HasPopCnt>(pos, ei); bonus += evaluate_unstoppable_pawns<HasPopCnt>(pos, ei);
if (Trace)
trace_add(UNSTOPPABLE, evaluate_unstoppable_pawns<HasPopCnt>(pos, ei));
}
// Evaluate space for both sides, only in middle-game. // Evaluate space for both sides, only in middle-game.
if (mi->space_weight()) if (mi->space_weight())
{ {
int s = evaluate_space<WHITE, HasPopCnt>(pos, ei) - evaluate_space<BLACK, HasPopCnt>(pos, ei); int s_w = evaluate_space<WHITE, HasPopCnt>(pos, ei);
bonus += apply_weight(make_score(s * mi->space_weight(), 0), Weights[Space]); int s_b = evaluate_space<BLACK, HasPopCnt>(pos, ei);
bonus += apply_weight(make_score((s_w - s_b) * mi->space_weight(), 0), Weights[Space]);
if (Trace)
trace_add(SPACE, apply_weight(make_score(s_w * mi->space_weight(), make_score(0, 0)), Weights[Space]),
apply_weight(make_score(s_b * mi->space_weight(), make_score(0, 0)), Weights[Space]));
} }
// Scale winning side if position is more drawish that what it appears // Scale winning side if position is more drawish that what it appears
@ -378,6 +409,25 @@ Value do_evaluate(const Position& pos, Value& margin) {
// Interpolate between the middle game and the endgame score // Interpolate between the middle game and the endgame score
margin = margins[pos.side_to_move()]; margin = margins[pos.side_to_move()];
Value v = scale_by_game_phase(bonus, phase, sf); Value v = scale_by_game_phase(bonus, phase, sf);
if (Trace)
{
trace_add(PST, pos.value());
trace_add(IMBALANCE, mi->material_value());
trace_add(PAWN, apply_weight(ei.pi->pawns_value(), Weights[PawnStructure]));
trace_add(MOBILITY, apply_weight(mobilityWhite, Weights[Mobility]), apply_weight(mobilityBlack, Weights[Mobility]));
trace_add(THREAT, evaluate_threats<WHITE>(pos, ei), evaluate_threats<BLACK>(pos, ei));
trace_add(PASSED, evaluate_passed_pawns<WHITE>(pos, ei), evaluate_passed_pawns<BLACK>(pos, ei));
trace_add(TOTAL, bonus);
TraceStream << "\nUncertainty margin: White: " << to_cp(margins[WHITE])
<< ", Black: " << to_cp(margins[BLACK])
<< "\nScaling: " << std::noshowpos
<< std::setw(6) << 100.0 * phase/128.0 << "% MG, "
<< std::setw(6) << 100.0 * (1.0 - phase/128.0) << "% * "
<< std::setw(6) << (100.0 * sf) / SCALE_FACTOR_NORMAL << "% EG.\n"
<< "Total evaluation: " << to_cp(v);
}
return pos.side_to_move() == WHITE ? v : -v; return pos.side_to_move() == WHITE ? v : -v;
} }
@ -497,7 +547,7 @@ namespace {
// evaluate_pieces<>() assigns bonuses and penalties to the pieces of a given color // evaluate_pieces<>() assigns bonuses and penalties to the pieces of a given color
template<PieceType Piece, Color Us, bool HasPopCnt> template<PieceType Piece, Color Us, bool HasPopCnt, bool Trace>
Score evaluate_pieces(const Position& pos, EvalInfo& ei, Score& mobility, Bitboard mobilityArea) { Score evaluate_pieces(const Position& pos, EvalInfo& ei, Score& mobility, Bitboard mobilityArea) {
Bitboard b; Bitboard b;
@ -622,6 +672,10 @@ namespace {
} }
} }
} }
if (Trace)
TracedTerms[Us][Piece] = bonus;
return bonus; return bonus;
} }
@ -662,7 +716,7 @@ namespace {
// evaluate_pieces_of_color<>() assigns bonuses and penalties to all the // evaluate_pieces_of_color<>() assigns bonuses and penalties to all the
// pieces of a given color. // pieces of a given color.
template<Color Us, bool HasPopCnt> template<Color Us, bool HasPopCnt, bool Trace>
Score evaluate_pieces_of_color(const Position& pos, EvalInfo& ei, Score& mobility) { Score evaluate_pieces_of_color(const Position& pos, EvalInfo& ei, Score& mobility) {
const Color Them = (Us == WHITE ? BLACK : WHITE); const Color Them = (Us == WHITE ? BLACK : WHITE);
@ -672,10 +726,10 @@ namespace {
// Do not include in mobility squares protected by enemy pawns or occupied by our pieces // Do not include in mobility squares protected by enemy pawns or occupied by our pieces
const Bitboard mobilityArea = ~(ei.attackedBy[Them][PAWN] | pos.pieces_of_color(Us)); const Bitboard mobilityArea = ~(ei.attackedBy[Them][PAWN] | pos.pieces_of_color(Us));
bonus += evaluate_pieces<KNIGHT, Us, HasPopCnt>(pos, ei, mobility, mobilityArea); bonus += evaluate_pieces<KNIGHT, Us, HasPopCnt, Trace>(pos, ei, mobility, mobilityArea);
bonus += evaluate_pieces<BISHOP, Us, HasPopCnt>(pos, ei, mobility, mobilityArea); bonus += evaluate_pieces<BISHOP, Us, HasPopCnt, Trace>(pos, ei, mobility, mobilityArea);
bonus += evaluate_pieces<ROOK, Us, HasPopCnt>(pos, ei, mobility, mobilityArea); bonus += evaluate_pieces<ROOK, Us, HasPopCnt, Trace>(pos, ei, mobility, mobilityArea);
bonus += evaluate_pieces<QUEEN, Us, HasPopCnt>(pos, ei, mobility, mobilityArea); bonus += evaluate_pieces<QUEEN, Us, HasPopCnt, Trace>(pos, ei, mobility, mobilityArea);
// Sum up all attacked squares // Sum up all attacked squares
ei.attackedBy[Us][0] = ei.attackedBy[Us][PAWN] | ei.attackedBy[Us][KNIGHT] ei.attackedBy[Us][0] = ei.attackedBy[Us][PAWN] | ei.attackedBy[Us][KNIGHT]
@ -687,7 +741,7 @@ namespace {
// evaluate_king<>() assigns bonuses and penalties to a king of a given color // evaluate_king<>() assigns bonuses and penalties to a king of a given color
template<Color Us, bool HasPopCnt> template<Color Us, bool HasPopCnt, bool Trace>
Score evaluate_king(const Position& pos, EvalInfo& ei, Value margins[]) { Score evaluate_king(const Position& pos, EvalInfo& ei, Value margins[]) {
const BitCountType Max15 = HasPopCnt ? CNT_POPCNT : CpuIs64Bit ? CNT64_MAX15 : CNT32_MAX15; const BitCountType Max15 = HasPopCnt ? CNT_POPCNT : CpuIs64Bit ? CNT64_MAX15 : CNT32_MAX15;
@ -791,6 +845,10 @@ namespace {
bonus -= KingDangerTable[Us][attackUnits]; bonus -= KingDangerTable[Us][attackUnits];
margins[Us] += mg_value(KingDangerTable[Us][attackUnits]); margins[Us] += mg_value(KingDangerTable[Us][attackUnits]);
} }
if (Trace)
TracedTerms[Us][KING] = bonus;
return bonus; return bonus;
} }
@ -1147,4 +1205,75 @@ namespace {
for (int i = 0; i < 100; i++) for (int i = 0; i < 100; i++)
KingDangerTable[c][i] = apply_weight(make_score(t[i], 0), Weights[KingDangerUs + c]); KingDangerTable[c][i] = apply_weight(make_score(t[i], 0), Weights[KingDangerUs + c]);
} }
// trace_row() is an helper function used by tracing code to register the
// values of a single evaluation term.
void trace_row(const char *name, int idx) {
Score term_w = TracedTerms[WHITE][idx];
Score term_b = TracedTerms[BLACK][idx];
switch (idx) {
case PST: case IMBALANCE: case PAWN: case UNSTOPPABLE: case TOTAL:
TraceStream << std::setw(20) << name << " | --- --- | --- --- | "
<< std::setw(6) << to_cp(mg_value(term_w)) << " "
<< std::setw(6) << to_cp(eg_value(term_w)) << " \n";
break;
default:
TraceStream << std::setw(20) << name << " | " << std::noshowpos
<< std::setw(5) << to_cp(mg_value(term_w)) << " "
<< std::setw(5) << to_cp(eg_value(term_w)) << " | "
<< std::setw(5) << to_cp(mg_value(term_b)) << " "
<< std::setw(5) << to_cp(eg_value(term_b)) << " | "
<< std::showpos
<< std::setw(6) << to_cp(mg_value(term_w - term_b)) << " "
<< std::setw(6) << to_cp(eg_value(term_w - term_b)) << " \n";
}
}
}
/// trace_evaluate() is like evaluate() but instead of a value returns a string
/// suitable to be print on stdout with the detailed descriptions and values of
/// each evaluation term. Used mainly for debugging.
std::string trace_evaluate(const Position& pos) {
Value margin;
std::string totals;
TraceStream.str("");
TraceStream << std::showpoint << std::showpos << std::fixed << std::setprecision(2);
memset(TracedTerms, 0, 2 * 16 * sizeof(Score));
do_evaluate<false, true>(pos, margin);
totals = TraceStream.str();
TraceStream.str("");
TraceStream << std::setw(21) << "Eval term " << "| White | Black | Total \n"
<< " | MG EG | MG EG | MG EG \n"
<< "---------------------+-------------+-------------+---------------\n";
trace_row("Material, PST, Tempo", PST);
trace_row("Material imbalance", IMBALANCE);
trace_row("Pawns", PAWN);
trace_row("Knights", KNIGHT);
trace_row("Bishops", BISHOP);
trace_row("Rooks", ROOK);
trace_row("Queens", QUEEN);
trace_row("Mobility", MOBILITY);
trace_row("King safety", KING);
trace_row("Threats", THREAT);
trace_row("Passed pawns", PASSED);
trace_row("Unstoppable pawns", UNSTOPPABLE);
trace_row("Space", SPACE);
TraceStream << "---------------------+-------------+-------------+---------------\n";
trace_row("Total", TOTAL);
TraceStream << totals;
return TraceStream.str();
} }

View file

@ -25,6 +25,7 @@
class Position; class Position;
extern Value evaluate(const Position& pos, Value& margin); extern Value evaluate(const Position& pos, Value& margin);
extern std::string trace_evaluate(const Position& pos);
extern void init_eval(int threads); extern void init_eval(int threads);
extern void quit_eval(); extern void quit_eval();
extern void read_evaluation_uci_options(Color sideToMove); extern void read_evaluation_uci_options(Color sideToMove);

View file

@ -65,7 +65,6 @@ bool execute_uci_command(const string& cmd) {
static Position pos(StartPositionFEN, false, 0); // The root position static Position pos(StartPositionFEN, false, 0); // The root position
UCIParser up(cmd); UCIParser up(cmd);
Value dummy;
string token; string token;
up >> token; // operator>>() skips any whitespace up >> token; // operator>>() skips any whitespace
@ -98,9 +97,7 @@ bool execute_uci_command(const string& cmd) {
pos.print(); pos.print();
else if (token == "eval") else if (token == "eval")
cout << "Incremental mg: " << mg_value(pos.value()) cout << trace_evaluate(pos) << endl;
<< "\nIncremental eg: " << eg_value(pos.value())
<< "\nFull eval: " << evaluate(pos, dummy) << endl;
else if (token == "key") else if (token == "key")
cout << "key: " << hex << pos.get_key() cout << "key: " << hex << pos.get_key()