X-Git-Url: https://git.sesse.net/?p=stockfish;a=blobdiff_plain;f=src%2Fendgame.cpp;h=5906b4d59187aecd8604e191bba119591d37a995;hp=e45f9ed3fe34ddfb357882ffccddc8f89f75ff76;hb=2bf18bfc6396ae7292f57fc021b390fc05cd0f95;hpb=5e8bc6ac2adf76b25042194717b04218d7ac2390 diff --git a/src/endgame.cpp b/src/endgame.cpp index e45f9ed3..5906b4d5 100644 --- a/src/endgame.cpp +++ b/src/endgame.cpp @@ -57,7 +57,13 @@ namespace { // 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(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 @@ -68,8 +74,8 @@ namespace { assert(code.length() > 0 && code.length() < 8); assert(code[0] == 'K'); - string sides[] = { code.substr(code.find('K', 1)), // Weaker - code.substr(0, code.find('K', 1)) }; // Stronger + string sides[] = { code.substr(code.find('K', 1)), // Weak + code.substr(0, code.find('K', 1)) }; // Strong std::transform(sides[c].begin(), sides[c].end(), sides[c].begin(), tolower); @@ -102,6 +108,7 @@ Endgames::Endgames() { add("KNPK"); add("KNPKB"); add("KRPKR"); + add("KRPKB"); add("KBPKB"); add("KBPKN"); add("KBPPKB"); @@ -129,28 +136,27 @@ void Endgames::add(const string& code) { template<> Value Endgame::operator()(const Position& pos) const { - assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO); - assert(!pos.count(weakerSide)); + assert(verify_material(pos, weakSide, VALUE_ZERO, 0)); assert(!pos.checkers()); // Eval is never called when in check // Stalemate detection with lone king - if (pos.side_to_move() == weakerSide && !MoveList(pos).size()) + if (pos.side_to_move() == weakSide && !MoveList(pos).size()) return VALUE_DRAW; - Square winnerKSq = pos.king_square(strongerSide); - Square loserKSq = pos.king_square(weakerSide); + Square winnerKSq = pos.king_square(strongSide); + Square loserKSq = pos.king_square(weakSide); - Value result = pos.non_pawn_material(strongerSide) - + pos.count(strongerSide) * PawnValueEg - + PushToEdges[loserKSq] - + PushClose[square_distance(winnerKSq, loserKSq)]; + Value result = pos.non_pawn_material(strongSide) + + pos.count(strongSide) * PawnValueEg + + PushToEdges[loserKSq] + + PushClose[square_distance(winnerKSq, loserKSq)]; - if ( pos.count(strongerSide) - || pos.count(strongerSide) - || pos.bishop_pair(strongerSide)) + if ( pos.count(strongSide) + || pos.count(strongSide) + || pos.bishop_pair(strongSide)) result += VALUE_KNOWN_WIN; - return strongerSide == pos.side_to_move() ? result : -result; + return strongSide == pos.side_to_move() ? result : -result; } @@ -159,16 +165,12 @@ Value Endgame::operator()(const Position& pos) const { template<> Value Endgame::operator()(const Position& pos) const { - assert(pos.non_pawn_material(strongerSide) == KnightValueMg + BishopValueMg); - assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO); - assert(pos.count(strongerSide) == 1); - assert(pos.count(strongerSide) == 1); - assert(pos.count< PAWN>(strongerSide) == 0); - assert(pos.count< PAWN>(weakerSide ) == 0); + assert(verify_material(pos, strongSide, KnightValueMg + BishopValueMg, 0)); + assert(verify_material(pos, weakSide, VALUE_ZERO, 0)); - Square winnerKSq = pos.king_square(strongerSide); - Square loserKSq = pos.king_square(weakerSide); - Square bishopSq = pos.list(strongerSide)[0]; + Square winnerKSq = pos.king_square(strongSide); + Square loserKSq = pos.king_square(weakSide); + Square bishopSq = pos.list(strongSide)[0]; // kbnk_mate_table() tries to drive toward corners A1 or H8, // if we have a bishop that cannot reach the above squares we @@ -183,7 +185,7 @@ Value Endgame::operator()(const Position& pos) const { + PushClose[square_distance(winnerKSq, loserKSq)] + PushToCorners[loserKSq]; - return strongerSide == pos.side_to_move() ? result : -result; + return strongSide == pos.side_to_move() ? result : -result; } @@ -191,17 +193,15 @@ Value Endgame::operator()(const Position& pos) const { template<> Value Endgame::operator()(const Position& pos) const { - assert(pos.non_pawn_material(strongerSide) == VALUE_ZERO); - assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO); - assert(pos.count(strongerSide) == 1); - assert(pos.count(weakerSide ) == 0); + assert(verify_material(pos, strongSide, VALUE_ZERO, 1)); + assert(verify_material(pos, weakSide, VALUE_ZERO, 0)); - Square wksq = pos.king_square(strongerSide); - Square bksq = pos.king_square(weakerSide); - Square psq = pos.list(strongerSide)[0]; + Square wksq = pos.king_square(strongSide); + Square bksq = pos.king_square(weakSide); + Square psq = pos.list(strongSide)[0]; Color us = pos.side_to_move(); - if (strongerSide == BLACK) + if (strongSide == BLACK) { wksq = ~wksq; bksq = ~bksq; @@ -221,7 +221,7 @@ Value Endgame::operator()(const Position& pos) const { Value result = VALUE_KNOWN_WIN + PawnValueEg + Value(rank_of(psq)); - return strongerSide == pos.side_to_move() ? result : -result; + return strongSide == pos.side_to_move() ? result : -result; } @@ -232,17 +232,15 @@ Value Endgame::operator()(const Position& pos) const { template<> Value Endgame::operator()(const Position& pos) const { - assert(pos.non_pawn_material(strongerSide) == RookValueMg); - assert(pos.non_pawn_material(weakerSide) == 0); - assert(pos.count(strongerSide) == 0); - assert(pos.count(weakerSide ) == 1); + assert(verify_material(pos, strongSide, RookValueMg, 0)); + assert(verify_material(pos, weakSide, VALUE_ZERO, 1)); - Square wksq = pos.king_square(strongerSide); - Square bksq = pos.king_square(weakerSide); - Square rsq = pos.list(strongerSide)[0]; - Square psq = pos.list(weakerSide)[0]; + Square wksq = pos.king_square(strongSide); + Square bksq = pos.king_square(weakSide); + Square rsq = pos.list(strongSide)[0]; + Square psq = pos.list(weakSide)[0]; - if (strongerSide == BLACK) + if (strongSide == BLACK) { wksq = ~wksq; bksq = ~bksq; @@ -259,7 +257,7 @@ Value Endgame::operator()(const Position& pos) const { // If the weaker side's king is too far from the pawn and the rook, // it's a win. - else if ( square_distance(bksq, psq) >= 3 + (pos.side_to_move() == weakerSide) + else if ( square_distance(bksq, psq) >= 3 + (pos.side_to_move() == weakSide) && square_distance(bksq, rsq) >= 3) result = RookValueEg - Value(square_distance(wksq, psq)); @@ -268,7 +266,7 @@ Value Endgame::operator()(const Position& pos) const { else if ( rank_of(bksq) <= RANK_3 && square_distance(bksq, psq) == 1 && rank_of(wksq) >= RANK_4 - && square_distance(wksq, psq) > 2 + (pos.side_to_move() == strongerSide)) + && square_distance(wksq, psq) > 2 + (pos.side_to_move() == strongSide)) result = Value(80 - square_distance(wksq, psq) * 8); else @@ -277,7 +275,7 @@ Value Endgame::operator()(const Position& pos) const { + Value(square_distance(bksq, psq + DELTA_S) * 8) + Value(square_distance(psq, queeningSq) * 8); - return strongerSide == pos.side_to_move() ? result : -result; + return strongSide == pos.side_to_move() ? result : -result; } @@ -286,14 +284,11 @@ Value Endgame::operator()(const Position& pos) const { template<> Value Endgame::operator()(const Position& pos) const { - assert(pos.non_pawn_material(strongerSide) == RookValueMg); - assert(pos.non_pawn_material(weakerSide ) == BishopValueMg); - assert(pos.count(weakerSide ) == 1); - assert(pos.count< PAWN>(weakerSide ) == 0); - assert(pos.count< PAWN>(strongerSide) == 0); + assert(verify_material(pos, strongSide, RookValueMg, 0)); + assert(verify_material(pos, weakSide, BishopValueMg, 0)); - Value result = Value(PushToEdges[pos.king_square(weakerSide)]); - return strongerSide == pos.side_to_move() ? result : -result; + Value result = Value(PushToEdges[pos.king_square(weakSide)]); + return strongSide == pos.side_to_move() ? result : -result; } @@ -302,16 +297,13 @@ Value Endgame::operator()(const Position& pos) const { template<> Value Endgame::operator()(const Position& pos) const { - assert(pos.non_pawn_material(strongerSide) == RookValueMg); - assert(pos.non_pawn_material(weakerSide ) == KnightValueMg); - assert(pos.count(weakerSide ) == 1); - assert(pos.count< PAWN>(weakerSide ) == 0); - assert(pos.count< PAWN>(strongerSide) == 0); + assert(verify_material(pos, strongSide, RookValueMg, 0)); + assert(verify_material(pos, weakSide, KnightValueMg, 0)); - Square bksq = pos.king_square(weakerSide); - Square bnsq = pos.list(weakerSide)[0]; + Square bksq = pos.king_square(weakSide); + Square bnsq = pos.list(weakSide)[0]; Value result = Value(PushToEdges[bksq] + PushAway[square_distance(bksq, bnsq)]); - return strongerSide == pos.side_to_move() ? result : -result; + return strongSide == pos.side_to_move() ? result : -result; } @@ -321,23 +313,21 @@ Value Endgame::operator()(const Position& pos) const { template<> Value Endgame::operator()(const Position& pos) const { - assert(pos.non_pawn_material(strongerSide) == QueenValueMg); - assert(pos.non_pawn_material(weakerSide ) == VALUE_ZERO); - assert(pos.count(strongerSide) == 0); - assert(pos.count(weakerSide ) == 1); + assert(verify_material(pos, strongSide, QueenValueMg, 0)); + assert(verify_material(pos, weakSide, VALUE_ZERO, 1)); - Square winnerKSq = pos.king_square(strongerSide); - Square loserKSq = pos.king_square(weakerSide); - Square pawnSq = pos.list(weakerSide)[0]; + Square winnerKSq = pos.king_square(strongSide); + Square loserKSq = pos.king_square(weakSide); + Square pawnSq = pos.list(weakSide)[0]; Value result = Value(PushClose[square_distance(winnerKSq, loserKSq)]); - if ( relative_rank(weakerSide, pawnSq) != RANK_7 + if ( relative_rank(weakSide, pawnSq) != RANK_7 || square_distance(loserKSq, pawnSq) != 1 || !((FileABB | FileCBB | FileFBB | FileHBB) & pawnSq)) result += QueenValueEg - PawnValueEg; - return strongerSide == pos.side_to_move() ? result : -result; + return strongSide == pos.side_to_move() ? result : -result; } @@ -349,41 +339,41 @@ Value Endgame::operator()(const Position& pos) const { template<> Value Endgame::operator()(const Position& pos) const { - assert(pos.non_pawn_material(strongerSide) == QueenValueMg); - assert(pos.non_pawn_material(weakerSide ) == RookValueMg); - assert(pos.count(strongerSide) == 0); - assert(pos.count(weakerSide ) == 0); + assert(verify_material(pos, strongSide, QueenValueMg, 0)); + assert(verify_material(pos, weakSide, RookValueMg, 0)); - Square winnerKSq = pos.king_square(strongerSide); - Square loserKSq = pos.king_square(weakerSide); + Square winnerKSq = pos.king_square(strongSide); + Square loserKSq = pos.king_square(weakSide); Value result = QueenValueEg - RookValueEg + PushToEdges[loserKSq] + PushClose[square_distance(winnerKSq, loserKSq)]; - return strongerSide == pos.side_to_move() ? result : -result; + return strongSide == 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::operator()(const Position& pos) const { - assert(pos.non_pawn_material(strongerSide) == 2 * BishopValueMg); - assert(pos.non_pawn_material(weakerSide ) == KnightValueMg); - assert(pos.count(strongerSide) == 2); - assert(pos.count(weakerSide ) == 1); - assert(!pos.pieces(PAWN)); + assert(verify_material(pos, strongSide, 2 * BishopValueMg, 0)); + assert(verify_material(pos, weakSide, KnightValueMg, 0)); - Square wksq = pos.king_square(strongerSide); - Square bksq = pos.king_square(weakerSide); - Square nsq = pos.list(weakerSide)[0]; + Square winnerKSq = pos.king_square(strongSide); + Square loserKSq = pos.king_square(weakSide); + Square knightSq = pos.list(weakSide)[0]; - Value result = BishopValueEg - + PushClose[square_distance(wksq, bksq)] - + square_distance(bksq, nsq) * 32 - + (8 - popcount(pos.attacks_from(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; + return strongSide == pos.side_to_move() ? result : -result; } @@ -399,59 +389,61 @@ template<> Value Endgame::operator()(const Position&) const { return VALU template<> ScaleFactor Endgame::operator()(const Position& pos) const { - assert(pos.non_pawn_material(strongerSide) == BishopValueMg); - assert(pos.count(strongerSide) == 1); - assert(pos.count< PAWN>(strongerSide) >= 1); + assert(pos.non_pawn_material(strongSide) == BishopValueMg); + assert(pos.count(strongSide) >= 1); - // No assertions about the material of weakerSide, because we want draws to + // No assertions about the material of weakSide, because we want draws to // be detected even when the weaker side has some pawns. - Bitboard pawns = pos.pieces(strongerSide, PAWN); - File pawnFile = file_of(pos.list(strongerSide)[0]); + Bitboard pawns = pos.pieces(strongSide, PAWN); + File pawnFile = file_of(pos.list(strongSide)[0]); // All pawns are on a single rook file ? if ( (pawnFile == FILE_A || pawnFile == FILE_H) && !(pawns & ~file_bb(pawnFile))) { - Square bishopSq = pos.list(strongerSide)[0]; - Square queeningSq = relative_square(strongerSide, pawnFile | RANK_8); - Square kingSq = pos.king_square(weakerSide); + Square bishopSq = pos.list(strongSide)[0]; + Square queeningSq = relative_square(strongSide, pawnFile | RANK_8); + Square kingSq = pos.king_square(weakSide); if ( opposite_colors(queeningSq, bishopSq) - && abs(file_of(kingSq) - pawnFile) <= 1) - { - // The bishop has the wrong color, and the defending king is on the - // file of the pawn(s) or the adjacent file. Find the rank of the - // frontmost pawn. - Square pawnSq = frontmost_sq(strongerSide, pawns); - - // If the defending king has distance 1 to the promotion square or - // is placed somewhere in front of the pawn, it's a draw. - if ( square_distance(kingSq, queeningSq) <= 1 - || relative_rank(weakerSide, kingSq) <= relative_rank(weakerSide, pawnSq)) - return SCALE_FACTOR_DRAW; - } + && square_distance(queeningSq, kingSq) <= 1) + return SCALE_FACTOR_DRAW; } // All pawns on same B or G file? Then potential draw if ( (pawnFile == FILE_B || pawnFile == FILE_G) && !(pos.pieces(PAWN) & ~file_bb(pawnFile)) - && pos.non_pawn_material(weakerSide) == 0 - && pos.count(weakerSide) >= 1) + && pos.non_pawn_material(weakSide) == 0 + && pos.count(weakSide) >= 1) { - // Get weakerSide pawn that is closest to home rank - Square weakerPawnSq = backmost_sq(weakerSide, pos.pieces(weakerSide, PAWN)); - - Square strongerKingSq = pos.king_square(strongerSide); - Square weakerKingSq = pos.king_square(weakerSide); - Square bishopSq = pos.list(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. - if ( relative_rank(strongerSide, weakerPawnSq) == RANK_7 - && opposite_colors(bishopSq, weakerPawnSq) - && square_distance(weakerPawnSq, weakerKingSq) <= square_distance(weakerPawnSq, strongerKingSq)) - return SCALE_FACTOR_DRAW; + // Get weakSide pawn that is closest to home rank + Square weakPawnSq = backmost_sq(weakSide, pos.pieces(weakSide, PAWN)); + + Square strongKingSq = pos.king_square(strongSide); + Square weakKingSq = pos.king_square(weakSide); + Square bishopSq = pos.list(strongSide)[0]; + + // 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(strongSide, weakPawnSq) == RANK_7 + && (pos.pieces(strongSide, PAWN) & (weakPawnSq + pawn_push(weakSide))) + && (opposite_colors(bishopSq, weakPawnSq) || pos.count(strongSide) == 1)) + { + int strongKingDist = square_distance(weakPawnSq, strongKingSq); + int weakKingDist = square_distance(weakPawnSq, weakKingSq); + + // 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(strongSide, weakKingSq) >= RANK_7 + && weakKingDist <= 2 + && weakKingDist <= strongKingDist) + return SCALE_FACTOR_DRAW; + } } return SCALE_FACTOR_NONE; @@ -463,21 +455,19 @@ ScaleFactor Endgame::operator()(const Position& pos) const { template<> ScaleFactor Endgame::operator()(const Position& pos) const { - assert(pos.non_pawn_material(strongerSide) == QueenValueMg); - assert(pos.count(strongerSide) == 1); - assert(pos.count< PAWN>(strongerSide) == 0); - assert(pos.count< ROOK>(weakerSide ) == 1); - assert(pos.count< PAWN>(weakerSide ) >= 1); - - Square kingSq = pos.king_square(weakerSide); - Square rsq = pos.list(weakerSide)[0]; - - if ( relative_rank(weakerSide, kingSq) <= RANK_2 - && relative_rank(weakerSide, pos.king_square(strongerSide)) >= RANK_4 - && (pos.pieces(weakerSide, ROOK) & rank_bb(relative_rank(weakerSide, RANK_3))) - && (pos.pieces(weakerSide, PAWN) & rank_bb(relative_rank(weakerSide, RANK_2))) - && (pos.attacks_from(kingSq) & pos.pieces(weakerSide, PAWN)) - && (pos.attacks_from(rsq, strongerSide) & pos.pieces(weakerSide, PAWN))) + assert(verify_material(pos, strongSide, QueenValueMg, 0)); + assert(pos.count(weakSide) == 1); + assert(pos.count(weakSide) >= 1); + + Square kingSq = pos.king_square(weakSide); + Square rsq = pos.list(weakSide)[0]; + + if ( relative_rank(weakSide, kingSq) <= RANK_2 + && relative_rank(weakSide, pos.king_square(strongSide)) >= RANK_4 + && relative_rank(weakSide, rsq) == RANK_3 + && ( pos.pieces(weakSide, PAWN) + & pos.attacks_from(kingSq) + & pos.attacks_from(rsq, strongSide))) return SCALE_FACTOR_DRAW; return SCALE_FACTOR_NONE; @@ -493,20 +483,18 @@ ScaleFactor Endgame::operator()(const Position& pos) const { template<> ScaleFactor Endgame::operator()(const Position& pos) const { - assert(pos.non_pawn_material(strongerSide) == RookValueMg); - assert(pos.non_pawn_material(weakerSide) == RookValueMg); - assert(pos.count(strongerSide) == 1); - assert(pos.count(weakerSide ) == 0); + assert(verify_material(pos, strongSide, RookValueMg, 1)); + assert(verify_material(pos, weakSide, RookValueMg, 0)); - Square wksq = pos.king_square(strongerSide); - Square bksq = pos.king_square(weakerSide); - Square wrsq = pos.list(strongerSide)[0]; - Square wpsq = pos.list(strongerSide)[0]; - Square brsq = pos.list(weakerSide)[0]; + Square wksq = pos.king_square(strongSide); + Square bksq = pos.king_square(weakSide); + Square wrsq = pos.list(strongSide)[0]; + Square wpsq = pos.list(strongSide)[0]; + Square brsq = pos.list(weakSide)[0]; // Orient the board in such a way that the stronger side is white, and the // pawn is on the left half of the board. - if (strongerSide == BLACK) + if (strongSide == BLACK) { wksq = ~wksq; wrsq = ~wrsq; @@ -527,7 +515,7 @@ ScaleFactor Endgame::operator()(const Position& pos) const { File f = file_of(wpsq); Rank r = rank_of(wpsq); Square queeningSq = f | RANK_8; - int tempo = (pos.side_to_move() == strongerSide); + int tempo = (pos.side_to_move() == strongSide); // If the pawn is not too far advanced and the defending king defends the // queening square, use the third-rank defence. @@ -605,6 +593,49 @@ ScaleFactor Endgame::operator()(const Position& pos) const { return SCALE_FACTOR_NONE; } +template<> +ScaleFactor Endgame::operator()(const Position& pos) const { + + assert(verify_material(pos, strongSide, RookValueMg, 1)); + assert(verify_material(pos, weakSide, BishopValueMg, 0)); + + // Test for a rook pawn + if (pos.pieces(PAWN) & (FileABB | FileHBB)) + { + Square ksq = pos.king_square(weakSide); + Square bsq = pos.list(weakSide)[0]; + Square psq = pos.list(strongSide)[0]; + Rank rk = relative_rank(strongSide, psq); + Square push = pawn_push(strongSide); + + // 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(strongSide) + 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 @@ -612,25 +643,22 @@ ScaleFactor Endgame::operator()(const Position& pos) const { template<> ScaleFactor Endgame::operator()(const Position& pos) const { - assert(pos.non_pawn_material(strongerSide) == RookValueMg); - assert(pos.non_pawn_material(weakerSide) == RookValueMg); - assert(pos.count(strongerSide) == 2); - assert(pos.count(weakerSide ) == 1); + assert(verify_material(pos, strongSide, RookValueMg, 2)); + assert(verify_material(pos, weakSide, RookValueMg, 1)); - Square wpsq1 = pos.list(strongerSide)[0]; - Square wpsq2 = pos.list(strongerSide)[1]; - Square bksq = pos.king_square(weakerSide); + Square wpsq1 = pos.list(strongSide)[0]; + Square wpsq2 = pos.list(strongSide)[1]; + Square bksq = pos.king_square(weakSide); // 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(strongSide, wpsq1) || pos.pawn_passed(strongSide, wpsq2)) return SCALE_FACTOR_NONE; - Rank r = std::max(relative_rank(strongerSide, wpsq1), relative_rank(strongerSide, wpsq2)); + Rank r = std::max(relative_rank(strongSide, wpsq1), relative_rank(strongSide, wpsq2)); if ( file_distance(bksq, wpsq1) <= 1 && file_distance(bksq, wpsq2) <= 1 - && relative_rank(strongerSide, bksq) > r) + && relative_rank(strongSide, bksq) > r) { switch (r) { case RANK_2: return ScaleFactor(10); @@ -650,30 +678,29 @@ ScaleFactor Endgame::operator()(const Position& pos) const { template<> ScaleFactor Endgame::operator()(const Position& pos) const { - assert(pos.non_pawn_material(strongerSide) == VALUE_ZERO); - assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO); - assert(pos.count(strongerSide) >= 2); - assert(pos.count(weakerSide ) == 0); + assert(pos.non_pawn_material(strongSide) == VALUE_ZERO); + assert(pos.count(strongSide) >= 2); + assert(verify_material(pos, weakSide, VALUE_ZERO, 0)); - Square ksq = pos.king_square(weakerSide); - Bitboard pawns = pos.pieces(strongerSide, PAWN); + Square ksq = pos.king_square(weakSide); + Bitboard pawns = pos.pieces(strongSide, PAWN); // Are all pawns on the 'a' file? if (!(pawns & ~FileABB)) { // Does the defending king block the pawns? - if ( square_distance(ksq, relative_square(strongerSide, SQ_A8)) <= 1 + if ( square_distance(ksq, relative_square(strongSide, SQ_A8)) <= 1 || ( file_of(ksq) == FILE_A - && !(in_front_bb(strongerSide, rank_of(ksq)) & pawns))) + && !(in_front_bb(strongSide, rank_of(ksq)) & pawns))) return SCALE_FACTOR_DRAW; } // Are all pawns on the 'h' file? else if (!(pawns & ~FileHBB)) { // Does the defending king block the pawns? - if ( square_distance(ksq, relative_square(strongerSide, SQ_H8)) <= 1 + if ( square_distance(ksq, relative_square(strongSide, SQ_H8)) <= 1 || ( file_of(ksq) == FILE_H - && !(in_front_bb(strongerSide, rank_of(ksq)) & pawns))) + && !(in_front_bb(strongSide, rank_of(ksq)) & pawns))) return SCALE_FACTOR_DRAW; } return SCALE_FACTOR_NONE; @@ -687,27 +714,23 @@ ScaleFactor Endgame::operator()(const Position& pos) const { template<> ScaleFactor Endgame::operator()(const Position& pos) const { - assert(pos.non_pawn_material(strongerSide) == BishopValueMg); - assert(pos.non_pawn_material(weakerSide ) == BishopValueMg); - assert(pos.count(strongerSide) == 1); - assert(pos.count(weakerSide ) == 1); - assert(pos.count< PAWN>(strongerSide) == 1); - assert(pos.count< PAWN>(weakerSide ) == 0); + assert(verify_material(pos, strongSide, BishopValueMg, 1)); + assert(verify_material(pos, weakSide, BishopValueMg, 0)); - Square pawnSq = pos.list(strongerSide)[0]; - Square strongerBishopSq = pos.list(strongerSide)[0]; - Square weakerBishopSq = pos.list(weakerSide)[0]; - Square weakerKingSq = pos.king_square(weakerSide); + Square pawnSq = pos.list(strongSide)[0]; + Square strongBishopSq = pos.list(strongSide)[0]; + Square weakBishopSq = pos.list(weakSide)[0]; + Square weakKingSq = pos.king_square(weakSide); // Case 1: Defending king blocks the pawn, and cannot be driven away - if ( file_of(weakerKingSq) == file_of(pawnSq) - && relative_rank(strongerSide, pawnSq) < relative_rank(strongerSide, weakerKingSq) - && ( opposite_colors(weakerKingSq, strongerBishopSq) - || relative_rank(strongerSide, weakerKingSq) <= RANK_6)) + if ( file_of(weakKingSq) == file_of(pawnSq) + && relative_rank(strongSide, pawnSq) < relative_rank(strongSide, weakKingSq) + && ( opposite_colors(weakKingSq, strongBishopSq) + || relative_rank(strongSide, weakKingSq) <= RANK_6)) return SCALE_FACTOR_DRAW; // Case 2: Opposite colored bishops - if (opposite_colors(strongerBishopSq, weakerBishopSq)) + if (opposite_colors(strongBishopSq, weakBishopSq)) { // We assume that the position is drawn in the following three situations: // @@ -719,17 +742,17 @@ ScaleFactor Endgame::operator()(const Position& pos) const { // These rules are probably not perfect, but in practice they work // reasonably well. - if (relative_rank(strongerSide, pawnSq) <= RANK_5) + if (relative_rank(strongSide, pawnSq) <= RANK_5) return SCALE_FACTOR_DRAW; else { - Bitboard path = forward_bb(strongerSide, pawnSq); + Bitboard path = forward_bb(strongSide, pawnSq); - if (path & pos.pieces(weakerSide, KING)) + if (path & pos.pieces(weakSide, KING)) return SCALE_FACTOR_DRAW; - if ( (pos.attacks_from(weakerBishopSq) & path) - && square_distance(weakerBishopSq, pawnSq) >= 3) + if ( (pos.attacks_from(weakBishopSq) & path) + && square_distance(weakBishopSq, pawnSq) >= 3) return SCALE_FACTOR_DRAW; } } @@ -742,34 +765,30 @@ ScaleFactor Endgame::operator()(const Position& pos) const { template<> ScaleFactor Endgame::operator()(const Position& pos) const { - assert(pos.non_pawn_material(strongerSide) == BishopValueMg); - assert(pos.non_pawn_material(weakerSide ) == BishopValueMg); - assert(pos.count(strongerSide) == 1); - assert(pos.count(weakerSide ) == 1); - assert(pos.count< PAWN>(strongerSide) == 2); - assert(pos.count< PAWN>(weakerSide ) == 0); + assert(verify_material(pos, strongSide, BishopValueMg, 2)); + assert(verify_material(pos, weakSide, BishopValueMg, 0)); - Square wbsq = pos.list(strongerSide)[0]; - Square bbsq = pos.list(weakerSide)[0]; + Square wbsq = pos.list(strongSide)[0]; + Square bbsq = pos.list(weakSide)[0]; if (!opposite_colors(wbsq, bbsq)) return SCALE_FACTOR_NONE; - Square ksq = pos.king_square(weakerSide); - Square psq1 = pos.list(strongerSide)[0]; - Square psq2 = pos.list(strongerSide)[1]; + Square ksq = pos.king_square(weakSide); + Square psq1 = pos.list(strongSide)[0]; + Square psq2 = pos.list(strongSide)[1]; Rank r1 = rank_of(psq1); Rank r2 = rank_of(psq2); Square blockSq1, blockSq2; - if (relative_rank(strongerSide, psq1) > relative_rank(strongerSide, psq2)) + if (relative_rank(strongSide, psq1) > relative_rank(strongSide, psq2)) { - blockSq1 = psq1 + pawn_push(strongerSide); + blockSq1 = psq1 + pawn_push(strongSide); blockSq2 = file_of(psq2) | rank_of(psq1); } else { - blockSq1 = psq2 + pawn_push(strongerSide); + blockSq1 = psq2 + pawn_push(strongSide); blockSq2 = file_of(psq1) | rank_of(psq2); } @@ -779,7 +798,7 @@ ScaleFactor Endgame::operator()(const Position& pos) const { // Both pawns are on the same file. Easy draw if defender firmly controls // some square in the frontmost pawn's path. if ( file_of(ksq) == file_of(blockSq1) - && relative_rank(strongerSide, ksq) >= relative_rank(strongerSide, blockSq1) + && relative_rank(strongSide, ksq) >= relative_rank(strongSide, blockSq1) && opposite_colors(ksq, wbsq)) return SCALE_FACTOR_DRAW; else @@ -792,14 +811,14 @@ ScaleFactor Endgame::operator()(const Position& pos) const { if ( ksq == blockSq1 && opposite_colors(ksq, wbsq) && ( bbsq == blockSq2 - || (pos.attacks_from(blockSq2) & pos.pieces(weakerSide, BISHOP)) + || (pos.attacks_from(blockSq2) & pos.pieces(weakSide, BISHOP)) || abs(r1 - r2) >= 2)) return SCALE_FACTOR_DRAW; else if ( ksq == blockSq2 && opposite_colors(ksq, wbsq) && ( bbsq == blockSq1 - || (pos.attacks_from(blockSq1) & pos.pieces(weakerSide, BISHOP)))) + || (pos.attacks_from(blockSq1) & pos.pieces(weakSide, BISHOP)))) return SCALE_FACTOR_DRAW; else return SCALE_FACTOR_NONE; @@ -817,21 +836,17 @@ ScaleFactor Endgame::operator()(const Position& pos) const { template<> ScaleFactor Endgame::operator()(const Position& pos) const { - assert(pos.non_pawn_material(strongerSide) == BishopValueMg); - assert(pos.non_pawn_material(weakerSide ) == KnightValueMg); - assert(pos.count(strongerSide) == 1); - assert(pos.count(weakerSide ) == 1); - assert(pos.count< PAWN>(strongerSide) == 1); - assert(pos.count< PAWN>(weakerSide ) == 0); - - Square pawnSq = pos.list(strongerSide)[0]; - Square strongerBishopSq = pos.list(strongerSide)[0]; - Square weakerKingSq = pos.king_square(weakerSide); - - if ( file_of(weakerKingSq) == file_of(pawnSq) - && relative_rank(strongerSide, pawnSq) < relative_rank(strongerSide, weakerKingSq) - && ( opposite_colors(weakerKingSq, strongerBishopSq) - || relative_rank(strongerSide, weakerKingSq) <= RANK_6)) + assert(verify_material(pos, strongSide, BishopValueMg, 1)); + assert(verify_material(pos, weakSide, KnightValueMg, 0)); + + Square pawnSq = pos.list(strongSide)[0]; + Square strongBishopSq = pos.list(strongSide)[0]; + Square weakKingSq = pos.king_square(weakSide); + + if ( file_of(weakKingSq) == file_of(pawnSq) + && relative_rank(strongSide, pawnSq) < relative_rank(strongSide, weakKingSq) + && ( opposite_colors(weakKingSq, strongBishopSq) + || relative_rank(strongSide, weakKingSq) <= RANK_6)) return SCALE_FACTOR_DRAW; return SCALE_FACTOR_NONE; @@ -844,21 +859,18 @@ ScaleFactor Endgame::operator()(const Position& pos) const { template<> ScaleFactor Endgame::operator()(const Position& pos) const { - assert(pos.non_pawn_material(strongerSide) == KnightValueMg); - assert(pos.non_pawn_material(weakerSide ) == VALUE_ZERO); - assert(pos.count(strongerSide) == 1); - assert(pos.count< PAWN>(strongerSide) == 1); - assert(pos.count< PAWN>(weakerSide ) == 0); + assert(verify_material(pos, strongSide, KnightValueMg, 1)); + assert(verify_material(pos, weakSide, VALUE_ZERO, 0)); - Square pawnSq = pos.list(strongerSide)[0]; - Square weakerKingSq = pos.king_square(weakerSide); + Square pawnSq = pos.list(strongSide)[0]; + Square weakKingSq = pos.king_square(weakSide); - if ( pawnSq == relative_square(strongerSide, SQ_A7) - && square_distance(weakerKingSq, relative_square(strongerSide, SQ_A8)) <= 1) + if ( pawnSq == relative_square(strongSide, SQ_A7) + && square_distance(weakKingSq, relative_square(strongSide, SQ_A8)) <= 1) return SCALE_FACTOR_DRAW; - if ( pawnSq == relative_square(strongerSide, SQ_H7) - && square_distance(weakerKingSq, relative_square(strongerSide, SQ_H8)) <= 1) + if ( pawnSq == relative_square(strongSide, SQ_H7) + && square_distance(weakKingSq, relative_square(strongSide, SQ_H8)) <= 1) return SCALE_FACTOR_DRAW; return SCALE_FACTOR_NONE; @@ -870,14 +882,14 @@ ScaleFactor Endgame::operator()(const Position& pos) const { template<> ScaleFactor Endgame::operator()(const Position& pos) const { - Square pawnSq = pos.list(strongerSide)[0]; - Square bishopSq = pos.list(weakerSide)[0]; - Square weakerKingSq = pos.king_square(weakerSide); + Square pawnSq = pos.list(strongSide)[0]; + Square bishopSq = pos.list(weakSide)[0]; + Square weakKingSq = pos.king_square(weakSide); // King needs to get close to promoting pawn to prevent knight from blocking. // Rules for this are very tricky, so just approximate. - if (forward_bb(strongerSide, pawnSq) & pos.attacks_from(bishopSq)) - return ScaleFactor(square_distance(weakerKingSq, pawnSq)); + if (forward_bb(strongSide, pawnSq) & pos.attacks_from(bishopSq)) + return ScaleFactor(square_distance(weakKingSq, pawnSq)); return SCALE_FACTOR_NONE; } @@ -891,17 +903,15 @@ ScaleFactor Endgame::operator()(const Position& pos) const { template<> ScaleFactor Endgame::operator()(const Position& pos) const { - assert(pos.non_pawn_material(strongerSide) == VALUE_ZERO); - assert(pos.non_pawn_material(weakerSide ) == VALUE_ZERO); - assert(pos.count(WHITE) == 1); - assert(pos.count(BLACK) == 1); + assert(verify_material(pos, strongSide, VALUE_ZERO, 1)); + assert(verify_material(pos, weakSide, VALUE_ZERO, 1)); - Square wksq = pos.king_square(strongerSide); - Square bksq = pos.king_square(weakerSide); - Square psq = pos.list(strongerSide)[0]; + Square wksq = pos.king_square(strongSide); + Square bksq = pos.king_square(weakSide); + Square psq = pos.list(strongSide)[0]; Color us = pos.side_to_move(); - if (strongerSide == BLACK) + if (strongSide == BLACK) { wksq = ~wksq; bksq = ~bksq;