X-Git-Url: https://git.sesse.net/?p=stockfish;a=blobdiff_plain;f=src%2Fendgame.cpp;h=daef61f2f587ecb6ba858b1a985a9ae113c3415c;hp=0ee0a6eb840bf2b0eabe740b0d1faf13d50e4ee5;hb=3054db989d28bee4c491811ba81a7933eb37ba14;hpb=431c3ac485386cc10413fc8a3c7d338dcc71602d diff --git a/src/endgame.cpp b/src/endgame.cpp index 0ee0a6eb..daef61f2 100644 --- a/src/endgame.cpp +++ b/src/endgame.cpp @@ -1,7 +1,7 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad + Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -60,8 +60,8 @@ namespace { 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; + bool verify_material(const Position& pos, Color c, Value npm, int pawnsCnt) { + return pos.non_pawn_material(c) == npm && pos.count(c) == pawnsCnt; } #endif @@ -82,8 +82,7 @@ namespace { // Get the material key of 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 do the work for us. Note that the - // FEN string could correspond to an illegal position. + // and then let a Position object do the work for us. Key key(const string& code, Color c) { assert(code.length() > 0 && code.length() < 8); @@ -94,15 +93,12 @@ namespace { 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"; + string fen = sides[0] + char(8 - sides[0].length() + '0') + "/8/8/8/8/8/8/" + + sides[1] + char(8 - sides[1].length() + '0') + " w - - 0 10"; - return Position(fen, false, NULL).material_key(); + return Position(fen, false, nullptr).material_key(); } - template - void delete_endgame(const typename M::value_type& p) { delete p.second; } - } // namespace @@ -118,7 +114,6 @@ Endgames::Endgames() { add("KRKN"); add("KQKP"); add("KQKR"); - add("KBBKN"); add("KNPK"); add("KNPKB"); @@ -130,17 +125,11 @@ Endgames::Endgames() { add("KRPPKRP"); } -Endgames::~Endgames() { - - for_each(m1.begin(), m1.end(), delete_endgame); - for_each(m2.begin(), m2.end(), delete_endgame); -} -template +template void Endgames::add(const string& code) { - - map((Endgame*)0)[key(code, WHITE)] = new Endgame(WHITE); - map((Endgame*)0)[key(code, BLACK)] = new Endgame(BLACK); + map()[key(code, WHITE)] = std::unique_ptr>(new Endgame(WHITE)); + map()[key(code, BLACK)] = std::unique_ptr>(new Endgame(BLACK)); } @@ -164,11 +153,13 @@ Value Endgame::operator()(const Position& pos) const { Value result = pos.non_pawn_material(strongSide) + pos.count(strongSide) * PawnValueEg + PushToEdges[loserKSq] - + PushClose[square_distance(winnerKSq, loserKSq)]; + + PushClose[distance(winnerKSq, loserKSq)]; if ( pos.count(strongSide) || pos.count(strongSide) - || pos.bishop_pair(strongSide)) + ||(pos.count(strongSide) && pos.count(strongSide)) + ||(pos.count(strongSide) > 1 && opposite_colors(pos.list(strongSide)[0], + pos.list(strongSide)[1]))) result += VALUE_KNOWN_WIN; return strongSide == pos.side_to_move() ? result : -result; @@ -197,7 +188,7 @@ Value Endgame::operator()(const Position& pos) const { } Value result = VALUE_KNOWN_WIN - + PushClose[square_distance(winnerKSq, loserKSq)] + + PushClose[distance(winnerKSq, loserKSq)] + PushToCorners[loserKSq]; return strongSide == pos.side_to_move() ? result : -result; @@ -218,7 +209,7 @@ Value Endgame::operator()(const Position& pos) const { Color us = strongSide == pos.side_to_move() ? WHITE : BLACK; - if (!Bitbases::probe_kpk(wksq, psq, bksq, us)) + if (!Bitbases::probe(wksq, psq, bksq, us)) return VALUE_DRAW; Value result = VALUE_KNOWN_WIN + PawnValueEg + Value(rank_of(psq)); @@ -242,32 +233,31 @@ Value Endgame::operator()(const Position& pos) const { 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; + Square queeningSq = make_square(file_of(psq), RANK_1); Value result; // If the stronger side's king is in front of the pawn, it's a win if (wksq < psq && file_of(wksq) == file_of(psq)) - result = RookValueEg - Value(square_distance(wksq, psq)); + result = RookValueEg - distance(wksq, psq); // 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() == weakSide) - && square_distance(bksq, rsq) >= 3) - result = RookValueEg - Value(square_distance(wksq, psq)); + else if ( distance(bksq, psq) >= 3 + (pos.side_to_move() == weakSide) + && distance(bksq, rsq) >= 3) + result = RookValueEg - distance(wksq, psq); // If the pawn is far advanced and supported by the defending king, // the position is drawish else if ( rank_of(bksq) <= RANK_3 - && square_distance(bksq, psq) == 1 + && distance(bksq, psq) == 1 && rank_of(wksq) >= RANK_4 - && square_distance(wksq, psq) > 2 + (pos.side_to_move() == strongSide)) - result = Value(80 - square_distance(wksq, psq) * 8); + && distance(wksq, psq) > 2 + (pos.side_to_move() == strongSide)) + result = Value(80) - 8 * distance(wksq, psq); else - result = Value(200) - - Value(square_distance(wksq, psq + DELTA_S) * 8) - + Value(square_distance(bksq, psq + DELTA_S) * 8) - + Value(square_distance(psq, queeningSq) * 8); + result = Value(200) - 8 * ( distance(wksq, psq + DELTA_S) + - distance(bksq, psq + DELTA_S) + - distance(psq, queeningSq)); return strongSide == pos.side_to_move() ? result : -result; } @@ -296,7 +286,7 @@ Value Endgame::operator()(const Position& pos) const { Square bksq = pos.king_square(weakSide); Square bnsq = pos.list(weakSide)[0]; - Value result = Value(PushToEdges[bksq] + PushAway[square_distance(bksq, bnsq)]); + Value result = Value(PushToEdges[bksq] + PushAway[distance(bksq, bnsq)]); return strongSide == pos.side_to_move() ? result : -result; } @@ -315,10 +305,10 @@ Value Endgame::operator()(const Position& pos) const { Square loserKSq = pos.king_square(weakSide); Square pawnSq = pos.list(weakSide)[0]; - Value result = Value(PushClose[square_distance(winnerKSq, loserKSq)]); + Value result = Value(PushClose[distance(winnerKSq, loserKSq)]); if ( relative_rank(weakSide, pawnSq) != RANK_7 - || square_distance(loserKSq, pawnSq) != 1 + || distance(loserKSq, pawnSq) != 1 || !((FileABB | FileCBB | FileFBB | FileHBB) & pawnSq)) result += QueenValueEg - PawnValueEg; @@ -342,30 +332,7 @@ Value Endgame::operator()(const Position& pos) const { Value result = QueenValueEg - RookValueEg + PushToEdges[loserKSq] - + PushClose[square_distance(winnerKSq, loserKSq)]; - - return strongSide == pos.side_to_move() ? result : -result; -} - - -/// KBB vs KN. This is almost always a win. We try to push the 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(verify_material(pos, strongSide, 2 * BishopValueMg, 0)); - assert(verify_material(pos, weakSide, KnightValueMg, 0)); - - Square winnerKSq = pos.king_square(strongSide); - Square loserKSq = pos.king_square(weakSide); - Square knightSq = pos.list(weakSide)[0]; - - Value result = VALUE_KNOWN_WIN - + PushToCorners[loserKSq] - + PushClose[square_distance(winnerKSq, loserKSq)] - + PushAway[square_distance(loserKSq, knightSq)]; + + PushClose[distance(winnerKSq, loserKSq)]; return strongSide == pos.side_to_move() ? result : -result; } @@ -373,7 +340,6 @@ Value Endgame::operator()(const Position& pos) const { /// Some cases of trivial draws template<> Value Endgame::operator()(const Position&) const { return VALUE_DRAW; } -template<> Value Endgame::operator()(const Position&) const { return VALUE_DRAW; } /// KB and one or more pawns vs K. It checks for draws with rook pawns and @@ -397,11 +363,11 @@ ScaleFactor Endgame::operator()(const Position& pos) const { && !(pawns & ~file_bb(pawnFile))) { Square bishopSq = pos.list(strongSide)[0]; - Square queeningSq = relative_square(strongSide, pawnFile | RANK_8); + Square queeningSq = relative_square(strongSide, make_square(pawnFile, RANK_8)); Square kingSq = pos.king_square(weakSide); if ( opposite_colors(queeningSq, bishopSq) - && square_distance(queeningSq, kingSq) <= 1) + && distance(queeningSq, kingSq) <= 1) return SCALE_FACTOR_DRAW; } @@ -424,8 +390,8 @@ ScaleFactor Endgame::operator()(const Position& pos) const { && (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); + int strongKingDist = distance(weakPawnSq, strongKingSq); + int weakKingDist = distance(weakPawnSq, weakKingSq); // 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 @@ -489,13 +455,13 @@ ScaleFactor Endgame::operator()(const Position& pos) const { File f = file_of(wpsq); Rank r = rank_of(wpsq); - Square queeningSq = f | RANK_8; + Square queeningSq = make_square(f, RANK_8); 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. if ( r <= RANK_5 - && square_distance(bksq, queeningSq) <= 1 + && distance(bksq, queeningSq) <= 1 && wksq <= SQ_H5 && (rank_of(brsq) == RANK_6 || (r <= RANK_3 && rank_of(wrsq) != RANK_6))) return SCALE_FACTOR_DRAW; @@ -503,15 +469,15 @@ ScaleFactor Endgame::operator()(const Position& pos) const { // The defending side saves a draw by checking from behind in case the pawn // has advanced to the 6th rank with the king behind. if ( r == RANK_6 - && square_distance(bksq, queeningSq) <= 1 + && distance(bksq, queeningSq) <= 1 && rank_of(wksq) + tempo <= RANK_6 - && (rank_of(brsq) == RANK_1 || (!tempo && abs(file_of(brsq) - f) >= 3))) + && (rank_of(brsq) == RANK_1 || (!tempo && distance(brsq, wpsq) >= 3))) return SCALE_FACTOR_DRAW; if ( r >= RANK_6 && bksq == queeningSq && rank_of(brsq) == RANK_1 - && (!tempo || square_distance(wksq, wpsq) >= 2)) + && (!tempo || distance(wksq, wpsq) >= 2)) return SCALE_FACTOR_DRAW; // White pawn on a7 and rook on a8 is a draw if black's king is on g7 or h7 @@ -527,8 +493,8 @@ ScaleFactor Endgame::operator()(const Position& pos) const { // away, it's a draw. if ( r <= RANK_5 && bksq == wpsq + DELTA_N - && square_distance(wksq, wpsq) - tempo >= 2 - && square_distance(wksq, brsq) - tempo >= 2) + && distance(wksq, wpsq) - tempo >= 2 + && distance(wksq, brsq) - tempo >= 2) return SCALE_FACTOR_DRAW; // Pawn on the 7th rank supported by the rook from behind usually wins if the @@ -538,22 +504,22 @@ ScaleFactor Endgame::operator()(const Position& pos) const { && f != FILE_A && file_of(wrsq) == f && wrsq != queeningSq - && (square_distance(wksq, queeningSq) < square_distance(bksq, queeningSq) - 2 + tempo) - && (square_distance(wksq, queeningSq) < square_distance(bksq, wrsq) + tempo)) - return ScaleFactor(SCALE_FACTOR_MAX - 2 * square_distance(wksq, queeningSq)); + && (distance(wksq, queeningSq) < distance(bksq, queeningSq) - 2 + tempo) + && (distance(wksq, queeningSq) < distance(bksq, wrsq) + tempo)) + return ScaleFactor(SCALE_FACTOR_MAX - 2 * distance(wksq, queeningSq)); // Similar to the above, but with the pawn further back if ( f != FILE_A && file_of(wrsq) == f && wrsq < wpsq - && (square_distance(wksq, queeningSq) < square_distance(bksq, queeningSq) - 2 + tempo) - && (square_distance(wksq, wpsq + DELTA_N) < square_distance(bksq, wpsq + DELTA_N) - 2 + tempo) - && ( square_distance(bksq, wrsq) + tempo >= 3 - || ( square_distance(wksq, queeningSq) < square_distance(bksq, wrsq) + tempo - && (square_distance(wksq, wpsq + DELTA_N) < square_distance(bksq, wrsq) + tempo)))) + && (distance(wksq, queeningSq) < distance(bksq, queeningSq) - 2 + tempo) + && (distance(wksq, wpsq + DELTA_N) < distance(bksq, wpsq + DELTA_N) - 2 + tempo) + && ( distance(bksq, wrsq) + tempo >= 3 + || ( distance(wksq, queeningSq) < distance(bksq, wrsq) + tempo + && (distance(wksq, wpsq + DELTA_N) < distance(bksq, wrsq) + tempo)))) return ScaleFactor( SCALE_FACTOR_MAX - - 8 * square_distance(wpsq, queeningSq) - - 2 * square_distance(wksq, queeningSq)); + - 8 * distance(wpsq, queeningSq) + - 2 * distance(wksq, queeningSq)); // If the pawn is not far advanced and the defending king is somewhere in // the pawn's path, it's probably a draw. @@ -561,9 +527,9 @@ ScaleFactor Endgame::operator()(const Position& pos) const { { if (file_of(bksq) == file_of(wpsq)) return ScaleFactor(10); - if ( abs(file_of(bksq) - file_of(wpsq)) == 1 - && square_distance(wksq, bksq) > 2) - return ScaleFactor(24 - 2 * square_distance(wksq, bksq)); + if ( distance(bksq, wpsq) == 1 + && distance(wksq, bksq) > 2) + return ScaleFactor(24 - 2 * distance(wksq, bksq)); } return SCALE_FACTOR_NONE; } @@ -590,7 +556,7 @@ ScaleFactor Endgame::operator()(const Position& pos) const { // corner but not trapped there. if (rk == RANK_5 && !opposite_colors(bsq, psq)) { - int d = square_distance(psq + 3 * push, ksq); + int d = distance(psq + 3 * push, ksq); if (d <= 2 && !(d == 0 && ksq == pos.king_square(strongSide) + 2 * push)) return ScaleFactor(24); @@ -603,9 +569,9 @@ ScaleFactor Endgame::operator()(const Position& pos) const { // pawn from a reasonable distance and the defending king is near // the corner if ( rk == RANK_6 - && square_distance(psq + 2 * push, ksq) <= 1 + && distance(psq + 2 * push, ksq) <= 1 && (PseudoAttacks[BISHOP][bsq] & (psq + push)) - && file_distance(bsq, psq) >= 2) + && distance(bsq, psq) >= 2) return ScaleFactor(8); } @@ -630,8 +596,8 @@ ScaleFactor Endgame::operator()(const Position& pos) const { Rank r = std::max(relative_rank(strongSide, wpsq1), relative_rank(strongSide, wpsq2)); - if ( file_distance(bksq, wpsq1) <= 1 - && file_distance(bksq, wpsq2) <= 1 + if ( distance(bksq, wpsq1) <= 1 + && distance(bksq, wpsq2) <= 1 && relative_rank(strongSide, bksq) > r) { switch (r) { @@ -664,7 +630,7 @@ ScaleFactor Endgame::operator()(const Position& pos) const { // the king is within one file of the pawns, it's a draw. if ( !(pawns & ~in_front_bb(weakSide, rank_of(ksq))) && !((pawns & ~FileABB) && (pawns & ~FileHBB)) - && file_distance(ksq, psq) <= 1) + && distance(ksq, psq) <= 1) return SCALE_FACTOR_DRAW; return SCALE_FACTOR_NONE; @@ -716,7 +682,7 @@ ScaleFactor Endgame::operator()(const Position& pos) const { return SCALE_FACTOR_DRAW; if ( (pos.attacks_from(weakBishopSq) & path) - && square_distance(weakBishopSq, pawnSq) >= 3) + && distance(weakBishopSq, pawnSq) >= 3) return SCALE_FACTOR_DRAW; } } @@ -747,15 +713,15 @@ ScaleFactor Endgame::operator()(const Position& pos) const { if (relative_rank(strongSide, psq1) > relative_rank(strongSide, psq2)) { blockSq1 = psq1 + pawn_push(strongSide); - blockSq2 = file_of(psq2) | rank_of(psq1); + blockSq2 = make_square(file_of(psq2), rank_of(psq1)); } else { blockSq1 = psq2 + pawn_push(strongSide); - blockSq2 = file_of(psq1) | rank_of(psq2); + blockSq2 = make_square(file_of(psq1), rank_of(psq2)); } - switch (file_distance(psq1, psq2)) + switch (distance(psq1, psq2)) { case 0: // Both pawns are on the same file. It's an easy draw if the defender firmly @@ -775,7 +741,7 @@ ScaleFactor Endgame::operator()(const Position& pos) const { && opposite_colors(ksq, wbsq) && ( bbsq == blockSq2 || (pos.attacks_from(blockSq2) & pos.pieces(weakSide, BISHOP)) - || abs(r1 - r2) >= 2)) + || distance(r1, r2) >= 2)) return SCALE_FACTOR_DRAW; else if ( ksq == blockSq2 @@ -817,7 +783,7 @@ ScaleFactor Endgame::operator()(const Position& pos) const { /// KNP vs K. There is a single rule: if the pawn is a rook pawn on the 7th rank -/// and the defending king prevents the pawn from advancing the position is drawn. +/// and the defending king prevents the pawn from advancing, the position is drawn. template<> ScaleFactor Endgame::operator()(const Position& pos) const { @@ -828,7 +794,7 @@ ScaleFactor Endgame::operator()(const Position& pos) const { Square pawnSq = normalize(pos, strongSide, pos.list(strongSide)[0]); Square weakKingSq = normalize(pos, strongSide, pos.king_square(weakSide)); - if (pawnSq == SQ_A7 && square_distance(SQ_A8, weakKingSq) <= 1) + if (pawnSq == SQ_A7 && distance(SQ_A8, weakKingSq) <= 1) return SCALE_FACTOR_DRAW; return SCALE_FACTOR_NONE; @@ -847,7 +813,7 @@ ScaleFactor Endgame::operator()(const Position& pos) const { // 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(strongSide, pawnSq) & pos.attacks_from(bishopSq)) - return ScaleFactor(square_distance(weakKingSq, pawnSq)); + return ScaleFactor(distance(weakKingSq, pawnSq)); return SCALE_FACTOR_NONE; } @@ -878,5 +844,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 Bitbases::probe_kpk(wksq, psq, bksq, us) ? SCALE_FACTOR_NONE : SCALE_FACTOR_DRAW; + return Bitbases::probe(wksq, psq, bksq, us) ? SCALE_FACTOR_NONE : SCALE_FACTOR_DRAW; }