namespace {
// Threshold for lazy and space evaluation
- constexpr Value LazyThreshold = Value(1400);
+ constexpr Value LazyThreshold1 = Value(1400);
+ constexpr Value LazyThreshold2 = Value(1300);
constexpr Value SpaceThreshold = Value(12222);
// KingAttackWeights[PieceType] contains king attack weights by piece type
};
// Assorted bonuses and penalties
+ constexpr Score BadOutpost = S( -7, 36);
constexpr Score BishopOnKingRing = S( 24, 0);
constexpr Score BishopPawns = S( 3, 7);
constexpr Score BishopXRayPawns = S( 4, 5);
if (Pt == BISHOP || Pt == KNIGHT)
{
- // Bonus if piece is on an outpost square or can reach one
+ // Bonus if the piece is on an outpost square or can reach one
+ // Reduced bonus for knights (BadOutpost) if few relevant targets
bb = OutpostRanks & attackedBy[Us][PAWN] & ~pe->pawn_attacks_span(Them);
- if (bb & s)
+ Bitboard targets = pos.pieces(Them) & ~pos.pieces(PAWN);
+
+ if ( Pt == KNIGHT
+ && bb & s & ~CenterFiles // on a side outpost
+ && !(b & targets) // no relevant attacks
+ && (!more_than_one(targets & (s & QueenSide ? QueenSide : KingSide))))
+ score += BadOutpost;
+ else if (bb & s)
score += Outpost[Pt == BISHOP];
else if (Pt == KNIGHT && bb & b & ~pos.pieces(Us))
score += ReachableOutpost;
// Bonus for threats on the next moves against enemy queen
if (pos.count<QUEEN>(Them) == 1)
{
+ bool queenImbalance = pos.count<QUEEN>() == 1;
+
Square s = pos.square<QUEEN>(Them);
- safe = mobilityArea[Us] & ~stronglyProtected;
+ safe = mobilityArea[Us]
+ & ~pos.pieces(Us, PAWN)
+ & ~stronglyProtected;
b = attackedBy[Us][KNIGHT] & attacks_bb<KNIGHT>(s);
- score += KnightOnQueen * popcount(b & safe);
+ score += KnightOnQueen * popcount(b & safe) * (1 + queenImbalance);
b = (attackedBy[Us][BISHOP] & attacks_bb<BISHOP>(s, pos.pieces()))
| (attackedBy[Us][ROOK ] & attacks_bb<ROOK >(s, pos.pieces()));
- score += SliderOnQueen * popcount(b & safe & attackedBy2[Us]);
+ score += SliderOnQueen * popcount(b & safe & attackedBy2[Us]) * (1 + queenImbalance);
}
if (T)
}
- // Evaluation::winnable() adjusts the mg and eg score components based on the
- // known attacking/defending status of the players. A single value is derived
- // by interpolation from the mg and eg values and returned.
+ // Evaluation::winnable() adjusts the midgame and endgame score components, based on
+ // the known attacking/defending status of the players. The final value is derived
+ // by interpolation from the midgame and endgame values.
template<Tracing T>
Value Evaluation<T>::winnable(Score score) const {
Color strongSide = eg > VALUE_DRAW ? WHITE : BLACK;
int sf = me->scale_factor(pos, strongSide);
- // If scale is not already specific, scale down the endgame via general heuristics
+ // If scale factor is not already specific, scale down via general heuristics
if (sf == SCALE_FACTOR_NORMAL)
{
if (pos.opposite_bishops())
score += pe->pawn_score(WHITE) - pe->pawn_score(BLACK);
// Early exit if score is high
- Value v = (mg_value(score) + eg_value(score)) / 2;
- if (abs(v) > LazyThreshold + pos.non_pawn_material() / 64)
- return pos.side_to_move() == WHITE ? v : -v;
+ auto lazy_skip = [&](Value lazyThreshold) {
+ return abs(mg_value(score) + eg_value(score)) / 2 > lazyThreshold + pos.non_pawn_material() / 64;
+ };
+
+ if (lazy_skip(LazyThreshold1))
+ goto make_v;
// Main evaluation begins here
initialize<WHITE>();
// More complex interactions that require fully populated attack bitboards
score += king< WHITE>() - king< BLACK>()
- + threats<WHITE>() - threats<BLACK>()
- + passed< WHITE>() - passed< BLACK>()
+ + passed< WHITE>() - passed< BLACK>();
+
+ if (lazy_skip(LazyThreshold2))
+ goto make_v;
+
+ score += threats<WHITE>() - threats<BLACK>()
+ space< WHITE>() - space< BLACK>();
+make_v:
// Derive single value from mg and eg parts of score
- v = winnable(score);
+ Value v = winnable(score);
// In case of tracing add all remaining individual evaluation terms
if (T)