// Tables used to drive a piece towards or away from another piece
const int PushClose[8] = { 0, 0, 100, 80, 60, 40, 20, 10 };
- const int PushAway [8] = { 0, 10, 14, 20, 30, 42, 58, 80 };
+ const int PushAway [8] = { 0, 5, 20, 40, 60, 80, 90, 100 };
+
+#ifndef NDEBUG
+ bool verify_material(const Position& pos, Color c, Value npm, int num_pawns) {
+ return pos.non_pawn_material(c) == npm && pos.count<PAWN>(c) == num_pawns;
+ }
+#endif
// Get the material key of a Position out of the given endgame key code
// like "KBPKN". The trick here is to first forge an ad-hoc fen string
add<KNPK>("KNPK");
add<KNPKB>("KNPKB");
add<KRPKR>("KRPKR");
+ add<KRPKB>("KRPKB");
add<KBPKB>("KBPKB");
add<KBPKN>("KBPKN");
add<KBPPKB>("KBPPKB");
template<>
Value Endgame<KXK>::operator()(const Position& pos) const {
- assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO);
- assert(!pos.count<PAWN>(weakerSide));
+ assert(verify_material(pos, weakerSide, VALUE_ZERO, 0));
assert(!pos.checkers()); // Eval is never called when in check
// Stalemate detection with lone king
Square loserKSq = pos.king_square(weakerSide);
Value result = pos.non_pawn_material(strongerSide)
- + pos.count<PAWN>(strongerSide) * PawnValueEg
- + PushToEdges[loserKSq]
- + PushClose[square_distance(winnerKSq, loserKSq)];
+ + pos.count<PAWN>(strongerSide) * PawnValueEg
+ + PushToEdges[loserKSq]
+ + PushClose[square_distance(winnerKSq, loserKSq)];
if ( pos.count<QUEEN>(strongerSide)
|| pos.count<ROOK>(strongerSide)
template<>
Value Endgame<KBNK>::operator()(const Position& pos) const {
- assert(pos.non_pawn_material(strongerSide) == KnightValueMg + BishopValueMg);
- assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO);
- assert(pos.count<BISHOP>(strongerSide) == 1);
- assert(pos.count<KNIGHT>(strongerSide) == 1);
- assert(pos.count< PAWN>(strongerSide) == 0);
- assert(pos.count< PAWN>(weakerSide ) == 0);
+ assert(verify_material(pos, strongerSide, KnightValueMg + BishopValueMg, 0));
+ assert(verify_material(pos, weakerSide, VALUE_ZERO, 0));
Square winnerKSq = pos.king_square(strongerSide);
Square loserKSq = pos.king_square(weakerSide);
template<>
Value Endgame<KPK>::operator()(const Position& pos) const {
- assert(pos.non_pawn_material(strongerSide) == VALUE_ZERO);
- assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO);
- assert(pos.count<PAWN>(strongerSide) == 1);
- assert(pos.count<PAWN>(weakerSide ) == 0);
+ assert(verify_material(pos, strongerSide, VALUE_ZERO, 1));
+ assert(verify_material(pos, weakerSide, VALUE_ZERO, 0));
Square wksq = pos.king_square(strongerSide);
Square bksq = pos.king_square(weakerSide);
template<>
Value Endgame<KRKP>::operator()(const Position& pos) const {
- assert(pos.non_pawn_material(strongerSide) == RookValueMg);
- assert(pos.non_pawn_material(weakerSide) == 0);
- assert(pos.count<PAWN>(strongerSide) == 0);
- assert(pos.count<PAWN>(weakerSide ) == 1);
+ assert(verify_material(pos, strongerSide, RookValueMg, 0));
+ assert(verify_material(pos, weakerSide, VALUE_ZERO, 1));
Square wksq = pos.king_square(strongerSide);
Square bksq = pos.king_square(weakerSide);
template<>
Value Endgame<KRKB>::operator()(const Position& pos) const {
- assert(pos.non_pawn_material(strongerSide) == RookValueMg);
- assert(pos.non_pawn_material(weakerSide ) == BishopValueMg);
- assert(pos.count<BISHOP>(weakerSide ) == 1);
- assert(pos.count< PAWN>(weakerSide ) == 0);
- assert(pos.count< PAWN>(strongerSide) == 0);
+ assert(verify_material(pos, strongerSide, RookValueMg, 0));
+ assert(verify_material(pos, weakerSide, BishopValueMg, 0));
Value result = Value(PushToEdges[pos.king_square(weakerSide)]);
return strongerSide == pos.side_to_move() ? result : -result;
template<>
Value Endgame<KRKN>::operator()(const Position& pos) const {
- assert(pos.non_pawn_material(strongerSide) == RookValueMg);
- assert(pos.non_pawn_material(weakerSide ) == KnightValueMg);
- assert(pos.count<KNIGHT>(weakerSide ) == 1);
- assert(pos.count< PAWN>(weakerSide ) == 0);
- assert(pos.count< PAWN>(strongerSide) == 0);
+ assert(verify_material(pos, strongerSide, RookValueMg, 0));
+ assert(verify_material(pos, weakerSide, KnightValueMg, 0));
Square bksq = pos.king_square(weakerSide);
Square bnsq = pos.list<KNIGHT>(weakerSide)[0];
template<>
Value Endgame<KQKP>::operator()(const Position& pos) const {
- assert(pos.non_pawn_material(strongerSide) == QueenValueMg);
- assert(pos.non_pawn_material(weakerSide ) == VALUE_ZERO);
- assert(pos.count<PAWN>(strongerSide) == 0);
- assert(pos.count<PAWN>(weakerSide ) == 1);
+ assert(verify_material(pos, strongerSide, QueenValueMg, 0));
+ assert(verify_material(pos, weakerSide, VALUE_ZERO, 1));
Square winnerKSq = pos.king_square(strongerSide);
Square loserKSq = pos.king_square(weakerSide);
template<>
Value Endgame<KQKR>::operator()(const Position& pos) const {
- assert(pos.non_pawn_material(strongerSide) == QueenValueMg);
- assert(pos.non_pawn_material(weakerSide ) == RookValueMg);
- assert(pos.count<PAWN>(strongerSide) == 0);
- assert(pos.count<PAWN>(weakerSide ) == 0);
+ assert(verify_material(pos, strongerSide, QueenValueMg, 0));
+ assert(verify_material(pos, weakerSide, RookValueMg, 0));
Square winnerKSq = pos.king_square(strongerSide);
Square loserKSq = pos.king_square(weakerSide);
return strongerSide == pos.side_to_move() ? result : -result;
}
+
+/// KBB vs KN. This is almost always a win. We try to push enemy king to a corner
+/// and away from his knight. For a reference of this difficult endgame see:
+/// en.wikipedia.org/wiki/Chess_endgame#Effect_of_tablebases_on_endgame_theory
+
template<>
Value Endgame<KBBKN>::operator()(const Position& pos) const {
- assert(pos.non_pawn_material(strongerSide) == 2 * BishopValueMg);
- assert(pos.non_pawn_material(weakerSide ) == KnightValueMg);
- assert(pos.count<BISHOP>(strongerSide) == 2);
- assert(pos.count<KNIGHT>(weakerSide ) == 1);
- assert(!pos.pieces(PAWN));
+ assert(verify_material(pos, strongerSide, 2 * BishopValueMg, 0));
+ assert(verify_material(pos, weakerSide, KnightValueMg, 0));
- Square wksq = pos.king_square(strongerSide);
- Square bksq = pos.king_square(weakerSide);
- Square nsq = pos.list<KNIGHT>(weakerSide)[0];
+ Square winnerKSq = pos.king_square(strongerSide);
+ Square loserKSq = pos.king_square(weakerSide);
+ Square knightSq = pos.list<KNIGHT>(weakerSide)[0];
- Value result = BishopValueEg
- + PushClose[square_distance(wksq, bksq)]
- + square_distance(bksq, nsq) * 32
- + (8 - popcount<Max15>(pos.attacks_from<KNIGHT>(nsq))) * 8;
+ Value result = VALUE_KNOWN_WIN
+ + PushToCorners[loserKSq]
+ + PushClose[square_distance(winnerKSq, loserKSq)]
+ + PushAway[square_distance(loserKSq, knightSq)];
return strongerSide == pos.side_to_move() ? result : -result;
}
ScaleFactor Endgame<KBPsK>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == BishopValueMg);
- assert(pos.count<BISHOP>(strongerSide) == 1);
- assert(pos.count< PAWN>(strongerSide) >= 1);
+ assert(pos.count<PAWN>(strongerSide) >= 1);
// No assertions about the material of weakerSide, because we want draws to
// be detected even when the weaker side has some pawns.
Square weakerKingSq = pos.king_square(weakerSide);
Square bishopSq = pos.list<BISHOP>(strongerSide)[0];
- // Draw if weaker pawn is on rank 7, bishop can't attack the pawn, and
- // weaker king can stop opposing opponent's king from penetrating.
+ // Potential for a draw if our pawn is blocked on the 7th rank
+ // the bishop cannot attack it or they only have one pawn left
if ( relative_rank(strongerSide, weakerPawnSq) == RANK_7
- && opposite_colors(bishopSq, weakerPawnSq)
- && square_distance(weakerPawnSq, weakerKingSq) <= square_distance(weakerPawnSq, strongerKingSq))
- return SCALE_FACTOR_DRAW;
+ && (pos.pieces(strongerSide, PAWN) & (weakerPawnSq + pawn_push(weakerSide)))
+ && (opposite_colors(bishopSq, weakerPawnSq) || pos.count<PAWN>(strongerSide) == 1))
+ {
+ int strongerKingDist = square_distance(weakerPawnSq, strongerKingSq);
+ int weakerKingDist = square_distance(weakerPawnSq, weakerKingSq);
+
+ // Draw if the weak king is on it's back two ranks, within 2
+ // squares of the blocking pawn and the strong king is not
+ // closer. (I think this rule only fails in practically
+ // unreachable positions such as 5k1K/6p1/6P1/8/8/3B4/8/8 w
+ // and positions where qsearch will immediately correct the
+ // problem such as 8/4k1p1/6P1/1K6/3B4/8/8/8 w)
+ if ( relative_rank(strongerSide, weakerKingSq) >= RANK_7
+ && weakerKingDist <= 2
+ && weakerKingDist <= strongerKingDist)
+ return SCALE_FACTOR_DRAW;
+ }
}
return SCALE_FACTOR_NONE;
template<>
ScaleFactor Endgame<KQKRPs>::operator()(const Position& pos) const {
- assert(pos.non_pawn_material(strongerSide) == QueenValueMg);
- assert(pos.count<QUEEN>(strongerSide) == 1);
- assert(pos.count< PAWN>(strongerSide) == 0);
- assert(pos.count< ROOK>(weakerSide ) == 1);
- assert(pos.count< PAWN>(weakerSide ) >= 1);
+ assert(verify_material(pos, strongerSide, QueenValueMg, 0));
+ assert(pos.count<ROOK>(weakerSide) == 1);
+ assert(pos.count<PAWN>(weakerSide) >= 1);
Square kingSq = pos.king_square(weakerSide);
Square rsq = pos.list<ROOK>(weakerSide)[0];
template<>
ScaleFactor Endgame<KRPKR>::operator()(const Position& pos) const {
- assert(pos.non_pawn_material(strongerSide) == RookValueMg);
- assert(pos.non_pawn_material(weakerSide) == RookValueMg);
- assert(pos.count<PAWN>(strongerSide) == 1);
- assert(pos.count<PAWN>(weakerSide ) == 0);
+ assert(verify_material(pos, strongerSide, RookValueMg, 1));
+ assert(verify_material(pos, weakerSide, RookValueMg, 0));
Square wksq = pos.king_square(strongerSide);
Square bksq = pos.king_square(weakerSide);
return SCALE_FACTOR_NONE;
}
+template<>
+ScaleFactor Endgame<KRPKB>::operator()(const Position& pos) const {
+
+ assert(verify_material(pos, strongerSide, RookValueMg, 1));
+ assert(verify_material(pos, weakerSide, BishopValueMg, 0));
+
+ // Test for a rook pawn
+ if (pos.pieces(PAWN) & (FileABB | FileHBB))
+ {
+ Square ksq = pos.king_square(weakerSide);
+ Square bsq = pos.list<BISHOP>(weakerSide)[0];
+ Square psq = pos.list<PAWN>(strongerSide)[0];
+ Rank rk = relative_rank(strongerSide, psq);
+ Square push = pawn_push(strongerSide);
+
+ // If the pawn is on the 5th rank and the pawn (currently) is on
+ // the same color square as the bishop then there is a chance of
+ // a fortress. Depending on the king position give a moderate
+ // reduction or a stronger one if the defending king is near the
+ // corner but not trapped there.
+ if (rk == RANK_5 && !opposite_colors(bsq, psq))
+ {
+ int d = square_distance(psq + 3 * push, ksq);
+
+ if (d <= 2 && !(d == 0 && ksq == pos.king_square(strongerSide) + 2 * push))
+ return ScaleFactor(24);
+ else
+ return ScaleFactor(48);
+ }
+
+ // When the pawn has moved to the 6th rank we can be fairly sure
+ // it's drawn if the bishop attacks the square in front of the
+ // pawn from a reasonable distance and the defending king is near
+ // the corner
+ if ( rk == RANK_6
+ && square_distance(psq + 2 * push, ksq) <= 1
+ && (PseudoAttacks[BISHOP][bsq] & (psq + push))
+ && file_distance(bsq, psq) >= 2)
+ return ScaleFactor(8);
+ }
+
+ return SCALE_FACTOR_NONE;
+}
/// K, rook and two pawns vs K, rook and one pawn. There is only a single
/// pattern: If the stronger side has no passed pawns and the defending king
template<>
ScaleFactor Endgame<KRPPKRP>::operator()(const Position& pos) const {
- assert(pos.non_pawn_material(strongerSide) == RookValueMg);
- assert(pos.non_pawn_material(weakerSide) == RookValueMg);
- assert(pos.count<PAWN>(strongerSide) == 2);
- assert(pos.count<PAWN>(weakerSide ) == 1);
+ assert(verify_material(pos, strongerSide, RookValueMg, 2));
+ assert(verify_material(pos, weakerSide, RookValueMg, 1));
Square wpsq1 = pos.list<PAWN>(strongerSide)[0];
Square wpsq2 = pos.list<PAWN>(strongerSide)[1];
Square bksq = pos.king_square(weakerSide);
// Does the stronger side have a passed pawn?
- if ( pos.pawn_is_passed(strongerSide, wpsq1)
- || pos.pawn_is_passed(strongerSide, wpsq2))
+ if (pos.pawn_passed(strongerSide, wpsq1) || pos.pawn_passed(strongerSide, wpsq2))
return SCALE_FACTOR_NONE;
Rank r = std::max(relative_rank(strongerSide, wpsq1), relative_rank(strongerSide, wpsq2));
ScaleFactor Endgame<KPsK>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == VALUE_ZERO);
- assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO);
assert(pos.count<PAWN>(strongerSide) >= 2);
- assert(pos.count<PAWN>(weakerSide ) == 0);
+ assert(verify_material(pos, weakerSide, VALUE_ZERO, 0));
Square ksq = pos.king_square(weakerSide);
Bitboard pawns = pos.pieces(strongerSide, PAWN);
template<>
ScaleFactor Endgame<KBPKB>::operator()(const Position& pos) const {
- assert(pos.non_pawn_material(strongerSide) == BishopValueMg);
- assert(pos.non_pawn_material(weakerSide ) == BishopValueMg);
- assert(pos.count<BISHOP>(strongerSide) == 1);
- assert(pos.count<BISHOP>(weakerSide ) == 1);
- assert(pos.count< PAWN>(strongerSide) == 1);
- assert(pos.count< PAWN>(weakerSide ) == 0);
+ assert(verify_material(pos, strongerSide, BishopValueMg, 1));
+ assert(verify_material(pos, weakerSide, BishopValueMg, 0));
Square pawnSq = pos.list<PAWN>(strongerSide)[0];
Square strongerBishopSq = pos.list<BISHOP>(strongerSide)[0];
template<>
ScaleFactor Endgame<KBPPKB>::operator()(const Position& pos) const {
- assert(pos.non_pawn_material(strongerSide) == BishopValueMg);
- assert(pos.non_pawn_material(weakerSide ) == BishopValueMg);
- assert(pos.count<BISHOP>(strongerSide) == 1);
- assert(pos.count<BISHOP>(weakerSide ) == 1);
- assert(pos.count< PAWN>(strongerSide) == 2);
- assert(pos.count< PAWN>(weakerSide ) == 0);
+ assert(verify_material(pos, strongerSide, BishopValueMg, 2));
+ assert(verify_material(pos, weakerSide, BishopValueMg, 0));
Square wbsq = pos.list<BISHOP>(strongerSide)[0];
Square bbsq = pos.list<BISHOP>(weakerSide)[0];
template<>
ScaleFactor Endgame<KBPKN>::operator()(const Position& pos) const {
- assert(pos.non_pawn_material(strongerSide) == BishopValueMg);
- assert(pos.non_pawn_material(weakerSide ) == KnightValueMg);
- assert(pos.count<BISHOP>(strongerSide) == 1);
- assert(pos.count<KNIGHT>(weakerSide ) == 1);
- assert(pos.count< PAWN>(strongerSide) == 1);
- assert(pos.count< PAWN>(weakerSide ) == 0);
+ assert(verify_material(pos, strongerSide, BishopValueMg, 1));
+ assert(verify_material(pos, weakerSide, KnightValueMg, 0));
Square pawnSq = pos.list<PAWN>(strongerSide)[0];
Square strongerBishopSq = pos.list<BISHOP>(strongerSide)[0];
template<>
ScaleFactor Endgame<KNPK>::operator()(const Position& pos) const {
- assert(pos.non_pawn_material(strongerSide) == KnightValueMg);
- assert(pos.non_pawn_material(weakerSide ) == VALUE_ZERO);
- assert(pos.count<KNIGHT>(strongerSide) == 1);
- assert(pos.count< PAWN>(strongerSide) == 1);
- assert(pos.count< PAWN>(weakerSide ) == 0);
+ assert(verify_material(pos, strongerSide, KnightValueMg, 1));
+ assert(verify_material(pos, weakerSide, VALUE_ZERO, 0));
Square pawnSq = pos.list<PAWN>(strongerSide)[0];
Square weakerKingSq = pos.king_square(weakerSide);
template<>
ScaleFactor Endgame<KPKP>::operator()(const Position& pos) const {
- assert(pos.non_pawn_material(strongerSide) == VALUE_ZERO);
- assert(pos.non_pawn_material(weakerSide ) == VALUE_ZERO);
- assert(pos.count<PAWN>(WHITE) == 1);
- assert(pos.count<PAWN>(BLACK) == 1);
+ assert(verify_material(pos, strongerSide, VALUE_ZERO, 1));
+ assert(verify_material(pos, weakerSide, VALUE_ZERO, 1));
Square wksq = pos.king_square(strongerSide);
Square bksq = pos.king_square(weakerSide);