From 087b638f6c8982927cbff0d44d6fb825a567e182 Mon Sep 17 00:00:00 2001 From: Marco Costalba Date: Tue, 25 Aug 2015 17:12:51 +0200 Subject: [PATCH 1/1] Reformat trace code Apart from usual renaiming, take advantage of C++11 function template default parmeter to get rid of Eval trampoline functions. Some triviality fixes while there. No functional change. --- src/benchmark.cpp | 2 +- src/evaluate.cpp | 427 ++++++++++++++++++++++------------------------ src/evaluate.h | 3 +- 3 files changed, 205 insertions(+), 227 deletions(-) diff --git a/src/benchmark.cpp b/src/benchmark.cpp index 07e1341e..f58d82e2 100644 --- a/src/benchmark.cpp +++ b/src/benchmark.cpp @@ -143,7 +143,6 @@ void benchmark(const Position& current, istream& is) { } uint64_t nodes = 0; - Search::StateStackPtr st; TimePoint elapsed = now(); for (size_t i = 0; i < fens.size(); ++i) @@ -157,6 +156,7 @@ void benchmark(const Position& current, istream& is) { else { + Search::StateStackPtr st; Threads.start_thinking(pos, limits, st); Threads.main()->join(); nodes += Search::RootPos.nodes_searched(); diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 3e8dcc26..ae85476b 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -30,34 +30,50 @@ namespace { - namespace Tracing { + namespace Trace { enum Term { // First 8 entries are for PieceType MATERIAL = 8, IMBALANCE, MOBILITY, THREAT, PASSED, SPACE, TOTAL, TERM_NB }; - Score scores[COLOR_NB][TERM_NB]; + double scores[TERM_NB][COLOR_NB][PHASE_NB]; - std::ostream& operator<<(std::ostream& os, Term idx); + double to_cp(Value v) { return double(v) / PawnValueEg; } - double to_cp(Value v); - void write(int idx, Color c, Score s); - void write(int idx, Score w, Score b = SCORE_ZERO); - std::string do_trace(const Position& pos); + void add(int idx, Color c, Score s) { + scores[idx][c][MG] = to_cp(mg_value(s)); + scores[idx][c][EG] = to_cp(eg_value(s)); + } + + void add(int idx, Score w, Score b = SCORE_ZERO) { + add(idx, WHITE, w); add(idx, BLACK, b); + } + + std::ostream& operator<<(std::ostream& os, Term t) { + + if (t == MATERIAL || t == IMBALANCE || t == Term(PAWN) || t == TOTAL) + os << " --- --- | --- --- | "; + else + os << std::setw(5) << scores[t][WHITE][MG] << " " + << std::setw(5) << scores[t][WHITE][EG] << " | " + << std::setw(5) << scores[t][BLACK][MG] << " " + << std::setw(5) << scores[t][BLACK][EG] << " | "; + + os << std::setw(5) << scores[t][WHITE][MG] - scores[t][BLACK][MG] << " " + << std::setw(5) << scores[t][WHITE][EG] - scores[t][BLACK][EG] << " \n"; + + return os; + } } + using namespace Trace; // Struct EvalInfo contains various information computed and collected // by the evaluation functions. struct EvalInfo { - // Pointers to material and pawn hash table entries - Material::Entry* mi; - Pawns::Entry* pi; - // attackedBy[color][piece type] is a bitboard representing all squares - // attacked by a given color and piece type, attackedBy[color][ALL_PIECES] - // contains all squares attacked by the given color. + // attacked by a given color and piece type (can be also ALL_PIECES). Bitboard attackedBy[COLOR_NB][PIECE_TYPE_NB]; // kingRing[color] is the zone around the king which is considered @@ -86,6 +102,7 @@ namespace { int kingAdjacentZoneAttacksCount[COLOR_NB]; Bitboard pinnedPieces[COLOR_NB]; + Pawns::Entry* pi; }; @@ -213,8 +230,9 @@ namespace { const Square Down = (Us == WHITE ? DELTA_S : DELTA_N); ei.pinnedPieces[Us] = pos.pinned_pieces(Us); - ei.attackedBy[Us][ALL_PIECES] = ei.attackedBy[Us][PAWN] = ei.pi->pawn_attacks(Us); Bitboard b = ei.attackedBy[Them][KING] = pos.attacks_from(pos.square(Them)); + ei.attackedBy[Them][ALL_PIECES] |= b; + 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.non_pawn_material(Us) >= QueenValueMg) @@ -231,7 +249,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; @@ -332,11 +350,11 @@ namespace { } } - if (Trace) - Tracing::write(Pt, Us, score); + if (DoTrace) + Trace::add(Pt, Us, score); // Recursively call evaluate_pieces() of next piece type until KING excluded - return score - evaluate_pieces(pos, ei, mobility, mobilityArea); + return score - evaluate_pieces(pos, ei, mobility, mobilityArea); } template<> @@ -347,7 +365,7 @@ namespace { // evaluate_king() assigns bonuses and penalties to a king of a given color - template + template Score evaluate_king(const Position& pos, const EvalInfo& ei) { const Color Them = (Us == WHITE ? BLACK : WHITE); @@ -378,9 +396,9 @@ namespace { attackUnits = std::min(74, ei.kingAttackersCount[Them] * ei.kingAttackersWeight[Them]) + 8 * ei.kingAdjacentZoneAttacksCount[Them] + 25 * popcount(undefended) - + 11 * (ei.pinnedPieces[Us] != 0) - - mg_value(score) / 8 - - !pos.count(Them) * 60; + + 11 * !!ei.pinnedPieces[Us] + - 60 * !pos.count(Them) + - mg_value(score) / 8; // Analyse the enemy's safe queen contact checks. Firstly, find the // undefended squares around the king reachable by the enemy queen... @@ -443,8 +461,8 @@ namespace { score -= KingDanger[std::max(std::min(attackUnits, 399), 0)]; } - if (Trace) - Tracing::write(KING, Us, score); + if (DoTrace) + Trace::add(KING, Us, score); return score; } @@ -453,7 +471,7 @@ namespace { // evaluate_threats() assigns bonuses according to the type of attacking piece // and the type of attacked one. - template + template Score evaluate_threats(const Position& pos, const EvalInfo& ei) { const Color Them = (Us == WHITE ? BLACK : WHITE); @@ -541,8 +559,8 @@ namespace { if (b) score += popcount(b) * PawnAttackThreat; - if (Trace) - Tracing::write(Tracing::THREAT, Us, score); + if (DoTrace) + Trace::add(THREAT, Us, score); return score; } @@ -550,7 +568,7 @@ namespace { // evaluate_passed_pawns() evaluates the passed pawns of the given color - template + template Score evaluate_passed_pawns(const Position& pos, const EvalInfo& ei) { const Color Them = (Us == WHITE ? BLACK : WHITE); @@ -623,8 +641,8 @@ namespace { score += make_score(mbonus, ebonus); } - if (Trace) - Tracing::write(Tracing::PASSED, Us, score * Weights[PassedPawns]); + if (DoTrace) + Trace::add(PASSED, Us, score * Weights[PassedPawns]); // Add the scores to the middlegame and endgame eval return score * Weights[PassedPawns]; @@ -655,10 +673,10 @@ namespace { behind |= (Us == WHITE ? behind >> 8 : behind << 8); behind |= (Us == WHITE ? behind >> 16 : behind << 16); - // Since SpaceMask[Us] is fully on our half of the board + // Since SpaceMask[Us] is fully on our half of the board... assert(unsigned(safe >> (Us == WHITE ? 32 : 0)) == 0); - // Count safe + (behind & safe) with a single popcount + // ...count safe + (behind & safe) with a single popcount int bonus = popcount((Us == WHITE ? safe << 32 : safe >> 32) | (behind & safe)); int weight = pos.count(Us) + pos.count(Us) + pos.count(Them) + pos.count(Them); @@ -666,234 +684,193 @@ namespace { return make_score(bonus * weight * weight, 0); } +} // namespace - // do_evaluate() is the evaluation entry point, called directly from evaluate() - - template - Value do_evaluate(const Position& pos) { - - assert(!pos.checkers()); - - EvalInfo ei; - Score score, mobility[2] = { SCORE_ZERO, SCORE_ZERO }; - - // Initialize score by reading the incrementally updated scores included - // 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); - score += ei.mi->imbalance(); - - // 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); - score += ei.pi->pawns_score() * Weights[PawnStructure]; - // Initialize attack and king safety bitboards - init_eval_info(pos, ei); - init_eval_info(pos, ei); +/// evaluate() is the main evaluation function. It returns a static evaluation +/// of the position always from the point of view of the side to move. - ei.attackedBy[WHITE][ALL_PIECES] |= ei.attackedBy[WHITE][KING]; - ei.attackedBy[BLACK][ALL_PIECES] |= ei.attackedBy[BLACK][KING]; +template +Value Eval::evaluate(const Position& pos) { - // Pawns blocked or on ranks 2 and 3. Will be excluded from the mobility area - Bitboard blockedPawns[] = { - pos.pieces(WHITE, PAWN) & (shift_bb(pos.pieces()) | Rank2BB | Rank3BB), - pos.pieces(BLACK, PAWN) & (shift_bb(pos.pieces()) | Rank7BB | Rank6BB) - }; + assert(!pos.checkers()); - // Do not include in mobility squares protected by enemy pawns, or occupied - // by our blocked pawns or king. - Bitboard mobilityArea[] = { - ~(ei.attackedBy[BLACK][PAWN] | blockedPawns[WHITE] | pos.square(WHITE)), - ~(ei.attackedBy[WHITE][PAWN] | blockedPawns[BLACK] | pos.square(BLACK)) - }; + EvalInfo ei; + Score score, mobility[2] = { SCORE_ZERO, SCORE_ZERO }; - // Evaluate pieces and mobility - score += evaluate_pieces(pos, ei, mobility, mobilityArea); - score += (mobility[WHITE] - mobility[BLACK]) * Weights[Mobility]; + // Initialize score by reading the incrementally updated scores included + // in the position object (material + piece square tables). + // Score is computed from the point of view of white. + score = pos.psq_score(); - // 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); + // Probe the material hash table + Material::Entry* me = Material::probe(pos); + score += me->imbalance(); - // Evaluate tactical threats, we need full attack information including king - score += evaluate_threats(pos, ei) - - evaluate_threats(pos, ei); + // If we have a specialized evaluation function for the current material + // configuration, call it and return. + if (me->specialized_eval_exists()) + return me->evaluate(pos); - // Evaluate passed pawns, we need full attack information including king - score += evaluate_passed_pawns(pos, ei) - - evaluate_passed_pawns(pos, ei); + // Probe the pawn hash table + ei.pi = Pawns::probe(pos); + score += ei.pi->pawns_score() * Weights[PawnStructure]; - // If both sides have only pawns, score for potential unstoppable pawns - if (!pos.non_pawn_material(WHITE) && !pos.non_pawn_material(BLACK)) - { - Bitboard b; - if ((b = ei.pi->passed_pawns(WHITE)) != 0) - score += int(relative_rank(WHITE, frontmost_sq(WHITE, b))) * Unstoppable; + // Initialize attack and king safety bitboards + ei.attackedBy[WHITE][ALL_PIECES] = ei.attackedBy[BLACK][ALL_PIECES] = 0; + init_eval_info(pos, ei); + init_eval_info(pos, ei); - if ((b = ei.pi->passed_pawns(BLACK)) != 0) - score -= int(relative_rank(BLACK, frontmost_sq(BLACK, b))) * Unstoppable; - } + // Pawns blocked or on ranks 2 and 3. Will be excluded from the mobility area + Bitboard blockedPawns[] = { + pos.pieces(WHITE, PAWN) & (shift_bb(pos.pieces()) | Rank2BB | Rank3BB), + pos.pieces(BLACK, PAWN) & (shift_bb(pos.pieces()) | Rank7BB | Rank6BB) + }; - // Evaluate space for both sides, only during opening - if (pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK) >= 11756) - score += (evaluate_space(pos, ei) - evaluate_space(pos, ei)) * Weights[Space]; + // Do not include in mobility squares protected by enemy pawns, or occupied + // by our blocked pawns or king. + Bitboard mobilityArea[] = { + ~(ei.attackedBy[BLACK][PAWN] | blockedPawns[WHITE] | pos.square(WHITE)), + ~(ei.attackedBy[WHITE][PAWN] | blockedPawns[BLACK] | pos.square(BLACK)) + }; - // Scale winning side if position is more drawish than it appears - Color strongSide = eg_value(score) > VALUE_DRAW ? WHITE : BLACK; - ScaleFactor sf = ei.mi->scale_factor(pos, strongSide); + // Evaluate pieces and mobility + score += evaluate_pieces(pos, ei, mobility, mobilityArea); + score += (mobility[WHITE] - mobility[BLACK]) * Weights[Mobility]; - // 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 - && (sf == SCALE_FACTOR_NORMAL || sf == SCALE_FACTOR_ONEPAWN)) - { - if (pos.opposite_bishops()) - { - // 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. - 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.square(~strongSide))) - sf = ei.pi->pawn_span(strongSide) ? ScaleFactor(56) : ScaleFactor(38); - } + // 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); - // 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; + // Evaluate tactical threats, we need full attack information including king + score += evaluate_threats(pos, ei) + - evaluate_threats(pos, ei); - v /= int(PHASE_MIDGAME); + // Evaluate passed pawns, we need full attack information including king + score += evaluate_passed_pawns(pos, ei) + - evaluate_passed_pawns(pos, ei); - // In case of tracing add all single evaluation terms for both white and black - if (Trace) - { - Tracing::write(Tracing::MATERIAL, pos.psq_score()); - Tracing::write(Tracing::IMBALANCE, ei.mi->imbalance()); - Tracing::write(PAWN, ei.pi->pawns_score()); - Tracing::write(Tracing::MOBILITY, mobility[WHITE] * Weights[Mobility] - , mobility[BLACK] * Weights[Mobility]); - Tracing::write(Tracing::SPACE, evaluate_space(pos, ei) * Weights[Space] - , evaluate_space(pos, ei) * Weights[Space]); - Tracing::write(Tracing::TOTAL, score); - } + // If both sides have only pawns, score for potential unstoppable pawns + if (!pos.non_pawn_material(WHITE) && !pos.non_pawn_material(BLACK)) + { + Bitboard b; + if ((b = ei.pi->passed_pawns(WHITE)) != 0) + score += int(relative_rank(WHITE, frontmost_sq(WHITE, b))) * Unstoppable; - return (pos.side_to_move() == WHITE ? v : -v) + Eval::Tempo; // Side to move point of view + if ((b = ei.pi->passed_pawns(BLACK)) != 0) + score -= int(relative_rank(BLACK, frontmost_sq(BLACK, b))) * Unstoppable; } - - // Tracing functions - - double Tracing::to_cp(Value v) { return double(v) / PawnValueEg; } - - void Tracing::write(int idx, Color c, Score s) { scores[c][idx] = s; } - - void Tracing::write(int idx, Score w, Score b) { - scores[WHITE][idx] = w, scores[BLACK][idx] = b; + // Evaluate space for both sides, only during opening + if (pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK) >= 11756) + score += (evaluate_space(pos, ei) - evaluate_space(pos, ei)) * Weights[Space]; + + // Scale winning side if position is more drawish than it appears + Color strongSide = eg_value(score) > VALUE_DRAW ? WHITE : BLACK; + ScaleFactor sf = me->scale_factor(pos, strongSide); + + // If we don't already have an unusual scale factor, check for certain + // types of endgames, and use a lower scale for those. + if ( me->game_phase() < PHASE_MIDGAME + && (sf == SCALE_FACTOR_NORMAL || sf == SCALE_FACTOR_ONEPAWN)) + { + if (pos.opposite_bishops()) + { + // 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. + 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.square(~strongSide))) + sf = ei.pi->pawn_span(strongSide) ? ScaleFactor(56) : ScaleFactor(38); } - std::ostream& Tracing::operator<<(std::ostream& os, Term t) { - - double wScore[] = { to_cp(mg_value(scores[WHITE][t])), to_cp(eg_value(scores[WHITE][t])) }; - double bScore[] = { to_cp(mg_value(scores[BLACK][t])), to_cp(eg_value(scores[BLACK][t])) }; + // Interpolate between a middlegame and a (scaled by 'sf') endgame score + Value v = mg_value(score) * int(me->game_phase()) + + eg_value(score) * int(PHASE_MIDGAME - me->game_phase()) * sf / SCALE_FACTOR_NORMAL; + + v /= int(PHASE_MIDGAME); + + // In case of tracing add all single evaluation terms + if (DoTrace) + { + Trace::add(MATERIAL, pos.psq_score()); + Trace::add(IMBALANCE, me->imbalance()); + Trace::add(PAWN, ei.pi->pawns_score()); + Trace::add(MOBILITY, mobility[WHITE] * Weights[Mobility] + , mobility[BLACK] * Weights[Mobility]); + Trace::add(SPACE, evaluate_space(pos, ei) * Weights[Space] + , evaluate_space(pos, ei) * Weights[Space]); + Trace::add(TOTAL, score); + } - if (t == MATERIAL || t == IMBALANCE || t == Term(PAWN) || t == TOTAL) - os << " --- --- | --- --- | "; - else - os << std::setw(5) << wScore[MG] << " " << std::setw(5) << wScore[EG] << " | " - << std::setw(5) << bScore[MG] << " " << std::setw(5) << bScore[EG] << " | "; + return (pos.side_to_move() == WHITE ? v : -v) + Eval::Tempo; // Side to move point of view +} - os << std::setw(5) << wScore[MG] - bScore[MG] << " " - << std::setw(5) << wScore[EG] - bScore[EG] << " \n"; +// Explicit template instantiations +template Value Eval::evaluate(const Position&); +template Value Eval::evaluate(const Position&); - return os; - } - std::string Tracing::do_trace(const Position& pos) { - - 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" - << " Material | " << Term(MATERIAL) - << " Imbalance | " << Term(IMBALANCE) - << " Pawns | " << Term(PAWN) - << " Knights | " << Term(KNIGHT) - << " Bishop | " << Term(BISHOP) - << " Rooks | " << Term(ROOK) - << " Queens | " << Term(QUEEN) - << " Mobility | " << Term(MOBILITY) - << " King safety | " << Term(KING) - << " Threats | " << Term(THREAT) - << " Passed pawns | " << Term(PASSED) - << " Space | " << Term(SPACE) - << "----------------+-------------+-------------+-------------\n" - << " Total | " << Term(TOTAL); - - ss << "\nTotal Evaluation: " << to_cp(v) << " (white side)\n"; - - return ss.str(); - } +/// 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. Useful for debugging. -} // namespace +std::string Eval::trace(const Position& pos) { + std::memset(scores, 0, sizeof(scores)); -namespace Eval { + Value v = evaluate(pos); + v = pos.side_to_move() == WHITE ? v : -v; // White's point of view - /// evaluate() is the main evaluation function. It returns a static evaluation - /// of the position always from the point of view of the side to move. + 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" + << " Material | " << Term(MATERIAL) + << " Imbalance | " << Term(IMBALANCE) + << " Pawns | " << Term(PAWN) + << " Knights | " << Term(KNIGHT) + << " Bishop | " << Term(BISHOP) + << " Rooks | " << Term(ROOK) + << " Queens | " << Term(QUEEN) + << " Mobility | " << Term(MOBILITY) + << " King safety | " << Term(KING) + << " Threats | " << Term(THREAT) + << " Passed pawns | " << Term(PASSED) + << " Space | " << Term(SPACE) + << "----------------+-------------+-------------+-------------\n" + << " Total | " << Term(TOTAL); - Value evaluate(const Position& pos) { - return do_evaluate(pos); - } + ss << "\nTotal Evaluation: " << to_cp(v) << " (white side)\n"; - - /// 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); - } + return ss.str(); +} - /// init() computes evaluation weights, usually at startup +/// init() computes evaluation weights, usually at startup - void init() { +void Eval::init() { - const int MaxSlope = 8700; - const int Peak = 1280000; - int t = 0; + const int MaxSlope = 8700; + const int Peak = 1280000; + int t = 0; - for (int i = 0; i < 400; ++i) - { - t = std::min(Peak, std::min(i * i * 27, t + MaxSlope)); - KingDanger[i] = make_score(t / 1000, 0) * Weights[KingSafety]; - } + for (int i = 0; i < 400; ++i) + { + t = std::min(Peak, std::min(i * i * 27, t + MaxSlope)); + KingDanger[i] = make_score(t / 1000, 0) * Weights[KingSafety]; } - -} // namespace Eval +} diff --git a/src/evaluate.h b/src/evaluate.h index d859582a..450f2535 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -31,9 +31,10 @@ namespace Eval { const Value Tempo = Value(17); // Must be visible to search void init(); -Value evaluate(const Position& pos); std::string trace(const Position& pos); +template +Value evaluate(const Position& pos); } #endif // #ifndef EVALUATE_H_INCLUDED -- 2.39.2