X-Git-Url: https://git.sesse.net/?p=stockfish;a=blobdiff_plain;f=src%2Fendgame.cpp;h=976b3d6365c46d4316784bb3ea078af54924e0b7;hp=5906b4d59187aecd8604e191bba119591d37a995;hb=c7e7d9217b232eb7d75b083a45f001ef998c9deb;hpb=2bf18bfc6396ae7292f57fc021b390fc05cd0f95 diff --git a/src/endgame.cpp b/src/endgame.cpp index 5906b4d5..976b3d63 100644 --- a/src/endgame.cpp +++ b/src/endgame.cpp @@ -65,6 +65,21 @@ namespace { } #endif + // Map the square as if strongSide is white and strongSide's only pawn + // is on the left half of the board. + Square normalize(const Position& pos, Color strongSide, Square sq) { + + assert(pos.count(strongSide) == 1); + + if (file_of(pos.list(strongSide)[0]) >= FILE_E) + sq = Square(sq ^ 7); // Mirror SQ_H1 -> SQ_A1 + + if (strongSide == BLACK) + sq = ~sq; + + return sq; + } + // 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 // and then let a Position object to do the work for us. Note that the @@ -130,7 +145,7 @@ void Endgames::add(const string& code) { /// Mate with KX vs K. This function is used to evaluate positions with -/// King and plenty of material vs a lone king. It simply gives the +/// king and plenty of material vs a lone king. It simply gives the /// attacking side a bonus for driving the defending king towards the edge /// of the board, and for keeping the distance between the two kings small. template<> @@ -172,13 +187,13 @@ Value Endgame::operator()(const Position& pos) const { 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 - // mirror the kings so to drive enemy toward corners A8 or H1. + // kbnk_mate_table() tries to drive toward corners A1 or H8, if we have a + // bishop that cannot reach the above squares we flip the kings in order + // to drive the enemy toward corners A8 or H1. if (opposite_colors(bishopSq, SQ_A1)) { - winnerKSq = mirror(winnerKSq); - loserKSq = mirror(loserKSq); + winnerKSq = ~winnerKSq; + loserKSq = ~loserKSq; } Value result = VALUE_KNOWN_WIN @@ -196,25 +211,12 @@ Value Endgame::operator()(const Position& pos) const { assert(verify_material(pos, strongSide, VALUE_ZERO, 1)); assert(verify_material(pos, weakSide, VALUE_ZERO, 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(); + // Assume strongSide is white and the pawn is on files A-D + Square wksq = normalize(pos, strongSide, pos.king_square(strongSide)); + Square bksq = normalize(pos, strongSide, pos.king_square(weakSide)); + Square psq = normalize(pos, strongSide, pos.list(strongSide)[0]); - if (strongSide == BLACK) - { - wksq = ~wksq; - bksq = ~bksq; - psq = ~psq; - us = ~us; - } - - if (file_of(psq) >= FILE_E) - { - wksq = mirror(wksq); - bksq = mirror(bksq); - psq = mirror(psq); - } + Color us = strongSide == pos.side_to_move() ? WHITE : BLACK; if (!Bitbases::probe_kpk(wksq, psq, bksq, us)) return VALUE_DRAW; @@ -235,18 +237,10 @@ Value Endgame::operator()(const Position& pos) const { assert(verify_material(pos, strongSide, RookValueMg, 0)); assert(verify_material(pos, weakSide, VALUE_ZERO, 1)); - 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 (strongSide == BLACK) - { - wksq = ~wksq; - bksq = ~bksq; - rsq = ~rsq; - psq = ~psq; - } + Square wksq = relative_square(strongSide, pos.king_square(strongSide)); + Square bksq = relative_square(strongSide, pos.king_square(weakSide)); + Square rsq = relative_square(strongSide, pos.list(strongSide)[0]); + Square psq = relative_square(strongSide, pos.list(weakSide)[0]); Square queeningSq = file_of(psq) | RANK_1; Value result; @@ -307,9 +301,10 @@ Value Endgame::operator()(const Position& pos) const { } -/// KQ vs KP. In general, a win for the stronger side, however, there are a few -/// important exceptions. Pawn on 7th rank, A,C,F or H file, with king next can -/// be a draw, so we scale down to distance between kings only. +/// KQ vs KP. In general, this is a win for the stronger side, but there are a +/// few important exceptions. A pawn on 7th rank and on the A,C,F or H files +/// with a king positioned next to it can be a draw, so in that case, we only +/// use the distance between the kings. template<> Value Endgame::operator()(const Position& pos) const { @@ -411,20 +406,20 @@ ScaleFactor Endgame::operator()(const Position& pos) const { return SCALE_FACTOR_DRAW; } - // All pawns on same B or G file? Then potential draw + // If all the pawns are on the same B or G file, then it's potentially a draw if ( (pawnFile == FILE_B || pawnFile == FILE_G) && !(pos.pieces(PAWN) & ~file_bb(pawnFile)) && pos.non_pawn_material(weakSide) == 0 && pos.count(weakSide) >= 1) { - // Get weakSide pawn that is closest to home rank + // Get weakSide pawn that is closest to the 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 + // There's 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))) @@ -433,7 +428,7 @@ ScaleFactor Endgame::operator()(const Position& pos) const { 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 + // It's a draw if the weak king is on its 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 @@ -479,38 +474,19 @@ ScaleFactor Endgame::operator()(const Position& pos) const { /// probably be a good idea to add more knowledge in the future. /// /// It would also be nice to rewrite the actual code for this function, -/// which is mostly copied from Glaurung 1.x, and not very pretty. +/// which is mostly copied from Glaurung 1.x, and isn't very pretty. template<> ScaleFactor Endgame::operator()(const Position& pos) const { assert(verify_material(pos, strongSide, RookValueMg, 1)); assert(verify_material(pos, weakSide, RookValueMg, 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 (strongSide == BLACK) - { - wksq = ~wksq; - wrsq = ~wrsq; - wpsq = ~wpsq; - bksq = ~bksq; - brsq = ~brsq; - } - - if (file_of(wpsq) > FILE_D) - { - wksq = mirror(wksq); - wrsq = mirror(wrsq); - wpsq = mirror(wpsq); - bksq = mirror(bksq); - brsq = mirror(brsq); - } + // Assume strongSide is white and the pawn is on files A-D + Square wksq = normalize(pos, strongSide, pos.king_square(strongSide)); + Square bksq = normalize(pos, strongSide, pos.king_square(weakSide)); + Square wrsq = normalize(pos, strongSide, pos.list(strongSide)[0]); + Square wpsq = normalize(pos, strongSide, pos.list(strongSide)[0]); + Square brsq = normalize(pos, strongSide, pos.list(weakSide)[0]); File f = file_of(wpsq); Rank r = rank_of(wpsq); @@ -684,25 +660,15 @@ ScaleFactor Endgame::operator()(const Position& pos) const { Square ksq = pos.king_square(weakSide); Bitboard pawns = pos.pieces(strongSide, PAWN); + Square psq = pos.list(strongSide)[0]; + + // If all pawns are ahead of the king, all pawns are on a single + // rook file and the king is within one file of the pawns then draw. + if ( !(pawns & ~in_front_bb(weakSide, rank_of(ksq))) + && !((pawns & ~FileABB) && (pawns & ~FileHBB)) + && file_distance(ksq, psq) <= 1) + return SCALE_FACTOR_DRAW; - // Are all pawns on the 'a' file? - if (!(pawns & ~FileABB)) - { - // Does the defending king block the pawns? - if ( square_distance(ksq, relative_square(strongSide, SQ_A8)) <= 1 - || ( file_of(ksq) == FILE_A - && !(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(strongSide, SQ_H8)) <= 1 - || ( file_of(ksq) == FILE_H - && !(in_front_bb(strongSide, rank_of(ksq)) & pawns))) - return SCALE_FACTOR_DRAW; - } return SCALE_FACTOR_NONE; } @@ -795,8 +761,8 @@ ScaleFactor Endgame::operator()(const Position& pos) const { switch (file_distance(psq1, psq2)) { case 0: - // Both pawns are on the same file. Easy draw if defender firmly controls - // some square in the frontmost pawn's path. + // Both pawns are on the same file. It's an easy draw if the defender firmly + // controls some square in the frontmost pawn's path. if ( file_of(ksq) == file_of(blockSq1) && relative_rank(strongSide, ksq) >= relative_rank(strongSide, blockSq1) && opposite_colors(ksq, wbsq)) @@ -805,9 +771,9 @@ ScaleFactor Endgame::operator()(const Position& pos) const { return SCALE_FACTOR_NONE; case 1: - // Pawns on adjacent files. Draw if defender firmly controls the square - // in front of the frontmost pawn's path, and the square diagonally behind - // this square on the file of the other pawn. + // Pawns on adjacent files. It's a draw if the defender firmly controls the + // square in front of the frontmost pawn's path, and the square diagonally + // behind this square on the file of the other pawn. if ( ksq == blockSq1 && opposite_colors(ksq, wbsq) && ( bbsq == blockSq2 @@ -862,15 +828,11 @@ ScaleFactor Endgame::operator()(const Position& pos) const { assert(verify_material(pos, strongSide, KnightValueMg, 1)); assert(verify_material(pos, weakSide, VALUE_ZERO, 0)); - Square pawnSq = pos.list(strongSide)[0]; - Square weakKingSq = pos.king_square(weakSide); + // Assume strongSide is white and the pawn is on files A-D + Square pawnSq = normalize(pos, strongSide, pos.list(strongSide)[0]); + Square weakKingSq = normalize(pos, strongSide, pos.king_square(weakSide)); - if ( pawnSq == relative_square(strongSide, SQ_A7) - && square_distance(weakKingSq, relative_square(strongSide, SQ_A8)) <= 1) - return SCALE_FACTOR_DRAW; - - if ( pawnSq == relative_square(strongSide, SQ_H7) - && square_distance(weakKingSq, relative_square(strongSide, SQ_H8)) <= 1) + if (pawnSq == SQ_A7 && square_distance(SQ_A8, weakKingSq) <= 1) return SCALE_FACTOR_DRAW; return SCALE_FACTOR_NONE; @@ -906,25 +868,12 @@ ScaleFactor Endgame::operator()(const Position& pos) const { assert(verify_material(pos, strongSide, VALUE_ZERO, 1)); assert(verify_material(pos, weakSide, VALUE_ZERO, 1)); - 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 (strongSide == BLACK) - { - wksq = ~wksq; - bksq = ~bksq; - psq = ~psq; - us = ~us; - } + // Assume strongSide is white and the pawn is on files A-D + Square wksq = normalize(pos, strongSide, pos.king_square(strongSide)); + Square bksq = normalize(pos, strongSide, pos.king_square(weakSide)); + Square psq = normalize(pos, strongSide, pos.list(strongSide)[0]); - if (file_of(psq) >= FILE_E) - { - wksq = mirror(wksq); - bksq = mirror(bksq); - psq = mirror(psq); - } + Color us = strongSide == pos.side_to_move() ? WHITE : BLACK; // If the pawn has advanced to the fifth rank or further, and is not a // rook pawn, it's too dangerous to assume that it's at least a draw.