X-Git-Url: https://git.sesse.net/?p=stockfish;a=blobdiff_plain;f=src%2Fendgame.cpp;h=ef55119b76f811f4fb75e038000af6f7491336f5;hp=8280131753b859ea1ea784230eb1abde1c23f7d8;hb=7c1f8dbde93267c7958a4de5e167a43e38c9e1e9;hpb=14df99130f53a2a5f57260eb830b4c0029bd4e99 diff --git a/src/endgame.cpp b/src/endgame.cpp index 82801317..ef55119b 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-2010 Marco Costalba, Joona Kiiski, Tord Romstad + Copyright (C) 2008-2012 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 @@ -17,17 +17,16 @@ along with this program. If not, see . */ -#include #include +#include +#include "bitboard.h" #include "bitcount.h" #include "endgame.h" -#include "pawns.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 @@ -60,43 +59,33 @@ namespace { // the two kings in basic endgames. const int DistanceBonus[8] = { 0, 0, 100, 80, 60, 40, 20, 10 }; - // Build corresponding key code for the opposite color: "KBPKN" -> "KNKBP" - const string swap_colors(const string& keyCode) { + // 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 + // fen string could correspond to an illegal position. + Key key(const string& code, Color c) { - size_t idx = keyCode.find('K', 1); - return keyCode.substr(idx) + keyCode.substr(0, idx); - } + assert(code.length() > 0 && code.length() < 8); + assert(code[0] == 'K'); - // Get the material key of a position out of the given endgame key code - // like "KBPKN". The trick here is to first build up a FEN string and then - // let a Position object to do the work for us. Note that the FEN string - // could correspond to an illegal position. - Key mat_key(const string& keyCode) { + string sides[] = { code.substr(code.find('K', 1)), // Weaker + code.substr(0, code.find('K', 1)) }; // Stronger - assert(keyCode.length() > 0 && keyCode.length() < 8); - assert(keyCode[0] == 'K'); + std::transform(sides[c].begin(), sides[c].end(), sides[c].begin(), tolower); - string fen; - size_t i = 0; + string fen = sides[0] + char('0' + int(8 - code.length())) + + sides[1] + "/8/8/8/8/8/8/8 w - - 0 10"; - // First add white and then black pieces - do fen += keyCode[i]; while (keyCode[++i] != 'K'); - do fen += char(tolower(keyCode[i])); while (++i < keyCode.length()); - - // Add file padding and remaining empty ranks - fen += string(1, '0' + int(8 - keyCode.length())) + "/8/8/8/8/8/8/8 w - - 0 10"; - - // Build a Position out of the fen string and get its material key - return Position(fen, false, 0).get_material_key(); + return Position(fen, false, NULL).material_key(); } -} // namespace + template + void delete_endgame(const typename M::value_type& p) { delete p.second; } +} // namespace -/// Endgames member definitions -template<> const Endgames::M1& Endgames::map() const { return m1; } -template<> const Endgames::M2& Endgames::map() const { return m2; } +/// Endgames members definitions Endgames::Endgames() { @@ -119,21 +108,15 @@ Endgames::Endgames() { Endgames::~Endgames() { - for (M1::const_iterator it = m1.begin(); it != m1.end(); ++it) - delete it->second; - - for (M2::const_iterator it = m2.begin(); it != m2.end(); ++it) - delete it->second; + for_each(m1.begin(), m1.end(), delete_endgame); + for_each(m2.begin(), m2.end(), delete_endgame); } template -void Endgames::add(const string& keyCode) { - - typedef typename eg_family::type T; - typedef typename Map::type M; +void Endgames::add(const string& code) { - const_cast(map()).insert(std::make_pair(mat_key(keyCode), new Endgame(WHITE))); - const_cast(map()).insert(std::make_pair(mat_key(swap_colors(keyCode)), new Endgame(BLACK))); + map((Endgame*)0)[key(code, WHITE)] = new Endgame(WHITE); + map((Endgame*)0)[key(code, BLACK)] = new Endgame(BLACK); } @@ -147,6 +130,13 @@ Value Endgame::operator()(const Position& pos) const { assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO); assert(pos.piece_count(weakerSide, PAWN) == VALUE_ZERO); + // Stalemate detection with lone king + if ( pos.side_to_move() == weakerSide + && !pos.in_check() + && !MoveList(pos).size()) { + return VALUE_DRAW; + } + Square winnerKSq = pos.king_square(strongerSide); Square loserKSq = pos.king_square(weakerSide); @@ -157,9 +147,9 @@ Value Endgame::operator()(const Position& pos) const { if ( pos.piece_count(strongerSide, QUEEN) || pos.piece_count(strongerSide, ROOK) - || pos.piece_count(strongerSide, BISHOP) > 1) - // TODO: check for two equal-colored bishops! - result += VALUE_KNOWN_WIN; + || pos.bishop_pair(strongerSide)) { + result += VALUE_KNOWN_WIN; + } return strongerSide == pos.side_to_move() ? result : -result; } @@ -219,10 +209,10 @@ Value Endgame::operator()(const Position& pos) const { } else { - wksq = flip(pos.king_square(BLACK)); - bksq = flip(pos.king_square(WHITE)); - wpsq = flip(pos.piece_list(BLACK, PAWN)[0]); - stm = flip(pos.side_to_move()); + wksq = ~pos.king_square(BLACK); + bksq = ~pos.king_square(WHITE); + wpsq = ~pos.piece_list(BLACK, PAWN)[0]; + stm = ~pos.side_to_move(); } if (file_of(wpsq) >= FILE_E) @@ -232,7 +222,7 @@ 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 @@ -265,13 +255,13 @@ Value Endgame::operator()(const Position& pos) const { if (strongerSide == BLACK) { - wksq = flip(wksq); - wrsq = flip(wrsq); - bksq = flip(bksq); - bpsq = flip(bpsq); + wksq = ~wksq; + wrsq = ~wrsq; + bksq = ~bksq; + 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 @@ -383,7 +373,7 @@ Value Endgame::operator()(const Position& pos) const { result += Value(square_distance(bksq, nsq) * 32); // Bonus for restricting the knight's mobility - result += Value((8 - count_1s(pos.attacks_from(nsq))) * 8); + result += Value((8 - popcount(pos.attacks_from(nsq))) * 8); return strongerSide == pos.side_to_move() ? result : -result; } @@ -415,22 +405,22 @@ ScaleFactor Endgame::operator()(const Position& pos) const { // 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) && 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 neighboring file. Find the rank of the + // file of the pawn(s) or the adjacent file. Find the rank of the // frontmost pawn. Rank rank; if (strongerSide == WHITE) @@ -469,12 +459,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; @@ -505,11 +495,11 @@ ScaleFactor Endgame::operator()(const Position& pos) const { // pawn is on the left half of the board. if (strongerSide == BLACK) { - wksq = flip(wksq); - wrsq = flip(wrsq); - wpsq = flip(wpsq); - bksq = flip(bksq); - brsq = flip(brsq); + wksq = ~wksq; + wrsq = ~wrsq; + wpsq = ~wpsq; + bksq = ~bksq; + brsq = ~brsq; } if (file_of(wpsq) > FILE_D) { @@ -522,7 +512,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 @@ -652,14 +642,14 @@ 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 + || ( file_of(ksq) == FILE_A && !in_front_bb(strongerSide, ksq) & pawns)) return SCALE_FACTOR_DRAW; } @@ -668,7 +658,7 @@ 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 + || ( file_of(ksq) == FILE_H && !in_front_bb(strongerSide, ksq) & pawns)) return SCALE_FACTOR_DRAW; } @@ -719,9 +709,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) @@ -761,12 +751,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)) @@ -782,20 +772,20 @@ ScaleFactor Endgame::operator()(const Position& pos) const { return SCALE_FACTOR_NONE; case 1: - // Pawns on neighboring files. Draw if defender firmly controls the square + // 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. 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; @@ -881,10 +871,10 @@ ScaleFactor Endgame::operator()(const Position& pos) const { if (strongerSide == BLACK) { - wksq = flip(wksq); - bksq = flip(bksq); - wpsq = flip(wpsq); - stm = flip(stm); + wksq = ~wksq; + bksq = ~bksq; + wpsq = ~wpsq; + stm = ~stm; } if (file_of(wpsq) >= FILE_E) @@ -902,5 +892,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; }