constexpr Value SpaceThreshold = Value(12222);
// KingAttackWeights[PieceType] contains king attack weights by piece type
- constexpr int KingAttackWeights[PIECE_TYPE_NB] = { 0, 0, 77, 55, 44, 10 };
+ constexpr int KingAttackWeights[PIECE_TYPE_NB] = { 0, 0, 81, 52, 44, 10 };
// Penalties for enemy's safe checks
constexpr int QueenSafeCheck = 780;
// RookOnFile[semiopen/open] contains bonuses for each rook when there is
// no (friendly) pawn on the rook file.
- constexpr Score RookOnFile[] = { S(18, 7), S(44, 20) };
+ constexpr Score RookOnFile[] = { S(21, 4), S(47, 25) };
// ThreatByMinor/ByRook[attacked PieceType] contains bonuses according to
// which piece type attacks which one. Attacks on lesser pieces which are
// pawn-defended are not considered.
constexpr Score ThreatByMinor[PIECE_TYPE_NB] = {
- S(0, 0), S(0, 31), S(39, 42), S(57, 44), S(68, 112), S(62, 120)
+ S(0, 0), S(6, 32), S(59, 41), S(79, 56), S(90, 119), S(79, 161)
};
constexpr Score ThreatByRook[PIECE_TYPE_NB] = {
- S(0, 0), S(0, 24), S(38, 71), S(38, 61), S(0, 38), S(51, 38)
+ S(0, 0), S(3, 44), S(38, 71), S(38, 61), S(0, 38), S(51, 38)
};
// PassedRank[Rank] contains a bonus according to the rank of a passed pawn
constexpr Score PassedRank[RANK_NB] = {
- S(0, 0), S(5, 18), S(12, 23), S(10, 31), S(57, 62), S(163, 167), S(271, 250)
- };
-
- // PassedFile[File] contains a bonus according to the file of a passed pawn
- constexpr Score PassedFile[FILE_NB] = {
- S( -1, 7), S( 0, 9), S(-9, -8), S(-30,-14),
- S(-30,-14), S(-9, -8), S( 0, 9), S( -1, 7)
+ S(0, 0), S(10, 28), S(17, 33), S(15, 41), S(62, 72), S(168, 177), S(276, 260)
};
// Assorted bonuses and penalties
constexpr Score KnightOnQueen = S( 16, 12);
constexpr Score LongDiagonalBishop = S( 45, 0);
constexpr Score MinorBehindPawn = S( 18, 3);
- constexpr Score Outpost = S( 9, 3);
+ constexpr Score Outpost = S( 32, 10);
+ constexpr Score PassedFile = S( 11, 8);
constexpr Score PawnlessFlank = S( 17, 95);
constexpr Score RestrictedPiece = S( 7, 7);
- constexpr Score RookOnPawn = S( 10, 32);
+ constexpr Score RookOnQueenFile = S( 7, 6);
constexpr Score SliderOnQueen = S( 59, 18);
constexpr Score ThreatByKing = S( 24, 89);
constexpr Score ThreatByPawnPush = S( 48, 39);
- constexpr Score ThreatByRank = S( 13, 0);
constexpr Score ThreatBySafePawn = S(173, 94);
constexpr Score TrappedRook = S( 47, 4);
constexpr Score WeakQueen = S( 49, 15);
- constexpr Score WeakUnopposedPawn = S( 12, 23);
#undef S
template<Color Us> Score passed() const;
template<Color Us> Score space() const;
ScaleFactor scale_factor(Value eg) const;
- Score initiative(Value eg) const;
+ Score initiative(Score score) const;
const Position& pos;
Material::Entry* me;
// color, including x-rays. But diagonal x-rays through pawns are not computed.
Bitboard attackedBy2[COLOR_NB];
- // kingRing[color] are the squares adjacent to the king, plus (only for a
- // king on its first rank) the squares two ranks in front. For instance,
- // if black's king is on g8, kingRing[BLACK] is f8, h8, f7, g7, h7, f6, g6
- // and h6.
+ // kingRing[color] are the squares adjacent to the king plus some other
+ // very near squares, depending on king position.
Bitboard kingRing[COLOR_NB];
// kingAttackersCount[color] is the number of pieces of the given color
void Evaluation<T>::initialize() {
constexpr Color Them = (Us == WHITE ? BLACK : WHITE);
- constexpr Direction Up = (Us == WHITE ? NORTH : SOUTH);
- constexpr Direction Down = (Us == WHITE ? SOUTH : NORTH);
- constexpr Bitboard LowRanks = (Us == WHITE ? Rank2BB | Rank3BB: Rank7BB | Rank6BB);
+ constexpr Direction Up = pawn_push(Us);
+ constexpr Direction Down = -Up;
+ constexpr Bitboard LowRanks = (Us == WHITE ? Rank2BB | Rank3BB : Rank7BB | Rank6BB);
const Square ksq = pos.square<KING>(Us);
attackedBy2[Us] = dblAttackByPawn | (attackedBy[Us][KING] & attackedBy[Us][PAWN]);
// Init our king safety tables
- kingRing[Us] = attackedBy[Us][KING];
- if (relative_rank(Us, ksq) == RANK_1)
- kingRing[Us] |= shift<Up>(kingRing[Us]);
-
- if (file_of(ksq) == FILE_H)
- kingRing[Us] |= shift<WEST>(kingRing[Us]);
-
- else if (file_of(ksq) == FILE_A)
- kingRing[Us] |= shift<EAST>(kingRing[Us]);
+ Square s = make_square(clamp(file_of(ksq), FILE_B, FILE_G),
+ clamp(rank_of(ksq), RANK_2, RANK_7));
+ kingRing[Us] = PseudoAttacks[KING][s] | s;
kingAttackersCount[Them] = popcount(kingRing[Us] & pe->pawn_attacks(Them));
kingAttacksCount[Them] = kingAttackersWeight[Them] = 0;
Score Evaluation<T>::pieces() {
constexpr Color Them = (Us == WHITE ? BLACK : WHITE);
- constexpr Direction Down = (Us == WHITE ? SOUTH : NORTH);
+ constexpr Direction Down = -pawn_push(Us);
constexpr Bitboard OutpostRanks = (Us == WHITE ? Rank4BB | Rank5BB | Rank6BB
: Rank5BB | Rank4BB | Rank3BB);
const Square* pl = pos.squares<Pt>(Us);
if (Pt == BISHOP || Pt == KNIGHT)
{
// Bonus if piece is on an outpost square or can reach one
- bb = OutpostRanks & ~pe->pawn_attacks_span(Them);
+ bb = OutpostRanks & attackedBy[Us][PAWN] & ~pe->pawn_attacks_span(Them);
if (bb & s)
- score += Outpost * (Pt == KNIGHT ? 4 : 2)
- * ((attackedBy[Us][PAWN] & s) ? 2 : 1);
+ score += Outpost * (Pt == KNIGHT ? 2 : 1);
- else if (bb &= b & ~pos.pieces(Us))
- score += Outpost * (Pt == KNIGHT ? 2 : 1)
- * ((attackedBy[Us][PAWN] & bb) ? 2 : 1);
+ else if (Pt == KNIGHT && bb & b & ~pos.pieces(Us))
+ score += Outpost;
// Knight and Bishop bonus for being right behind a pawn
if (shift<Down>(pos.pieces(PAWN)) & s)
if (Pt == ROOK)
{
- // Bonus for aligning rook with enemy pawns on the same rank/file
- if (relative_rank(Us, s) >= RANK_5)
- score += RookOnPawn * popcount(pos.pieces(Them, PAWN) & PseudoAttacks[ROOK][s]);
+ // Bonus for rook on the same file as a queen
+ if (file_bb(s) & pos.pieces(QUEEN))
+ score += RookOnQueenFile;
// Bonus for rook on an open or semi-open file
if (pos.is_on_semiopen_file(Us, s))
- score += RookOnFile[bool(pos.is_on_semiopen_file(Them, s))];
+ score += RookOnFile[pos.is_on_semiopen_file(Them, s)];
// Penalty when trapped by the king, even more if the king cannot castle
else if (mob <= 3)
else
unsafeChecks |= knightChecks;
- // Unsafe or occupied checking squares will also be considered, as long as
- // the square is in the attacker's mobility area.
- unsafeChecks &= mobilityArea[Them];
-
// Find the squares that opponent attacks in our king flank, and the squares
// which are attacked twice in that flank.
b1 = attackedBy[Them][ALL_PIECES] & KingFlank[file_of(ksq)] & Camp;
int kingFlankAttacks = popcount(b1) + popcount(b2);
kingDanger += kingAttackersCount[Them] * kingAttackersWeight[Them]
- + 69 * kingAttacksCount[Them]
+ 185 * popcount(kingRing[Us] & weak)
+ + 148 * popcount(unsafeChecks)
+ + 98 * popcount(pos.blockers_for_king(Us))
+ + 69 * kingAttacksCount[Them]
+ + 3 * kingFlankAttacks * kingFlankAttacks / 8
+ + mg_value(mobility[Them] - mobility[Us])
+ - 873 * !pos.count<QUEEN>(Them)
- 100 * bool(attackedBy[Us][KNIGHT] & attackedBy[Us][KING])
- 35 * bool(attackedBy[Us][BISHOP] & attackedBy[Us][KING])
- + 150 * popcount(pos.blockers_for_king(Us) | unsafeChecks)
- - 873 * !pos.count<QUEEN>(Them)
- 6 * mg_value(score) / 8
- + mg_value(mobility[Them] - mobility[Us])
- + 5 * kingFlankAttacks * kingFlankAttacks / 16
- 7;
// Transform the kingDanger units into a Score, and subtract it from the evaluation
Score Evaluation<T>::threats() const {
constexpr Color Them = (Us == WHITE ? BLACK : WHITE);
- constexpr Direction Up = (Us == WHITE ? NORTH : SOUTH);
+ constexpr Direction Up = pawn_push(Us);
constexpr Bitboard TRank3BB = (Us == WHITE ? Rank3BB : Rank6BB);
Bitboard b, weak, defended, nonPawnEnemies, stronglyProtected, safe;
// Enemies not strongly protected and under our attack
weak = pos.pieces(Them) & ~stronglyProtected & attackedBy[Us][ALL_PIECES];
- // Safe or protected squares
- safe = ~attackedBy[Them][ALL_PIECES] | attackedBy[Us][ALL_PIECES];
-
// Bonus according to the kind of attacking pieces
if (defended | weak)
{
b = (defended | weak) & (attackedBy[Us][KNIGHT] | attackedBy[Us][BISHOP]);
while (b)
- {
- Square s = pop_lsb(&b);
- score += ThreatByMinor[type_of(pos.piece_on(s))];
- if (type_of(pos.piece_on(s)) != PAWN)
- score += ThreatByRank * (int)relative_rank(Them, s);
- }
+ score += ThreatByMinor[type_of(pos.piece_on(pop_lsb(&b)))];
b = weak & attackedBy[Us][ROOK];
while (b)
- {
- Square s = pop_lsb(&b);
- score += ThreatByRook[type_of(pos.piece_on(s))];
- if (type_of(pos.piece_on(s)) != PAWN)
- score += ThreatByRank * (int)relative_rank(Them, s);
- }
+ score += ThreatByRook[type_of(pos.piece_on(pop_lsb(&b)))];
if (weak & attackedBy[Us][KING])
score += ThreatByKing;
score += RestrictedPiece * popcount(b);
- // Bonus for enemy unopposed weak pawns
- if (pos.pieces(Us, ROOK, QUEEN))
- score += WeakUnopposedPawn * pe->weak_unopposed(Them);
+ // Protected or unattacked squares
+ safe = ~attackedBy[Them][ALL_PIECES] | attackedBy[Us][ALL_PIECES];
+
+ // Bonus for attacking enemy pieces with our relatively safe pawns
+ b = pos.pieces(Us, PAWN) & safe;
+ b = pawn_attacks_bb<Us>(b) & nonPawnEnemies;
+ score += ThreatBySafePawn * popcount(b);
// Find squares where our pawns can push on the next move
b = shift<Up>(pos.pieces(Us, PAWN)) & ~pos.pieces();
b &= ~attackedBy[Them][PAWN] & safe;
// Bonus for safe pawn threats on the next move
- b = pawn_attacks_bb<Us>(b) & pos.pieces(Them);
- score += ThreatByPawnPush * popcount(b);
-
- // Our safe or protected pawns
- b = pos.pieces(Us, PAWN) & safe;
-
b = pawn_attacks_bb<Us>(b) & nonPawnEnemies;
- score += ThreatBySafePawn * popcount(b);
+ score += ThreatByPawnPush * popcount(b);
// Bonus for threats on the next moves against enemy queen
if (pos.count<QUEEN>(Them) == 1)
Score Evaluation<T>::passed() const {
constexpr Color Them = (Us == WHITE ? BLACK : WHITE);
- constexpr Direction Up = (Us == WHITE ? NORTH : SOUTH);
+ constexpr Direction Up = pawn_push(Us);
auto king_proximity = [&](Color c, Square s) {
return std::min(distance(pos.square<KING>(c), s), 5);
};
- Bitboard b, bb, squaresToQueen, defendedSquares, unsafeSquares;
+ Bitboard b, bb, squaresToQueen, unsafeSquares;
Score score = SCORE_ZERO;
b = pe->passed_pawns(Us);
if (r > RANK_3)
{
- int w = (r-2) * (r-2) + 2;
+ int w = 5 * r - 13;
Square blockSq = s + Up;
// Adjust bonus based on the king's proximity
// If the pawn is free to advance, then increase the bonus
if (pos.empty(blockSq))
{
- // 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_file_bb(Us, s);
+ squaresToQueen = forward_file_bb(Us, s);
+ unsafeSquares = passed_pawn_span(Us, s);
bb = forward_file_bb(Them, s) & pos.pieces(ROOK, QUEEN);
- if (!(pos.pieces(Us) & bb))
- defendedSquares &= attackedBy[Us][ALL_PIECES];
-
if (!(pos.pieces(Them) & bb))
- unsafeSquares &= attackedBy[Them][ALL_PIECES] | pos.pieces(Them);
+ unsafeSquares &= attackedBy[Them][ALL_PIECES];
- // If there aren't any enemy attacks, assign a big bonus. Otherwise
- // assign a smaller bonus if the block square isn't attacked.
- int k = !unsafeSquares ? 20 : !(unsafeSquares & blockSq) ? 9 : 0;
+ // If there are no enemy attacks on passed pawn span, assign a big bonus.
+ // Otherwise assign a smaller bonus if the path to queen is not attacked
+ // and even smaller bonus if it is attacked but block square is not.
+ int k = !unsafeSquares ? 35 :
+ !(unsafeSquares & squaresToQueen) ? 20 :
+ !(unsafeSquares & blockSq) ? 9 :
+ 0 ;
- // If the path to the queen is fully defended, assign a big bonus.
- // Otherwise assign a smaller bonus if the block square is defended.
- if (defendedSquares == squaresToQueen)
- k += 6;
-
- else if (defendedSquares & blockSq)
- k += 4;
+ // Assign a larger bonus if the block square is defended
+ if ((pos.pieces(Us) & bb) || (attackedBy[Us][ALL_PIECES] & blockSq))
+ k += 5;
bonus += make_score(k * w, k * w);
}
// Scale down bonus for candidate passers which need more than one
// pawn push to become passed, or have a pawn in front of them.
if ( !pos.pawn_passed(Us, s + Up)
- || (pos.pieces(PAWN) & forward_file_bb(Us, s)))
+ || (pos.pieces(PAWN) & (s + Up)))
bonus = bonus / 2;
- score += bonus + PassedFile[file_of(s)];
+ score += bonus - PassedFile * map_to_queenside(file_of(s));
}
if (T)
return SCORE_ZERO;
constexpr Color Them = (Us == WHITE ? BLACK : WHITE);
- constexpr Direction Down = (Us == WHITE ? SOUTH : NORTH);
+ constexpr Direction Down = -pawn_push(Us);
constexpr Bitboard SpaceMask =
Us == WHITE ? CenterFiles & (Rank2BB | Rank3BB | Rank4BB)
: CenterFiles & (Rank7BB | Rank6BB | Rank5BB);
behind |= shift<Down>(behind);
behind |= shift<Down+Down>(behind);
- int bonus = popcount(safe) + popcount(behind & safe);
+ int bonus = popcount(safe) + popcount(behind & safe & ~attackedBy[Them][ALL_PIECES]);
int weight = pos.count<ALL_PIECES>(Us) - 1;
Score score = make_score(bonus * weight * weight / 16, 0);
// known attacking/defending status of the players.
template<Tracing T>
- Score Evaluation<T>::initiative(Value eg) const {
+ Score Evaluation<T>::initiative(Score score) const {
+
+ Value mg = mg_value(score);
+ Value eg = eg_value(score);
int outflanking = distance<File>(pos.square<KING>(WHITE), pos.square<KING>(BLACK))
- distance<Rank>(pos.square<KING>(WHITE), pos.square<KING>(BLACK));
bool pawnsOnBothFlanks = (pos.pieces(PAWN) & QueenSide)
&& (pos.pieces(PAWN) & KingSide);
+ bool almostUnwinnable = !pe->passed_count()
+ && outflanking < 0
+ && !pawnsOnBothFlanks;
+
// Compute the initiative bonus for the attacking side
int complexity = 9 * pe->passed_count()
+ 11 * pos.count<PAWN>()
+ 9 * outflanking
- + 18 * pawnsOnBothFlanks
- + 49 * !pos.non_pawn_material()
- -103 ;
-
- // Now apply the bonus: note that we find the attacking side by extracting
- // the sign of the endgame value, and that we carefully cap the bonus so
- // that the endgame score will never change sign after the bonus.
+ + 21 * pawnsOnBothFlanks
+ + 51 * !pos.non_pawn_material()
+ - 43 * almostUnwinnable
+ - 95 ;
+
+ // Now apply the bonus: note that we find the attacking side by extracting the
+ // sign of the midgame or endgame values, and that we carefully cap the bonus
+ // so that the midgame and endgame scores do not change sign after the bonus.
+ int u = ((mg > 0) - (mg < 0)) * std::max(std::min(complexity + 50, 0), -abs(mg));
int v = ((eg > 0) - (eg < 0)) * std::max(complexity, -abs(eg));
if (T)
- Trace::add(INITIATIVE, make_score(0, v));
+ Trace::add(INITIATIVE, make_score(u, v));
- return make_score(0, v);
+ return make_score(u, v);
}
&& pos.non_pawn_material() == 2 * BishopValueMg)
sf = 16 + 4 * pe->passed_count();
else
- sf = std::min(40 + (pos.opposite_bishops() ? 2 : 7) * pos.count<PAWN>(strongSide), sf);
+ sf = std::min(sf, 36 + (pos.opposite_bishops() ? 2 : 7) * pos.count<PAWN>(strongSide));
+ sf = std::max(0, sf - (pos.rule50_count() - 12) / 4);
}
return ScaleFactor(sf);
// Early exit if score is high
Value v = (mg_value(score) + eg_value(score)) / 2;
- if (abs(v) > (LazyThreshold + pos.non_pawn_material() / 64))
+ if (abs(v) > LazyThreshold + pos.non_pawn_material() / 64)
return pos.side_to_move() == WHITE ? v : -v;
// Main evaluation begins here
+ passed< WHITE>() - passed< BLACK>()
+ space< WHITE>() - space< BLACK>();
- score += initiative(eg_value(score));
+ score += initiative(score);
// Interpolate between a middlegame and a (scaled by 'sf') endgame score
ScaleFactor sf = scale_factor(eg_value(score));