From: Marco Costalba Date: Sat, 26 Feb 2011 13:09:58 +0000 (+0100) Subject: Add evaluation tracing code X-Git-Url: https://git.sesse.net/?p=stockfish;a=commitdiff_plain;h=bd33766da0a00d9df69afcbf31a75d5f9972f1c8 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 --- diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 53afc447..e9feba06 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -23,6 +23,9 @@ //// #include +#include +#include +#include #include "bitcount.h" #include "evaluate.h" @@ -219,22 +222,32 @@ namespace { // weighted scores, indexed by color and by a calculated integer number. 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. // Note that they will be initialized at 0 being global variables. MaterialInfoTable* MaterialTable[MAX_THREADS]; PawnInfoTable* PawnTable[MAX_THREADS]; // Function prototypes - template + template Value do_evaluate(const Position& pos, Value& margin); template void init_eval_info(const Position& pos, EvalInfo& ei); - template + template Score evaluate_pieces_of_color(const Position& pos, EvalInfo& ei, Score& mobility); - template + template Score evaluate_king(const Position& pos, EvalInfo& ei, Value margins[]); template @@ -274,13 +287,21 @@ void prefetchPawn(Key key, int threadID) { /// between them based on the remaining material. Value evaluate(const Position& pos, Value& margin) { - return CpuHasPOPCNT ? do_evaluate(pos, margin) - : do_evaluate(pos, margin); + return CpuHasPOPCNT ? do_evaluate(pos, margin) + : do_evaluate(pos, margin); } namespace { -template +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 Value do_evaluate(const Position& pos, Value& margin) { EvalInfo ei; @@ -320,15 +341,15 @@ Value do_evaluate(const Position& pos, Value& margin) { init_eval_info(pos, ei); // Evaluate pieces and mobility - bonus += evaluate_pieces_of_color(pos, ei, mobilityWhite) - - evaluate_pieces_of_color(pos, ei, mobilityBlack); + bonus += evaluate_pieces_of_color(pos, ei, mobilityWhite) + - evaluate_pieces_of_color(pos, ei, mobilityBlack); bonus += apply_weight(mobilityWhite - mobilityBlack, Weights[Mobility]); // Evaluate kings after all other pieces because we need complete attack // information when computing the king safety evaluation. - bonus += evaluate_king(pos, ei, margins) - - evaluate_king(pos, ei, margins); + bonus += evaluate_king(pos, ei, margins) + - evaluate_king(pos, ei, margins); // Evaluate tactical threats, we need full attack information including king bonus += evaluate_threats(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 (!pos.non_pawn_material(WHITE) || !pos.non_pawn_material(BLACK)) + { bonus += evaluate_unstoppable_pawns(pos, ei); + if (Trace) + trace_add(UNSTOPPABLE, evaluate_unstoppable_pawns(pos, ei)); + } + // Evaluate space for both sides, only in middle-game. if (mi->space_weight()) { - int s = evaluate_space(pos, ei) - evaluate_space(pos, ei); - bonus += apply_weight(make_score(s * mi->space_weight(), 0), Weights[Space]); + int s_w = evaluate_space(pos, ei); + int s_b = evaluate_space(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 @@ -378,6 +409,25 @@ Value do_evaluate(const Position& pos, Value& margin) { // Interpolate between the middle game and the endgame score margin = margins[pos.side_to_move()]; 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(pos, ei), evaluate_threats(pos, ei)); + trace_add(PASSED, evaluate_passed_pawns(pos, ei), evaluate_passed_pawns(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; } @@ -497,7 +547,7 @@ namespace { // evaluate_pieces<>() assigns bonuses and penalties to the pieces of a given color - template + template Score evaluate_pieces(const Position& pos, EvalInfo& ei, Score& mobility, Bitboard mobilityArea) { Bitboard b; @@ -622,6 +672,10 @@ namespace { } } } + + if (Trace) + TracedTerms[Us][Piece] = bonus; + return bonus; } @@ -662,7 +716,7 @@ namespace { // evaluate_pieces_of_color<>() assigns bonuses and penalties to all the // pieces of a given color. - template + template Score evaluate_pieces_of_color(const Position& pos, EvalInfo& ei, Score& mobility) { 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 const Bitboard mobilityArea = ~(ei.attackedBy[Them][PAWN] | pos.pieces_of_color(Us)); - bonus += evaluate_pieces(pos, ei, mobility, mobilityArea); - bonus += evaluate_pieces(pos, ei, mobility, mobilityArea); - bonus += evaluate_pieces(pos, ei, mobility, mobilityArea); - bonus += evaluate_pieces(pos, ei, mobility, mobilityArea); + bonus += evaluate_pieces(pos, ei, mobility, mobilityArea); + bonus += evaluate_pieces(pos, ei, mobility, mobilityArea); + bonus += evaluate_pieces(pos, ei, mobility, mobilityArea); + bonus += evaluate_pieces(pos, ei, mobility, mobilityArea); // Sum up all attacked squares 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 - template + template Score evaluate_king(const Position& pos, EvalInfo& ei, Value margins[]) { const BitCountType Max15 = HasPopCnt ? CNT_POPCNT : CpuIs64Bit ? CNT64_MAX15 : CNT32_MAX15; @@ -791,6 +845,10 @@ namespace { bonus -= KingDangerTable[Us][attackUnits]; margins[Us] += mg_value(KingDangerTable[Us][attackUnits]); } + + if (Trace) + TracedTerms[Us][KING] = bonus; + return bonus; } @@ -1147,4 +1205,75 @@ namespace { for (int i = 0; i < 100; i++) 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(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(); } diff --git a/src/evaluate.h b/src/evaluate.h index 4198ca56..18f4ab0a 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -25,6 +25,7 @@ class Position; extern Value evaluate(const Position& pos, Value& margin); +extern std::string trace_evaluate(const Position& pos); extern void init_eval(int threads); extern void quit_eval(); extern void read_evaluation_uci_options(Color sideToMove); diff --git a/src/uci.cpp b/src/uci.cpp index d15b0866..bc412012 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -65,7 +65,6 @@ bool execute_uci_command(const string& cmd) { static Position pos(StartPositionFEN, false, 0); // The root position UCIParser up(cmd); - Value dummy; string token; up >> token; // operator>>() skips any whitespace @@ -98,9 +97,7 @@ bool execute_uci_command(const string& cmd) { pos.print(); else if (token == "eval") - cout << "Incremental mg: " << mg_value(pos.value()) - << "\nIncremental eg: " << eg_value(pos.value()) - << "\nFull eval: " << evaluate(pos, dummy) << endl; + cout << trace_evaluate(pos) << endl; else if (token == "key") cout << "key: " << hex << pos.get_key()