X-Git-Url: https://git.sesse.net/?p=stockfish;a=blobdiff_plain;f=src%2Fendgame.cpp;h=c0d317b779234f4ae5cca323c221ffb5be8c4c93;hp=0e70efe1d386fd387018d787be94acce38bc7d25;hb=cee6336515d5772370d061aa54cf3ec9f9a0d3c2;hpb=0439a79566d03065317b2025b9b479b9ae9f2d65 diff --git a/src/endgame.cpp b/src/endgame.cpp index 0e70efe1..c0d317b7 100644 --- a/src/endgame.cpp +++ b/src/endgame.cpp @@ -20,19 +20,18 @@ #include #include +#include "bitboard.h" #include "bitcount.h" #include "endgame.h" #include "movegen.h" using std::string; -extern uint32_t probe_kpk_bitbase(Square wksq, Square wpsq, Square bksq, Color stm); - namespace { // Table used to drive the defending king towards the edge of the board // in KX vs K and KQ vs KR endgames. - const int MateTable[64] = { + const int MateTable[SQUARE_NB] = { 100, 90, 80, 70, 70, 80, 90, 100, 90, 70, 60, 50, 50, 60, 70, 90, 80, 60, 40, 30, 30, 40, 60, 80, @@ -45,7 +44,7 @@ namespace { // Table used to drive the defending king towards a corner square of the // right color in KBN vs K endgames. - const int KBNKMateTable[64] = { + const int KBNKMateTable[SQUARE_NB] = { 200, 190, 180, 170, 160, 150, 140, 130, 190, 180, 170, 160, 150, 140, 130, 140, 180, 170, 155, 140, 140, 125, 140, 150, @@ -72,12 +71,12 @@ namespace { string sides[] = { code.substr(code.find('K', 1)), // Weaker code.substr(0, code.find('K', 1)) }; // Stronger - transform(sides[c].begin(), sides[c].end(), sides[c].begin(), tolower); + std::transform(sides[c].begin(), sides[c].end(), sides[c].begin(), tolower); string fen = sides[0] + char('0' + int(8 - code.length())) + sides[1] + "/8/8/8/8/8/8/8 w - - 0 10"; - return Position(fen, false, 0).material_key(); + return Position(fen, false, NULL).material_key(); } template @@ -96,6 +95,7 @@ Endgames::Endgames() { add("KRKP"); add("KRKB"); add("KRKN"); + add("KQKP"); add("KQKR"); add("KBBKN"); @@ -134,7 +134,7 @@ Value Endgame::operator()(const Position& pos) const { // Stalemate detection with lone king if ( pos.side_to_move() == weakerSide && !pos.in_check() - && !MoveList(pos).size()) { + && !MoveList(pos).size()) { return VALUE_DRAW; } @@ -142,7 +142,7 @@ Value Endgame::operator()(const Position& pos) const { Square loserKSq = pos.king_square(weakerSide); Value result = pos.non_pawn_material(strongerSide) - + pos.piece_count(strongerSide, PAWN) * PawnValueEndgame + + pos.piece_count(strongerSide, PAWN) * PawnValueEg + MateTable[loserKSq] + DistanceBonus[square_distance(winnerKSq, loserKSq)]; @@ -163,19 +163,19 @@ Value Endgame::operator()(const Position& pos) const { assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO); assert(pos.piece_count(weakerSide, PAWN) == VALUE_ZERO); - assert(pos.non_pawn_material(strongerSide) == KnightValueMidgame + BishopValueMidgame); + assert(pos.non_pawn_material(strongerSide) == KnightValueMg + BishopValueMg); assert(pos.piece_count(strongerSide, BISHOP) == 1); assert(pos.piece_count(strongerSide, KNIGHT) == 1); assert(pos.piece_count(strongerSide, PAWN) == 0); Square winnerKSq = pos.king_square(strongerSide); Square loserKSq = pos.king_square(weakerSide); - Square bishopSquare = pos.piece_list(strongerSide, BISHOP)[0]; + Square bishopSq = pos.piece_list(strongerSide, BISHOP)[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. - if (opposite_colors(bishopSquare, SQ_A1)) + if (opposite_colors(bishopSq, SQ_A1)) { winnerKSq = mirror(winnerKSq); loserKSq = mirror(loserKSq); @@ -223,12 +223,10 @@ Value Endgame::operator()(const Position& pos) const { wpsq = mirror(wpsq); } - if (!probe_kpk_bitbase(wksq, wpsq, bksq, stm)) + if (!Bitbases::probe_kpk(wksq, wpsq, bksq, stm)) return VALUE_DRAW; - Value result = VALUE_KNOWN_WIN - + PawnValueEndgame - + Value(rank_of(wpsq)); + Value result = VALUE_KNOWN_WIN + PawnValueEg + Value(rank_of(wpsq)); return strongerSide == pos.side_to_move() ? result : -result; } @@ -241,7 +239,7 @@ Value Endgame::operator()(const Position& pos) const { template<> Value Endgame::operator()(const Position& pos) const { - assert(pos.non_pawn_material(strongerSide) == RookValueMidgame); + assert(pos.non_pawn_material(strongerSide) == RookValueMg); assert(pos.piece_count(strongerSide, PAWN) == 0); assert(pos.non_pawn_material(weakerSide) == 0); assert(pos.piece_count(weakerSide, PAWN) == 1); @@ -262,18 +260,18 @@ Value Endgame::operator()(const Position& pos) const { bpsq = ~bpsq; } - Square queeningSq = make_square(file_of(bpsq), RANK_1); + Square queeningSq = file_of(bpsq) | RANK_1; Value result; // If the stronger side's king is in front of the pawn, it's a win if (wksq < bpsq && file_of(wksq) == file_of(bpsq)) - result = RookValueEndgame - Value(square_distance(wksq, bpsq)); + result = RookValueEg - Value(square_distance(wksq, bpsq)); // If the weaker side's king is too far from the pawn and the rook, // it's a win else if ( square_distance(bksq, bpsq) - (tempo ^ 1) >= 3 && square_distance(bksq, wrsq) >= 3) - result = RookValueEndgame - Value(square_distance(wksq, bpsq)); + result = RookValueEg - Value(square_distance(wksq, bpsq)); // If the pawn is far advanced and supported by the defending king, // the position is drawish @@ -298,9 +296,9 @@ Value Endgame::operator()(const Position& pos) const { template<> Value Endgame::operator()(const Position& pos) const { - assert(pos.non_pawn_material(strongerSide) == RookValueMidgame); + assert(pos.non_pawn_material(strongerSide) == RookValueMg); assert(pos.piece_count(strongerSide, PAWN) == 0); - assert(pos.non_pawn_material(weakerSide) == BishopValueMidgame); + assert(pos.non_pawn_material(weakerSide) == BishopValueMg); assert(pos.piece_count(weakerSide, PAWN) == 0); assert(pos.piece_count(weakerSide, BISHOP) == 1); @@ -314,9 +312,9 @@ Value Endgame::operator()(const Position& pos) const { template<> Value Endgame::operator()(const Position& pos) const { - assert(pos.non_pawn_material(strongerSide) == RookValueMidgame); + assert(pos.non_pawn_material(strongerSide) == RookValueMg); assert(pos.piece_count(strongerSide, PAWN) == 0); - assert(pos.non_pawn_material(weakerSide) == KnightValueMidgame); + assert(pos.non_pawn_material(weakerSide) == KnightValueMg); assert(pos.piece_count(weakerSide, PAWN) == 0); assert(pos.piece_count(weakerSide, KNIGHT) == 1); @@ -329,6 +327,37 @@ 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. +template<> +Value Endgame::operator()(const Position& pos) const { + + assert(pos.non_pawn_material(strongerSide) == QueenValueMg); + assert(pos.piece_count(strongerSide, PAWN) == 0); + assert(pos.non_pawn_material(weakerSide) == 0); + assert(pos.piece_count(weakerSide, PAWN) == 1); + + Square winnerKSq = pos.king_square(strongerSide); + Square loserKSq = pos.king_square(weakerSide); + Square pawnSq = pos.piece_list(weakerSide, PAWN)[0]; + + Value result = QueenValueEg + - PawnValueEg + + DistanceBonus[square_distance(winnerKSq, loserKSq)]; + + if ( square_distance(loserKSq, pawnSq) == 1 + && relative_rank(weakerSide, pawnSq) == RANK_7) + { + File f = file_of(pawnSq); + + if (f == FILE_A || f == FILE_C || f == FILE_F || f == FILE_H) + result = Value(DistanceBonus[square_distance(winnerKSq, loserKSq)]); + } + return strongerSide == pos.side_to_move() ? result : -result; +} + + /// KQ vs KR. This is almost identical to KX vs K: We give the attacking /// king a bonus for having the kings close together, and for forcing the /// defending king towards the edge. If we also take care to avoid null move @@ -337,16 +366,16 @@ Value Endgame::operator()(const Position& pos) const { template<> Value Endgame::operator()(const Position& pos) const { - assert(pos.non_pawn_material(strongerSide) == QueenValueMidgame); + assert(pos.non_pawn_material(strongerSide) == QueenValueMg); assert(pos.piece_count(strongerSide, PAWN) == 0); - assert(pos.non_pawn_material(weakerSide) == RookValueMidgame); + assert(pos.non_pawn_material(weakerSide) == RookValueMg); assert(pos.piece_count(weakerSide, PAWN) == 0); Square winnerKSq = pos.king_square(strongerSide); Square loserKSq = pos.king_square(weakerSide); - Value result = QueenValueEndgame - - RookValueEndgame + Value result = QueenValueEg + - RookValueEg + MateTable[loserKSq] + DistanceBonus[square_distance(winnerKSq, loserKSq)]; @@ -357,12 +386,12 @@ template<> Value Endgame::operator()(const Position& pos) const { assert(pos.piece_count(strongerSide, BISHOP) == 2); - assert(pos.non_pawn_material(strongerSide) == 2*BishopValueMidgame); + assert(pos.non_pawn_material(strongerSide) == 2*BishopValueMg); assert(pos.piece_count(weakerSide, KNIGHT) == 1); - assert(pos.non_pawn_material(weakerSide) == KnightValueMidgame); + assert(pos.non_pawn_material(weakerSide) == KnightValueMg); assert(!pos.pieces(PAWN)); - Value result = BishopValueEndgame; + Value result = BishopValueEg; Square wksq = pos.king_square(strongerSide); Square bksq = pos.king_square(weakerSide); Square nsq = pos.piece_list(weakerSide, KNIGHT)[0]; @@ -399,22 +428,22 @@ Value Endgame::operator()(const Position&) const { template<> ScaleFactor Endgame::operator()(const Position& pos) const { - assert(pos.non_pawn_material(strongerSide) == BishopValueMidgame); + assert(pos.non_pawn_material(strongerSide) == BishopValueMg); assert(pos.piece_count(strongerSide, BISHOP) == 1); assert(pos.piece_count(strongerSide, PAWN) >= 1); // No assertions about the material of weakerSide, because we want draws to // be detected even when the weaker side has some pawns. - Bitboard pawns = pos.pieces(PAWN, strongerSide); + Bitboard pawns = pos.pieces(strongerSide, PAWN); File pawnFile = file_of(pos.piece_list(strongerSide, PAWN)[0]); // All pawns are on a single rook file ? - if ( (pawnFile == FILE_A || pawnFile == FILE_H) + if ( (pawnFile == FILE_A || pawnFile == FILE_H) && !(pawns & ~file_bb(pawnFile))) { Square bishopSq = pos.piece_list(strongerSide, BISHOP)[0]; - Square queeningSq = relative_square(strongerSide, make_square(pawnFile, RANK_8)); + Square queeningSq = relative_square(strongerSide, pawnFile | RANK_8); Square kingSq = pos.king_square(weakerSide); if ( opposite_colors(queeningSq, bishopSq) @@ -451,7 +480,7 @@ ScaleFactor Endgame::operator()(const Position& pos) const { template<> ScaleFactor Endgame::operator()(const Position& pos) const { - assert(pos.non_pawn_material(strongerSide) == QueenValueMidgame); + assert(pos.non_pawn_material(strongerSide) == QueenValueMg); assert(pos.piece_count(strongerSide, QUEEN) == 1); assert(pos.piece_count(strongerSide, PAWN) == 0); assert(pos.piece_count(weakerSide, ROOK) == 1); @@ -460,12 +489,12 @@ ScaleFactor Endgame::operator()(const Position& pos) const { Square kingSq = pos.king_square(weakerSide); if ( relative_rank(weakerSide, kingSq) <= RANK_2 && relative_rank(weakerSide, pos.king_square(strongerSide)) >= RANK_4 - && (pos.pieces(ROOK, weakerSide) & rank_bb(relative_rank(weakerSide, RANK_3))) - && (pos.pieces(PAWN, weakerSide) & rank_bb(relative_rank(weakerSide, RANK_2))) - && (pos.attacks_from(kingSq) & pos.pieces(PAWN, weakerSide))) + && (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))) { Square rsq = pos.piece_list(weakerSide, ROOK)[0]; - if (pos.attacks_from(rsq, strongerSide) & pos.pieces(PAWN, weakerSide)) + if (pos.attacks_from(rsq, strongerSide) & pos.pieces(weakerSide, PAWN)) return SCALE_FACTOR_DRAW; } return SCALE_FACTOR_NONE; @@ -481,9 +510,9 @@ ScaleFactor Endgame::operator()(const Position& pos) const { template<> ScaleFactor Endgame::operator()(const Position& pos) const { - assert(pos.non_pawn_material(strongerSide) == RookValueMidgame); + assert(pos.non_pawn_material(strongerSide) == RookValueMg); assert(pos.piece_count(strongerSide, PAWN) == 1); - assert(pos.non_pawn_material(weakerSide) == RookValueMidgame); + assert(pos.non_pawn_material(weakerSide) == RookValueMg); assert(pos.piece_count(weakerSide, PAWN) == 0); Square wksq = pos.king_square(strongerSide); @@ -513,7 +542,7 @@ ScaleFactor Endgame::operator()(const Position& pos) const { File f = file_of(wpsq); Rank r = rank_of(wpsq); - Square queeningSq = make_square(f, RANK_8); + Square queeningSq = f | RANK_8; int tempo = (pos.side_to_move() == strongerSide); // If the pawn is not too far advanced and the defending king defends the @@ -599,9 +628,9 @@ ScaleFactor Endgame::operator()(const Position& pos) const { template<> ScaleFactor Endgame::operator()(const Position& pos) const { - assert(pos.non_pawn_material(strongerSide) == RookValueMidgame); + assert(pos.non_pawn_material(strongerSide) == RookValueMg); assert(pos.piece_count(strongerSide, PAWN) == 2); - assert(pos.non_pawn_material(weakerSide) == RookValueMidgame); + assert(pos.non_pawn_material(weakerSide) == RookValueMg); assert(pos.piece_count(weakerSide, PAWN) == 1); Square wpsq1 = pos.piece_list(strongerSide, PAWN)[0]; @@ -643,15 +672,15 @@ ScaleFactor Endgame::operator()(const Position& pos) const { assert(pos.piece_count(weakerSide, PAWN) == 0); Square ksq = pos.king_square(weakerSide); - Bitboard pawns = pos.pieces(PAWN, strongerSide); + Bitboard pawns = pos.pieces(strongerSide, 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 - || ( file_of(ksq) == FILE_A - && !in_front_bb(strongerSide, ksq) & pawns)) + || ( file_of(ksq) == FILE_A + && !(in_front_bb(strongerSide, ksq) & pawns))) return SCALE_FACTOR_DRAW; } // Are all pawns on the 'h' file? @@ -659,8 +688,8 @@ ScaleFactor Endgame::operator()(const Position& pos) const { { // Does the defending king block the pawns? if ( square_distance(ksq, relative_square(strongerSide, SQ_H8)) <= 1 - || ( file_of(ksq) == FILE_H - && !in_front_bb(strongerSide, ksq) & pawns)) + || ( file_of(ksq) == FILE_H + && !(in_front_bb(strongerSide, ksq) & pawns))) return SCALE_FACTOR_DRAW; } return SCALE_FACTOR_NONE; @@ -674,10 +703,10 @@ ScaleFactor Endgame::operator()(const Position& pos) const { template<> ScaleFactor Endgame::operator()(const Position& pos) const { - assert(pos.non_pawn_material(strongerSide) == BishopValueMidgame); + assert(pos.non_pawn_material(strongerSide) == BishopValueMg); assert(pos.piece_count(strongerSide, BISHOP) == 1); assert(pos.piece_count(strongerSide, PAWN) == 1); - assert(pos.non_pawn_material(weakerSide) == BishopValueMidgame); + assert(pos.non_pawn_material(weakerSide) == BishopValueMg); assert(pos.piece_count(weakerSide, BISHOP) == 1); assert(pos.piece_count(weakerSide, PAWN) == 0); @@ -710,9 +739,9 @@ ScaleFactor Endgame::operator()(const Position& pos) const { return SCALE_FACTOR_DRAW; else { - Bitboard path = squares_in_front_of(strongerSide, pawnSq); + Bitboard path = forward_bb(strongerSide, pawnSq); - if (path & pos.pieces(KING, weakerSide)) + if (path & pos.pieces(weakerSide, KING)) return SCALE_FACTOR_DRAW; if ( (pos.attacks_from(weakerBishopSq) & path) @@ -729,10 +758,10 @@ ScaleFactor Endgame::operator()(const Position& pos) const { template<> ScaleFactor Endgame::operator()(const Position& pos) const { - assert(pos.non_pawn_material(strongerSide) == BishopValueMidgame); + assert(pos.non_pawn_material(strongerSide) == BishopValueMg); assert(pos.piece_count(strongerSide, BISHOP) == 1); assert(pos.piece_count(strongerSide, PAWN) == 2); - assert(pos.non_pawn_material(weakerSide) == BishopValueMidgame); + assert(pos.non_pawn_material(weakerSide) == BishopValueMg); assert(pos.piece_count(weakerSide, BISHOP) == 1); assert(pos.piece_count(weakerSide, PAWN) == 0); @@ -752,12 +781,12 @@ ScaleFactor Endgame::operator()(const Position& pos) const { if (relative_rank(strongerSide, psq1) > relative_rank(strongerSide, psq2)) { blockSq1 = psq1 + pawn_push(strongerSide); - blockSq2 = make_square(file_of(psq2), rank_of(psq1)); + blockSq2 = file_of(psq2) | rank_of(psq1); } else { blockSq1 = psq2 + pawn_push(strongerSide); - blockSq2 = make_square(file_of(psq1), rank_of(psq2)); + blockSq2 = file_of(psq1) | rank_of(psq2); } switch (file_distance(psq1, psq2)) @@ -779,14 +808,14 @@ ScaleFactor Endgame::operator()(const Position& pos) const { if ( ksq == blockSq1 && opposite_colors(ksq, wbsq) && ( bbsq == blockSq2 - || (pos.attacks_from(blockSq2) & pos.pieces(BISHOP, weakerSide)) + || (pos.attacks_from(blockSq2) & pos.pieces(weakerSide, 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(BISHOP, weakerSide)))) + || (pos.attacks_from(blockSq1) & pos.pieces(weakerSide, BISHOP)))) return SCALE_FACTOR_DRAW; else return SCALE_FACTOR_NONE; @@ -804,10 +833,10 @@ ScaleFactor Endgame::operator()(const Position& pos) const { template<> ScaleFactor Endgame::operator()(const Position& pos) const { - assert(pos.non_pawn_material(strongerSide) == BishopValueMidgame); + assert(pos.non_pawn_material(strongerSide) == BishopValueMg); assert(pos.piece_count(strongerSide, BISHOP) == 1); assert(pos.piece_count(strongerSide, PAWN) == 1); - assert(pos.non_pawn_material(weakerSide) == KnightValueMidgame); + assert(pos.non_pawn_material(weakerSide) == KnightValueMg); assert(pos.piece_count(weakerSide, KNIGHT) == 1); assert(pos.piece_count(weakerSide, PAWN) == 0); @@ -831,7 +860,7 @@ ScaleFactor Endgame::operator()(const Position& pos) const { template<> ScaleFactor Endgame::operator()(const Position& pos) const { - assert(pos.non_pawn_material(strongerSide) == KnightValueMidgame); + assert(pos.non_pawn_material(strongerSide) == KnightValueMg); assert(pos.piece_count(strongerSide, KNIGHT) == 1); assert(pos.piece_count(strongerSide, PAWN) == 1); assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO); @@ -893,5 +922,5 @@ ScaleFactor Endgame::operator()(const Position& pos) const { // Probe the KPK bitbase with the weakest side's pawn removed. If it's a draw, // it's probably at least a draw even with the pawn. - return probe_kpk_bitbase(wksq, wpsq, bksq, stm) ? SCALE_FACTOR_NONE : SCALE_FACTOR_DRAW; + return Bitbases::probe_kpk(wksq, wpsq, bksq, stm) ? SCALE_FACTOR_NONE : SCALE_FACTOR_DRAW; }