1
0
Fork 0
mirror of https://github.com/sockspls/badfish synced 2025-04-30 16:53:09 +00:00

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.
This commit is contained in:
Marco Costalba 2014-04-12 08:32:52 +02:00
parent 0510112f91
commit 2f92e3b525

View file

@ -206,199 +206,37 @@ namespace {
// scores, indexed by color and by a calculated integer number.
Score KingDanger[COLOR_NB][128];
// Function prototypes
template<bool Trace>
Value do_evaluate(const Position& pos);
template<Color Us>
void init_eval_info(const Position& pos, EvalInfo& ei);
template<PieceType Pt, Color Us, bool Trace>
Score evaluate_pieces(const Position& pos, EvalInfo& ei, Score* mobility, Bitboard* mobilityArea);
template<Color Us, bool Trace>
Score evaluate_king(const Position& pos, const EvalInfo& ei);
template<Color Us, bool Trace>
Score evaluate_threats(const Position& pos, const EvalInfo& ei);
template<Color Us, bool Trace>
Score evaluate_passed_pawns(const Position& pos, const EvalInfo& ei);
template<Color Us>
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<false>(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);
}
/// 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);
// 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() computes evaluation weights from the corresponding UCI parameters
/// and setup king tables.
// interpolate() interpolates between a middlegame and an endgame score,
// based on game phase. It also scales the return value by a ScaleFactor array.
void init() {
Value interpolate(const Score& v, Phase ph, ScaleFactor sf) {
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]);
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);
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]);
}
int eg = (eg_value(v) * int(sf)) / SCALE_FACTOR_NORMAL;
return Value((mg_value(v) * int(ph) + eg * int(PHASE_MIDGAME - ph)) / PHASE_MIDGAME);
}
} // namespace Eval
namespace {
template<bool Trace>
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<WHITE>(pos, ei);
init_eval_info<BLACK>(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<KNIGHT, WHITE, Trace>(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<WHITE, Trace>(pos, ei)
- evaluate_king<BLACK, Trace>(pos, ei);
// Evaluate tactical threats, we need full attack information including king
score += evaluate_threats<WHITE, Trace>(pos, ei)
- evaluate_threats<BLACK, Trace>(pos, ei);
// Evaluate passed pawns, we need full attack information including king
score += evaluate_passed_pawns<WHITE, Trace>(pos, ei)
- evaluate_passed_pawns<BLACK, Trace>(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<WHITE>(pos, ei) - evaluate_space<BLACK>(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);
// 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<PAWN>(WHITE) + pos.count<PAWN>(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 v = interpolate(score, ei.mi->game_phase(), sf);
// 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<WHITE>(pos, ei);
Score b = ei.mi->space_weight() * evaluate_space<BLACK>(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;
}
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<Pt>(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<NextPt, Them, Trace>(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<bool Trace>
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();
// apply_weight() weights score v by score w trying to prevent overflow
Score apply_weight(Score v, const Weight& w) {
// 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);
return make_score(mg_value(v) * w.mg / 256, eg_value(v) * w.eg / 256);
}
// Probe the material hash table
ei.mi = Material::probe(pos, thisThread->materialTable, thisThread->endgames);
score += ei.mi->material_value();
// weight_option() computes the value of an evaluation weight, by combining
// two UCI-configurable weights (midgame and endgame) with an internal weight.
// 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);
Weight weight_option(const std::string& mgOpt, const std::string& egOpt, Score internalWeight) {
// Probe the pawn hash table
ei.pi = Pawns::probe(pos, thisThread->pawnsTable);
score += apply_weight(ei.pi->pawns_value(), Weights[PawnStructure]);
Weight w = { Options[mgOpt] * mg_value(internalWeight) / 100,
Options[egOpt] * eg_value(internalWeight) / 100 };
return w;
// Initialize attack and king safety bitboards
init_eval_info<WHITE>(pos, ei);
init_eval_info<BLACK>(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<KNIGHT, WHITE, Trace>(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<WHITE, Trace>(pos, ei)
- evaluate_king<BLACK, Trace>(pos, ei);
// Evaluate tactical threats, we need full attack information including king
score += evaluate_threats<WHITE, Trace>(pos, ei)
- evaluate_threats<BLACK, Trace>(pos, ei);
// Evaluate passed pawns, we need full attack information including king
score += evaluate_passed_pawns<WHITE, Trace>(pos, ei)
- evaluate_passed_pawns<BLACK, Trace>(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<WHITE>(pos, ei) - evaluate_space<BLACK>(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);
// 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<PAWN>(WHITE) + pos.count<PAWN>(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 v = interpolate(score, ei.mi->game_phase(), sf);
// 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<WHITE>(pos, ei);
Score b = ei.mi->space_weight() * evaluate_space<BLACK>(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;
}
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<false>(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