X-Git-Url: https://git.sesse.net/?p=stockfish;a=blobdiff_plain;f=src%2Fevaluate.cpp;h=b47570e86303863cc968e632ca49b15ca9e175b0;hp=29d71ea9e0cce28078ccf4a715521d85b3f6bbac;hb=0a1f54975fe3cb59a5274cbfee51f5b53e64071b;hpb=9e8bf82350002b826e40581100dbbcd1d65c796e diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 29d71ea9..b47570e8 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -17,17 +17,16 @@ along with this program. If not, see . */ +#include #include #include #include -#include #include "bitcount.h" #include "evaluate.h" #include "material.h" #include "pawns.h" #include "thread.h" -#include "ucioption.h" namespace { @@ -58,9 +57,8 @@ namespace { // kingAttackersWeight[color] is the sum of the "weight" of the pieces of the // given color which attack a square in the kingRing of the enemy king. The - // weights of the individual piece types are given by the variables - // QueenAttackWeight, RookAttackWeight, BishopAttackWeight and - // KnightAttackWeight in evaluate.cpp + // weights of the individual piece types are given by the elements in the + // KingAttackWeights array. int kingAttackersWeight[COLOR_NB]; // kingAdjacentZoneAttacksCount[color] is the number of attacks to squares @@ -76,36 +74,29 @@ namespace { namespace Tracing { enum Terms { // First 8 entries are for PieceType - PST = 8, IMBALANCE, MOBILITY, THREAT, PASSED, SPACE, TOTAL, TERMS_NB + MATERIAL = 8, IMBALANCE, MOBILITY, THREAT, PASSED, SPACE, TOTAL, TERMS_NB }; - Score terms[COLOR_NB][TERMS_NB]; + Score scores[COLOR_NB][TERMS_NB]; EvalInfo ei; ScaleFactor sf; double to_cp(Value v); - void add_term(int idx, Score term_w, Score term_b = SCORE_ZERO); - void format_row(std::stringstream& ss, const char* name, int idx); + void write(int idx, Color c, Score s); + void write(int idx, Score w, Score b = SCORE_ZERO); + void print(std::stringstream& ss, const char* name, int idx); std::string do_trace(const Position& pos); } - // Evaluation weights, initialized from UCI options - enum { Mobility, PawnStructure, PassedPawns, Space, KingDangerUs, KingDangerThem }; - struct Weight { int mg, eg; } Weights[6]; + // Evaluation weights, indexed by evaluation term + enum { Mobility, PawnStructure, PassedPawns, Space, KingSafety }; + const struct Weight { int mg, eg; } Weights[] = { + {289, 344}, {233, 201}, {221, 273}, {46, 0}, {318, 0} + }; typedef Value V; #define S(mg, eg) make_score(mg, eg) - // Internal evaluation weights. These are applied on top of the evaluation - // weights read from UCI parameters. The purpose is to be able to change - // the evaluation weights while keeping the default values of the UCI - // parameters at 100, which looks prettier. - // - // Values modified by Joona Kiiski - const Score WeightsInternal[] = { - S(289, 344), S(233, 201), S(221, 273), S(46, 0), S(271, 0), S(307, 0) - }; - // MobilityBonus[PieceType][attacked] contains bonuses for middle and end // game, indexed by piece type and number of attacked squares not occupied by // friendly pieces. @@ -145,38 +136,39 @@ namespace { V(0), V(5), V(8), V(8), V(8), V(8), V(5), V(0) } }; - // Threat[attacking][attacked] contains bonuses according to which piece - // type attacks which one. - const Score Threat[][PIECE_TYPE_NB] = { - { S(0, 0), S( 7, 39), S(24, 49), S(24, 49), S(41,100), S(41,100) }, // Minor - { S(0, 0), S(15, 39), S(15, 45), S(15, 45), S(15, 45), S(24, 49) } // Major + // Threat[defended/weak][minor/major attacking][attacked PieceType] contains + // bonuses according to which piece type attacks which one. + const Score Threat[][2][PIECE_TYPE_NB] = { + { { S(0, 0), S( 0, 0), S(19, 37), S(24, 37), S(44, 97), S(35,106) }, // Defended Minor + { S(0, 0), S( 0, 0), S( 9, 14), S( 9, 14), S( 7, 14), S(24, 48) } }, // Defended Major + { { S(0, 0), S( 0,32), S(33, 41), S(31, 50), S(41,100), S(35,104) }, // Weak Minor + { S(0, 0), S( 0,27), S(26, 57), S(26, 57), S(0 , 43), S(23, 51) } } // Weak Major }; // ThreatenedByPawn[PieceType] contains a penalty according to which piece // type is attacked by an enemy pawn. const Score ThreatenedByPawn[] = { - S(0, 0), S(0, 0), S(56, 70), S(56, 70), S(76, 99), S(86, 118) + S(0, 0), S(0, 0), S(87, 118), S(84, 122), S(114, 203), S(121, 217) }; - // Hanging[side to move] contains a bonus for each enemy hanging piece - const Score Hanging[2] = { S(23, 20) , S(35, 45) }; - - #undef S - - const Score Tempo = make_score(24, 11); - const Score RookOnPawn = make_score(10, 28); - const Score RookOpenFile = make_score(43, 21); - const Score RookSemiopenFile = make_score(19, 10); - const Score BishopPawns = make_score( 8, 12); - const Score MinorBehindPawn = make_score(16, 0); - const Score UndefendedMinor = make_score(25, 10); - const Score TrappedRook = make_score(90, 0); - const Score Unstoppable = make_score( 0, 20); + // Assorted bonuses and penalties used by evaluation + const Score KingOnOne = S( 2, 58); + const Score KingOnMany = S( 6,125); + const Score RookOnPawn = S( 7, 27); + const Score RookOpenFile = S(43, 21); + const Score RookSemiOpenFile = S(19, 10); + const Score BishopPawns = S( 8, 12); + const Score MinorBehindPawn = S(16, 0); + const Score TrappedRook = S(92, 0); + const Score Unstoppable = S( 0, 20); + const Score Hanging = S(31, 26); // Penalty for a bishop on a1/h1 (a8/h8 for black) which is trapped by // a friendly pawn on b2/g2 (b7/g7 for black). This can obviously only // happen in Chess960 games. - const Score TrappedBishopA1H1 = make_score(50, 50); + const Score TrappedBishopA1H1 = S(50, 50); + + #undef S // SpaceMask[Color] contains the area of the board which is considered // by the space evaluation. In the middlegame, each side is given a bonus @@ -203,10 +195,9 @@ namespace { const int BishopCheck = 2; const int KnightCheck = 3; - // KingDanger[Color][attackUnits] contains the actual king danger weighted - // scores, indexed by color and by a calculated integer number. - Score KingDanger[COLOR_NB][128]; - + // KingDanger[attackUnits] contains the actual king danger weighted + // scores, indexed by a calculated integer number. + Score KingDanger[128]; // apply_weight() weighs score 'v' by weight 'w' trying to prevent overflow Score apply_weight(Score v, const Weight& w) { @@ -214,17 +205,6 @@ namespace { } - // weight_option() computes the value of an evaluation weight, by combining - // two UCI-configurable weights (midgame and endgame) with an internal weight. - - Weight weight_option(const std::string& mgOpt, const std::string& egOpt, Score internalWeight) { - - Weight w = { Options[mgOpt] * mg_value(internalWeight) / 100, - Options[egOpt] * eg_value(internalWeight) / 100 }; - return w; - } - - // init_eval_info() initializes king bitboards for given color adding // pawn attacks. To be done at the beginning of the evaluation. @@ -240,7 +220,7 @@ namespace { ei.attackedBy[Us][ALL_PIECES] = ei.attackedBy[Us][PAWN] = ei.pi->pawn_attacks(Us); // Init king safety tables only if we are going to use them - if (pos.count(Us) && pos.non_pawn_material(Us) > QueenValueMg + PawnValueMg) + if (pos.non_pawn_material(Us) >= QueenValueMg) { ei.kingRing[Them] = b | shift_bb(b); b &= ei.attackedBy[Us][PAWN]; @@ -252,10 +232,10 @@ namespace { } - // evaluate_outposts() evaluates bishop and knight outpost squares + // evaluate_outpost() evaluates bishop and knight outpost squares template - Score evaluate_outposts(const Position& pos, EvalInfo& ei, Square s) { + Score evaluate_outpost(const Position& pos, const EvalInfo& ei, Square s) { const Color Them = (Us == WHITE ? BLACK : WHITE); @@ -275,7 +255,7 @@ namespace { bonus += bonus / 2; } - return make_score(bonus, bonus); + return make_score(bonus * 2, bonus / 2); } @@ -336,9 +316,9 @@ namespace { if (Pt == BISHOP) score -= BishopPawns * ei.pi->pawns_on_same_color_squares(Us, s); - // Bishop and knight outposts squares + // Bishop and knight outpost square if (!(pos.pieces(Them, PAWN) & pawn_attack_span(Us, s))) - score += evaluate_outposts(pos, ei, s); + score += evaluate_outpost(pos, ei, s); // Bishop or knight behind a pawn if ( relative_rank(Us, s) < RANK_5 @@ -358,7 +338,7 @@ namespace { // Give a bonus for a rook on a open or semi-open file if (ei.pi->semiopen_file(Us, file_of(s))) - score += ei.pi->semiopen_file(Them, file_of(s)) ? RookOpenFile : RookSemiopenFile; + score += ei.pi->semiopen_file(Them, file_of(s)) ? RookOpenFile : RookSemiOpenFile; if (mob > 3 || ei.pi->semiopen_file(Us, file_of(s))) continue; @@ -370,7 +350,7 @@ namespace { if ( ((file_of(ksq) < FILE_E) == (file_of(s) < file_of(ksq))) && (rank_of(ksq) == rank_of(s) || relative_rank(Us, ksq) == RANK_1) && !ei.pi->semiopen_side(Us, file_of(ksq), file_of(s) < file_of(ksq))) - score -= (TrappedRook - make_score(mob * 8, 0)) * (1 + !pos.can_castle(Us)); + score -= (TrappedRook - make_score(mob * 22, 0)) * (1 + !pos.can_castle(Us)); } // An important Chess960 pattern: A cornered bishop blocked by a friendly @@ -389,7 +369,7 @@ namespace { } if (Trace) - Tracing::terms[Us][Pt] = score; + Tracing::write(Pt, Us, score); return score - evaluate_pieces(pos, ei, mobility, mobilityArea); } @@ -433,7 +413,8 @@ namespace { attackUnits = std::min(20, (ei.kingAttackersCount[Them] * ei.kingAttackersWeight[Them]) / 2) + 3 * (ei.kingAdjacentZoneAttacksCount[Them] + popcount(undefended)) + 2 * (ei.pinnedPieces[Us] != 0) - - mg_value(score) / 32; + - mg_value(score) / 32 + - !pos.count(Them) * 15; // Analyse the enemy's safe queen contact checks. Firstly, find the // undefended squares around the king that are attacked by the enemy's @@ -446,9 +427,7 @@ namespace { | ei.attackedBy[Them][BISHOP] | ei.attackedBy[Them][ROOK]); if (b) - attackUnits += QueenContactCheck - * popcount(b) - * (Them == pos.side_to_move() ? 2 : 1); + attackUnits += QueenContactCheck * popcount(b); } // Analyse the enemy's safe rook contact checks. Firstly, find the @@ -466,9 +445,7 @@ namespace { | ei.attackedBy[Them][BISHOP] | ei.attackedBy[Them][QUEEN]); if (b) - attackUnits += RookContactCheck - * popcount(b) - * (Them == pos.side_to_move() ? 2 : 1); + attackUnits += RookContactCheck * popcount(b); } // Analyse the enemy's safe distance checks for sliders and knights @@ -502,11 +479,11 @@ namespace { // Finally, extract the king danger score from the KingDanger[] // array and subtract the score from evaluation. - score -= KingDanger[Us == Search::RootColor][attackUnits]; + score -= KingDanger[attackUnits]; } if (Trace) - Tracing::terms[Us][KING] = score; + Tracing::write(KING, Us, score); return score; } @@ -520,40 +497,56 @@ namespace { const Color Them = (Us == WHITE ? BLACK : WHITE); - Bitboard b, undefendedMinors, weakEnemies; + enum { Defended, Weak }; + enum { Minor, Major }; + + Bitboard b, weak, defended; Score score = SCORE_ZERO; - // Undefended minors get penalized even if they are not under attack - undefendedMinors = pos.pieces(Them, BISHOP, KNIGHT) - & ~ei.attackedBy[Them][ALL_PIECES]; + // Non-pawn enemies defended by a pawn and under our attack + defended = (pos.pieces(Them) ^ pos.pieces(Them, PAWN)) + & ei.attackedBy[Them][PAWN] + & (ei.attackedBy[Us][KNIGHT] | ei.attackedBy[Us][BISHOP] | ei.attackedBy[Us][ROOK]); - if (undefendedMinors) - score += UndefendedMinor; + // Add a bonus according to the kind of attacking pieces + if (defended) + { + b = defended & (ei.attackedBy[Us][KNIGHT] | ei.attackedBy[Us][BISHOP]); + while (b) + score += Threat[Defended][Minor][type_of(pos.piece_on(pop_lsb(&b)))]; + + b = defended & (ei.attackedBy[Us][ROOK]); + while (b) + score += Threat[Defended][Major][type_of(pos.piece_on(pop_lsb(&b)))]; + } // Enemies not defended by a pawn and under our attack - weakEnemies = pos.pieces(Them) - & ~ei.attackedBy[Them][PAWN] - & ei.attackedBy[Us][ALL_PIECES]; + weak = pos.pieces(Them) + & ~ei.attackedBy[Them][PAWN] + & ei.attackedBy[Us][ALL_PIECES]; - // Add a bonus according if the attacking pieces are minor or major - if (weakEnemies) + // Add a bonus according to the kind of attacking pieces + if (weak) { - b = weakEnemies & (ei.attackedBy[Us][PAWN] | ei.attackedBy[Us][KNIGHT] | ei.attackedBy[Us][BISHOP]); - if (b) - score += Threat[0][type_of(pos.piece_on(lsb(b)))]; + b = weak & (ei.attackedBy[Us][KNIGHT] | ei.attackedBy[Us][BISHOP]); + while (b) + score += Threat[Weak][Minor][type_of(pos.piece_on(pop_lsb(&b)))]; + + b = weak & (ei.attackedBy[Us][ROOK] | ei.attackedBy[Us][QUEEN]); + while (b) + score += Threat[Weak][Major][type_of(pos.piece_on(pop_lsb(&b)))]; - b = weakEnemies & (ei.attackedBy[Us][ROOK] | ei.attackedBy[Us][QUEEN]); + b = weak & ~ei.attackedBy[Them][ALL_PIECES]; if (b) - score += Threat[1][type_of(pos.piece_on(lsb(b)))]; + score += more_than_one(b) ? Hanging * popcount(b) : Hanging; - b = weakEnemies & ~ei.attackedBy[Them][ALL_PIECES]; + b = weak & ei.attackedBy[Us][KING]; if (b) - score += more_than_one(b) ? Hanging[Us != pos.side_to_move()] * popcount(b) - : Hanging[Us == pos.side_to_move()]; + score += more_than_one(b) ? KingOnMany : KingOnOne; } if (Trace) - Tracing::terms[Us][Tracing::THREAT] = score; + Tracing::write(Tracing::THREAT, Us, score); return score; } @@ -588,32 +581,28 @@ namespace { Square blockSq = s + pawn_push(Us); // Adjust bonus based on the king's proximity - ebonus += square_distance(pos.king_square(Them), blockSq) * 5 * rr - - square_distance(pos.king_square(Us ), blockSq) * 2 * rr; + ebonus += distance(pos.king_square(Them), blockSq) * 5 * rr + - distance(pos.king_square(Us ), blockSq) * 2 * rr; // If blockSq is not the queening square then consider also a second push if (relative_rank(Us, blockSq) != RANK_8) - ebonus -= square_distance(pos.king_square(Us), blockSq + pawn_push(Us)) * rr; + ebonus -= distance(pos.king_square(Us), blockSq + pawn_push(Us)) * rr; // If the pawn is free to advance, then increase the bonus if (pos.empty(blockSq)) { - squaresToQueen = forward_bb(Us, s); - - // If there is an enemy rook or queen attacking the pawn from behind, - // add all X-ray attacks by the rook or queen. Otherwise consider only - // the squares in the pawn's path attacked or occupied by the enemy. - if ( unlikely(forward_bb(Them, s) & pos.pieces(Them, ROOK, QUEEN)) - && (forward_bb(Them, s) & pos.pieces(Them, ROOK, QUEEN) & pos.attacks_from(s))) - unsafeSquares = squaresToQueen; - else - unsafeSquares = squaresToQueen & (ei.attackedBy[Them][ALL_PIECES] | pos.pieces(Them)); - - if ( unlikely(forward_bb(Them, s) & pos.pieces(Us, ROOK, QUEEN)) - && (forward_bb(Them, s) & pos.pieces(Us, ROOK, QUEEN) & pos.attacks_from(s))) - defendedSquares = squaresToQueen; - else - defendedSquares = squaresToQueen & ei.attackedBy[Us][ALL_PIECES]; + // If there is a rook or queen attacking/defending the pawn from behind, + // consider all the squaresToQueen. Otherwise consider only the squares + // in the pawn's path attacked or occupied by the enemy. + defendedSquares = unsafeSquares = squaresToQueen = forward_bb(Us, s); + + Bitboard bb = forward_bb(Them, s) & pos.pieces(ROOK, QUEEN) & pos.attacks_from(s); + + if (!(pos.pieces(Us) & bb)) + defendedSquares &= ei.attackedBy[Us][ALL_PIECES]; + + if (!(pos.pieces(Them) & bb)) + unsafeSquares &= ei.attackedBy[Them][ALL_PIECES] | pos.pieces(Them); // If there aren't any enemy attacks, assign a big bonus. Otherwise // assign a smaller bonus if the block square isn't attacked. @@ -629,6 +618,8 @@ namespace { mbonus += k * rr, ebonus += k * rr; } + else if (pos.pieces(Us) & blockSq) + mbonus += rr * 3 + r * 2 + 3, ebonus += rr + r * 2; } // rr != 0 if (pos.count(Us) < pos.count(Them)) @@ -638,25 +629,22 @@ namespace { } if (Trace) - Tracing::terms[Us][Tracing::PASSED] = apply_weight(score, Weights[PassedPawns]); + Tracing::write(Tracing::PASSED, Us, apply_weight(score, Weights[PassedPawns])); // Add the scores to the middlegame and endgame eval return apply_weight(score, Weights[PassedPawns]); } - // evaluate_unstoppable_pawns() scores the most advanced among the passed and - // candidate pawns. In case opponent has no pieces but pawns, this is somewhat - // related to the possibility that pawns are unstoppable. - - Score evaluate_unstoppable_pawns(const Position& pos, Color us, const EvalInfo& ei) { + // evaluate_unstoppable_pawns() scores the most advanced passed pawn. In case + // both players have no pieces but pawns, this is somewhat related to the + // possibility that pawns are unstoppable. - Bitboard b = ei.pi->passed_pawns(us) | ei.pi->candidate_pawns(us); + Score evaluate_unstoppable_pawns(Color us, const EvalInfo& ei) { - if (!b || pos.non_pawn_material(~us)) - return SCORE_ZERO; + Bitboard b = ei.pi->passed_pawns(us); - return Unstoppable * int(relative_rank(us, frontmost_sq(us, b))); + return b ? Unstoppable * int(relative_rank(us, frontmost_sq(us, b))) : SCORE_ZERO; } @@ -704,9 +692,9 @@ namespace { Thread* thisThread = pos.this_thread(); // Initialize score by reading the incrementally updated scores included - // in the position object (material + piece square tables) and adding a - // Tempo bonus. Score is computed from the point of view of white. - score = pos.psq_score() + (pos.side_to_move() == WHITE ? Tempo : -Tempo); + // in the position object (material + piece square tables). + // Score is computed from the point of view of white. + score = pos.psq_score(); // Probe the material hash table ei.mi = Material::probe(pos, thisThread->materialTable, thisThread->endgames); @@ -715,7 +703,7 @@ namespace { // If we have a specialized evaluation function for the current material // configuration, call it and return. if (ei.mi->specialized_eval_exists()) - return ei.mi->evaluate(pos); + return ei.mi->evaluate(pos) + Eval::Tempo; // Probe the pawn hash table ei.pi = Pawns::probe(pos, thisThread->pawnsTable); @@ -749,10 +737,10 @@ namespace { score += evaluate_passed_pawns(pos, ei) - evaluate_passed_pawns(pos, ei); - // If one side has only a king, score for potential unstoppable pawns - if (!pos.non_pawn_material(WHITE) || !pos.non_pawn_material(BLACK)) - score += evaluate_unstoppable_pawns(pos, WHITE, ei) - - evaluate_unstoppable_pawns(pos, BLACK, ei); + // If both sides have only pawns, score for potential unstoppable pawns + if (!pos.non_pawn_material(WHITE) && !pos.non_pawn_material(BLACK)) + score += evaluate_unstoppable_pawns(WHITE, ei) + - evaluate_unstoppable_pawns(BLACK, ei); // Evaluate space for both sides, only in middlegame if (ei.mi->space_weight()) @@ -762,37 +750,35 @@ namespace { } // Scale winning side if position is more drawish than it appears - ScaleFactor sf = eg_value(score) > VALUE_DRAW ? ei.mi->scale_factor(pos, WHITE) - : ei.mi->scale_factor(pos, BLACK); + Color strongSide = eg_value(score) > VALUE_DRAW ? WHITE : BLACK; + ScaleFactor sf = ei.mi->scale_factor(pos, strongSide); - // If we don't already have an unusual scale factor, check for opposite - // colored bishop endgames, and use a lower scale for those. + // If we don't already have an unusual scale factor, check for certain + // types of endgames, and use a lower scale for those. if ( ei.mi->game_phase() < PHASE_MIDGAME - && pos.opposite_bishops() && (sf == SCALE_FACTOR_NORMAL || sf == SCALE_FACTOR_ONEPAWN)) { - // Ignoring any pawns, do both sides only have a single bishop and no - // other pieces? - if ( pos.non_pawn_material(WHITE) == BishopValueMg - && pos.non_pawn_material(BLACK) == BishopValueMg) + if (pos.opposite_bishops()) { - // Check for KBP vs KB with only a single pawn that is almost - // certainly a draw or at least two pawns. - bool one_pawn = (pos.count(WHITE) + pos.count(BLACK) == 1); - sf = one_pawn ? ScaleFactor(8) : ScaleFactor(32); - } - else + // Endgame with opposite-colored bishops and no other pieces (ignoring pawns) + // is almost a draw, in case of KBP vs KB is even more a draw. + if ( pos.non_pawn_material(WHITE) == BishopValueMg + && pos.non_pawn_material(BLACK) == BishopValueMg) + sf = more_than_one(pos.pieces(PAWN)) ? ScaleFactor(32) : ScaleFactor(8); + // Endgame with opposite-colored bishops, but also other pieces. Still // a bit drawish, but not as drawish as with only the two bishops. - sf = ScaleFactor(50 * sf / SCALE_FACTOR_NORMAL); + else + sf = ScaleFactor(50 * sf / SCALE_FACTOR_NORMAL); + } + // Endings where weaker side can place his king in front of the opponent's + // pawns are drawish. + else if ( abs(eg_value(score)) <= BishopValueEg + && ei.pi->pawn_span(strongSide) <= 1 + && !pos.pawn_passed(~strongSide, pos.king_square(~strongSide))) + sf = ei.pi->pawn_span(strongSide) ? ScaleFactor(56) : ScaleFactor(38); } - // Stealmate detection - Color stm = pos.side_to_move(); - if ( (ei.attackedBy[stm][ALL_PIECES] == ei.attackedBy[stm][KING]) - && (!(ei.attackedBy[stm][KING] & ~ei.attackedBy[~stm][ALL_PIECES]))) - sf = SCALE_FACTOR_DRAW; - // Interpolate between a middlegame and a (scaled by 'sf') endgame score Value v = mg_value(score) * int(ei.mi->game_phase()) + eg_value(score) * int(PHASE_MIDGAME - ei.mi->game_phase()) * sf / SCALE_FACTOR_NORMAL; @@ -802,20 +788,20 @@ namespace { // In case of tracing add all single evaluation contributions for both white and black if (Trace) { - Tracing::add_term(Tracing::PST, pos.psq_score()); - Tracing::add_term(Tracing::IMBALANCE, ei.mi->material_value()); - Tracing::add_term(PAWN, ei.pi->pawns_value()); - Tracing::add_term(Tracing::MOBILITY, apply_weight(mobility[WHITE], Weights[Mobility]) - , apply_weight(mobility[BLACK], Weights[Mobility])); + Tracing::write(Tracing::MATERIAL, pos.psq_score()); + Tracing::write(Tracing::IMBALANCE, ei.mi->material_value()); + Tracing::write(PAWN, ei.pi->pawns_value()); + Tracing::write(Tracing::MOBILITY, apply_weight(mobility[WHITE], Weights[Mobility]) + , apply_weight(mobility[BLACK], Weights[Mobility])); Score w = ei.mi->space_weight() * evaluate_space(pos, ei); Score b = ei.mi->space_weight() * evaluate_space(pos, ei); - Tracing::add_term(Tracing::SPACE, apply_weight(w, Weights[Space]), apply_weight(b, Weights[Space])); - Tracing::add_term(Tracing::TOTAL, score); + Tracing::write(Tracing::SPACE, apply_weight(w, Weights[Space]), apply_weight(b, Weights[Space])); + Tracing::write(Tracing::TOTAL, score); Tracing::ei = ei; Tracing::sf = sf; } - return pos.side_to_move() == WHITE ? v : -v; + return (pos.side_to_move() == WHITE ? v : -v) + Eval::Tempo; } @@ -823,25 +809,27 @@ namespace { double Tracing::to_cp(Value v) { return double(v) / PawnValueEg; } - void Tracing::add_term(int idx, Score wScore, Score bScore) { + void Tracing::write(int idx, Color c, Score s) { scores[c][idx] = s; } + + void Tracing::write(int idx, Score w, Score b) { - terms[WHITE][idx] = wScore; - terms[BLACK][idx] = bScore; + write(idx, WHITE, w); + write(idx, BLACK, b); } - void Tracing::format_row(std::stringstream& ss, const char* name, int idx) { + void Tracing::print(std::stringstream& ss, const char* name, int idx) { - Score wScore = terms[WHITE][idx]; - Score bScore = terms[BLACK][idx]; + Score wScore = scores[WHITE][idx]; + Score bScore = scores[BLACK][idx]; switch (idx) { - case PST: case IMBALANCE: case PAWN: case TOTAL: - ss << std::setw(20) << name << " | --- --- | --- --- | " + case MATERIAL: case IMBALANCE: case PAWN: case TOTAL: + ss << std::setw(15) << name << " | --- --- | --- --- | " << std::setw(5) << to_cp(mg_value(wScore - bScore)) << " " << std::setw(5) << to_cp(eg_value(wScore - bScore)) << " \n"; break; default: - ss << std::setw(20) << name << " | " << std::noshowpos + ss << std::setw(15) << name << " | " << std::noshowpos << std::setw(5) << to_cp(mg_value(wScore)) << " " << std::setw(5) << to_cp(eg_value(wScore)) << " | " << std::setw(5) << to_cp(mg_value(bScore)) << " " @@ -853,32 +841,32 @@ namespace { std::string Tracing::do_trace(const Position& pos) { - std::memset(terms, 0, sizeof(terms)); + std::memset(scores, 0, sizeof(scores)); Value v = do_evaluate(pos); v = pos.side_to_move() == WHITE ? v : -v; // White's point of view std::stringstream ss; ss << std::showpoint << std::noshowpos << std::fixed << std::setprecision(2) - << " Eval term | White | Black | Total \n" - << " | MG EG | MG EG | MG EG \n" - << "---------------------+-------------+-------------+-------------\n"; - - format_row(ss, "Material, PST, Tempo", PST); - format_row(ss, "Material imbalance", IMBALANCE); - format_row(ss, "Pawns", PAWN); - format_row(ss, "Knights", KNIGHT); - format_row(ss, "Bishops", BISHOP); - format_row(ss, "Rooks", ROOK); - format_row(ss, "Queens", QUEEN); - format_row(ss, "Mobility", MOBILITY); - format_row(ss, "King safety", KING); - format_row(ss, "Threats", THREAT); - format_row(ss, "Passed pawns", PASSED); - format_row(ss, "Space", SPACE); - - ss << "---------------------+-------------+-------------+-------------\n"; - format_row(ss, "Total", TOTAL); + << " Eval term | White | Black | Total \n" + << " | MG EG | MG EG | MG EG \n" + << "----------------+-------------+-------------+-------------\n"; + + print(ss, "Material", MATERIAL); + print(ss, "Imbalance", IMBALANCE); + print(ss, "Pawns", PAWN); + print(ss, "Knights", KNIGHT); + print(ss, "Bishops", BISHOP); + print(ss, "Rooks", ROOK); + print(ss, "Queens", QUEEN); + print(ss, "Mobility", MOBILITY); + print(ss, "King safety", KING); + print(ss, "Threats", THREAT); + print(ss, "Passed pawns", PASSED); + print(ss, "Space", SPACE); + + ss << "----------------+-------------+-------------+-------------\n"; + print(ss, "Total", TOTAL); ss << "\nTotal Evaluation: " << to_cp(v) << " (white side)\n"; @@ -907,27 +895,17 @@ namespace Eval { } - /// init() computes evaluation weights from the corresponding UCI parameters - /// and setup king tables. + /// init() computes evaluation weights. void init() { - Weights[Mobility] = weight_option("Mobility (Midgame)", "Mobility (Endgame)", WeightsInternal[Mobility]); - Weights[PawnStructure] = weight_option("Pawn Structure (Midgame)", "Pawn Structure (Endgame)", WeightsInternal[PawnStructure]); - Weights[PassedPawns] = weight_option("Passed Pawns (Midgame)", "Passed Pawns (Endgame)", WeightsInternal[PassedPawns]); - Weights[Space] = weight_option("Space", "Space", WeightsInternal[Space]); - Weights[KingDangerUs] = weight_option("Cowardice", "Cowardice", WeightsInternal[KingDangerUs]); - Weights[KingDangerThem] = weight_option("Aggressiveness", "Aggressiveness", WeightsInternal[KingDangerThem]); - const double MaxSlope = 30; const double Peak = 1280; for (int t = 0, i = 1; i < 100; ++i) { t = int(std::min(Peak, std::min(0.4 * i * i, t + MaxSlope))); - - KingDanger[1][i] = apply_weight(make_score(t, 0), Weights[KingDangerUs]); - KingDanger[0][i] = apply_weight(make_score(t, 0), Weights[KingDangerThem]); + KingDanger[i] = apply_weight(make_score(t, 0), Weights[KingSafety]); } }