From 2f92e3b5251e8da1c573a9860b8f42afca4f9366 Mon Sep 17 00:00:00 2001 From: Marco Costalba Date: Sat, 12 Apr 2014 08:32:52 +0200 Subject: [PATCH] Big reshuffle in evaluate.cpp Reshuffle functions to define them in reverse calling order (C style). This allow us to define templates before they are used. Currently it is not like this, for instance evaluate_pieces is defined after do_evaluate that calls it. This happens to work for some strange reason (two phase lookup?) but we want to avoid code that works 'by magic'. As a nice side-effect we can now remove the function prototypes. No functional change. --- src/evaluate.cpp | 374 ++++++++++++++++++++++------------------------- 1 file changed, 173 insertions(+), 201 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 3228c9a1..121f2c60 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -206,199 +206,37 @@ namespace { // scores, indexed by color and by a calculated integer number. Score KingDanger[COLOR_NB][128]; - // Function prototypes - template - Value do_evaluate(const Position& pos); - - template - void init_eval_info(const Position& pos, EvalInfo& ei); - - template - Score evaluate_pieces(const Position& pos, EvalInfo& ei, Score* mobility, Bitboard* mobilityArea); - - template - Score evaluate_king(const Position& pos, const EvalInfo& ei); - - template - Score evaluate_threats(const Position& pos, const EvalInfo& ei); - template - Score evaluate_passed_pawns(const Position& pos, const EvalInfo& ei); - - template - int evaluate_space(const Position& pos, const EvalInfo& ei); - - Score evaluate_unstoppable_pawns(const Position& pos, Color us, const EvalInfo& ei); - - Value interpolate(const Score& v, Phase ph, ScaleFactor sf); - Score apply_weight(Score v, const Weight& w); - Weight weight_option(const std::string& mgOpt, const std::string& egOpt, Score internalWeight); -} - - -namespace Eval { - - /// evaluate() is the main evaluation function. It always computes two - /// values, an endgame score and a middlegame score, and interpolates - /// between them based on the remaining material. - - Value evaluate(const Position& pos) { - return do_evaluate(pos); - } - - - /// trace() is like evaluate(), but instead of returning a value, it returns - /// a string (suitable for outputting to stdout) that contains the detailed - /// descriptions and values of each evaluation term. It's mainly used for - /// debugging. - std::string trace(const Position& pos) { - return Tracing::do_trace(pos); + // apply_weight() weights score v by score w trying to prevent overflow + Score apply_weight(Score v, const Weight& w) { + return make_score(mg_value(v) * w.mg / 256, eg_value(v) * w.eg / 256); } - /// init() computes evaluation weights from the corresponding UCI parameters - /// and setup king tables. - - 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 int MaxSlope = 30; - const int Peak = 1280; + // weight_option() computes the value of an evaluation weight, by combining + // two UCI-configurable weights (midgame and endgame) with an internal weight. - for (int t = 0, i = 1; i < 100; ++i) - { - t = std::min(Peak, std::min(int(0.4 * i * i), t + MaxSlope)); + Weight weight_option(const std::string& mgOpt, const std::string& egOpt, Score internalWeight) { - KingDanger[1][i] = apply_weight(make_score(t, 0), Weights[KingDangerUs]); - KingDanger[0][i] = apply_weight(make_score(t, 0), Weights[KingDangerThem]); - } + Weight w = { Options[mgOpt] * mg_value(internalWeight) / 100, + Options[egOpt] * eg_value(internalWeight) / 100 }; + return w; } -} // namespace Eval - - -namespace { - -template -Value do_evaluate(const Position& pos) { - assert(!pos.checkers()); - - EvalInfo ei; - Score score, mobility[2] = { SCORE_ZERO, SCORE_ZERO }; - 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); - - // Probe the material hash table - ei.mi = Material::probe(pos, thisThread->materialTable, thisThread->endgames); - score += ei.mi->material_value(); - - // 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); - - // Probe the pawn hash table - ei.pi = Pawns::probe(pos, thisThread->pawnsTable); - score += apply_weight(ei.pi->pawns_value(), Weights[PawnStructure]); - - // Initialize attack and king safety bitboards - init_eval_info(pos, ei); - init_eval_info(pos, ei); - - ei.attackedBy[WHITE][ALL_PIECES] |= ei.attackedBy[WHITE][KING]; - ei.attackedBy[BLACK][ALL_PIECES] |= ei.attackedBy[BLACK][KING]; - - // Do not include in mobility squares protected by enemy pawns or occupied by our pieces - Bitboard mobilityArea[] = { ~(ei.attackedBy[BLACK][PAWN] | pos.pieces(WHITE, PAWN, KING)), - ~(ei.attackedBy[WHITE][PAWN] | pos.pieces(BLACK, PAWN, KING)) }; - - // Evaluate pieces and mobility - score += evaluate_pieces(pos, ei, mobility, mobilityArea); - score += apply_weight(mobility[WHITE] - mobility[BLACK], Weights[Mobility]); - - // Evaluate kings after all other pieces because we need complete attack - // information when computing the king safety evaluation. - score += evaluate_king(pos, ei) - - evaluate_king(pos, ei); - - // Evaluate tactical threats, we need full attack information including king - score += evaluate_threats(pos, ei) - - evaluate_threats(pos, ei); - - // Evaluate passed pawns, we need full attack information including king - 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); - - // Evaluate space for both sides, only in middlegame - if (ei.mi->space_weight()) - { - int s = evaluate_space(pos, ei) - evaluate_space(pos, ei); - score += apply_weight(s * ei.mi->space_weight(), Weights[Space]); - } - - // 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); + // interpolate() interpolates between a middlegame and an endgame score, + // based on game phase. It also scales the return value by a ScaleFactor array. - // If we don't already have an unusual scale factor, check for opposite - // colored bishop 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) - { - // 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, 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); - } + Value interpolate(const Score& v, Phase ph, ScaleFactor sf) { - Value v = interpolate(score, ei.mi->game_phase(), sf); + assert(-VALUE_INFINITE < mg_value(v) && mg_value(v) < VALUE_INFINITE); + assert(-VALUE_INFINITE < eg_value(v) && eg_value(v) < VALUE_INFINITE); + assert(PHASE_ENDGAME <= ph && ph <= PHASE_MIDGAME); - // 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])); - 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::ei = ei; - Tracing::sf = sf; + int eg = (eg_value(v) * int(sf)) / SCALE_FACTOR_NORMAL; + return Value((mg_value(v) * int(ph) + eg * int(PHASE_MIDGAME - ph)) / PHASE_MIDGAME); } - return pos.side_to_move() == WHITE ? v : -v; -} - // init_eval_info() initializes king bitboards for given color adding // pawn attacks. To be done at the beginning of the evaluation. @@ -463,6 +301,7 @@ Value do_evaluate(const Position& pos) { Square s; Score score = SCORE_ZERO; + const PieceType NextPt = (Us == WHITE ? Pt : PieceType(Pt + 1)); const Color Them = (Us == WHITE ? BLACK : WHITE); const Square* pl = pos.list(Us); @@ -574,8 +413,6 @@ Value do_evaluate(const Position& pos) { if (Trace) Tracing::terms[Us][Pt] = score; - const PieceType NextPt = (Us == WHITE ? Pt : PieceType(Pt + 1)); - return score - evaluate_pieces(pos, ei, mobility, mobilityArea); } @@ -899,33 +736,120 @@ Value do_evaluate(const Position& pos) { } - // interpolate() interpolates between a middlegame and an endgame score, - // based on game phase. It also scales the return value by a ScaleFactor array. + // do_evaluate() is the evaluation entry point, called directly from evaluate() - Value interpolate(const Score& v, Phase ph, ScaleFactor sf) { + template + Value do_evaluate(const Position& pos) { - assert(-VALUE_INFINITE < mg_value(v) && mg_value(v) < VALUE_INFINITE); - assert(-VALUE_INFINITE < eg_value(v) && eg_value(v) < VALUE_INFINITE); - assert(PHASE_ENDGAME <= ph && ph <= PHASE_MIDGAME); + assert(!pos.checkers()); - int eg = (eg_value(v) * int(sf)) / SCALE_FACTOR_NORMAL; - return Value((mg_value(v) * int(ph) + eg * int(PHASE_MIDGAME - ph)) / PHASE_MIDGAME); - } + EvalInfo ei; + Score score, mobility[2] = { SCORE_ZERO, SCORE_ZERO }; + 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); + + // Probe the material hash table + ei.mi = Material::probe(pos, thisThread->materialTable, thisThread->endgames); + score += ei.mi->material_value(); + + // 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); + + // Probe the pawn hash table + ei.pi = Pawns::probe(pos, thisThread->pawnsTable); + score += apply_weight(ei.pi->pawns_value(), Weights[PawnStructure]); + + // Initialize attack and king safety bitboards + init_eval_info(pos, ei); + init_eval_info(pos, ei); + + ei.attackedBy[WHITE][ALL_PIECES] |= ei.attackedBy[WHITE][KING]; + ei.attackedBy[BLACK][ALL_PIECES] |= ei.attackedBy[BLACK][KING]; + + // Do not include in mobility squares protected by enemy pawns or occupied by our pieces + Bitboard mobilityArea[] = { ~(ei.attackedBy[BLACK][PAWN] | pos.pieces(WHITE, PAWN, KING)), + ~(ei.attackedBy[WHITE][PAWN] | pos.pieces(BLACK, PAWN, KING)) }; + + // Evaluate pieces and mobility + score += evaluate_pieces(pos, ei, mobility, mobilityArea); + score += apply_weight(mobility[WHITE] - mobility[BLACK], Weights[Mobility]); + + // Evaluate kings after all other pieces because we need complete attack + // information when computing the king safety evaluation. + score += evaluate_king(pos, ei) + - evaluate_king(pos, ei); + + // Evaluate tactical threats, we need full attack information including king + score += evaluate_threats(pos, ei) + - evaluate_threats(pos, ei); + + // Evaluate passed pawns, we need full attack information including king + 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); + + // Evaluate space for both sides, only in middlegame + if (ei.mi->space_weight()) + { + int s = evaluate_space(pos, ei) - evaluate_space(pos, ei); + score += apply_weight(s * ei.mi->space_weight(), Weights[Space]); + } - // apply_weight() weights score v by score w trying to prevent overflow - Score apply_weight(Score v, const Weight& w) { + // 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); - return make_score(mg_value(v) * w.mg / 256, eg_value(v) * w.eg / 256); - } + // If we don't already have an unusual scale factor, check for opposite + // colored bishop 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) + { + // 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, 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); + } - // weight_option() computes the value of an evaluation weight, by combining - // two UCI-configurable weights (midgame and endgame) with an internal weight. + Value v = interpolate(score, ei.mi->game_phase(), sf); - Weight weight_option(const std::string& mgOpt, const std::string& egOpt, Score internalWeight) { + // 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])); + 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::ei = ei; + Tracing::sf = sf; + } - Weight w = { Options[mgOpt] * mg_value(internalWeight) / 100, - Options[egOpt] * eg_value(internalWeight) / 100 }; - return w; + return pos.side_to_move() == WHITE ? v : -v; } @@ -994,4 +918,52 @@ Value do_evaluate(const Position& pos) { return ss.str(); } -} + +} // namespace + + +namespace Eval { + + /// evaluate() is the main evaluation function. It always computes two + /// values, an endgame score and a middlegame score, and interpolates + /// between them based on the remaining material. + + Value evaluate(const Position& pos) { + return do_evaluate(pos); + } + + + /// trace() is like evaluate(), but instead of returning a value, it returns + /// a string (suitable for outputting to stdout) that contains the detailed + /// descriptions and values of each evaluation term. It's mainly used for + /// debugging. + std::string trace(const Position& pos) { + return Tracing::do_trace(pos); + } + + + /// init() computes evaluation weights from the corresponding UCI parameters + /// and setup king tables. + + 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 int MaxSlope = 30; + const int Peak = 1280; + + for (int t = 0, i = 1; i < 100; ++i) + { + t = std::min(Peak, std::min(int(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]); + } + } + +} // namespace Eval -- 2.39.2