X-Git-Url: https://git.sesse.net/?p=stockfish;a=blobdiff_plain;f=src%2Fendgame.cpp;h=b78d1ba909f64f19845cc29f7b4d93ea2a64be7a;hp=5e021f1132917aa36d397a1f090f71938c72cb99;hb=45b0aea875860e9f0fe2d0435ee6163906639194;hpb=c2fc80e5d16339266b5bf6687fa843ff65de3b3e diff --git a/src/endgame.cpp b/src/endgame.cpp index 5e021f11..b78d1ba9 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-2012 Marco Costalba, Joona Kiiski, Tord Romstad + Copyright (C) 2008-2013 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 @@ -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 + // Table used to drive the king towards the edge of the board // in KX vs K and KQ vs KR endgames. - const int MateTable[64] = { + const int PushToEdges[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, @@ -43,9 +42,9 @@ namespace { 100, 90, 80, 70, 70, 80, 90, 100, }; - // Table used to drive the defending king towards a corner square of the + // Table used to drive the king towards a corner square of the // right color in KBN vs K endgames. - const int KBNKMateTable[64] = { + const int PushToCorners[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, @@ -56,9 +55,9 @@ namespace { 130, 140, 150, 160, 170, 180, 190, 200 }; - // The attacking side is given a descending bonus based on distance between - // the two kings in basic endgames. - const int DistanceBonus[8] = { 0, 0, 100, 80, 60, 40, 20, 10 }; + // 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, 5, 20, 40, 60, 80, 90, 100 }; // 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 @@ -72,7 +71,7 @@ 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"; @@ -96,10 +95,12 @@ Endgames::Endgames() { add("KRKP"); add("KRKB"); add("KRKN"); + add("KQKP"); add("KQKR"); add("KBBKN"); add("KNPK"); + add("KNPKB"); add("KRPKR"); add("KBPKB"); add("KBPKN"); @@ -129,28 +130,25 @@ template<> 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.count(weakerSide)); + assert(!pos.checkers()); // Eval is never called when in check // Stalemate detection with lone king - if ( pos.side_to_move() == weakerSide - && !pos.in_check() - && !MoveList(pos).size()) { - return VALUE_DRAW; - } + if (pos.side_to_move() == weakerSide && !MoveList(pos).size()) + return VALUE_DRAW; Square winnerKSq = pos.king_square(strongerSide); Square loserKSq = pos.king_square(weakerSide); Value result = pos.non_pawn_material(strongerSide) - + pos.piece_count(strongerSide, PAWN) * PawnValueEndgame - + MateTable[loserKSq] - + DistanceBonus[square_distance(winnerKSq, loserKSq)]; - - if ( pos.piece_count(strongerSide, QUEEN) - || pos.piece_count(strongerSide, ROOK) - || pos.bishop_pair(strongerSide)) { - result += VALUE_KNOWN_WIN; - } + + pos.count(strongerSide) * PawnValueEg + + PushToEdges[loserKSq] + + PushClose[square_distance(winnerKSq, loserKSq)]; + + if ( pos.count(strongerSide) + || pos.count(strongerSide) + || pos.bishop_pair(strongerSide)) + result += VALUE_KNOWN_WIN; return strongerSide == pos.side_to_move() ? result : -result; } @@ -161,29 +159,29 @@ 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.piece_count(weakerSide, PAWN) == VALUE_ZERO); - assert(pos.non_pawn_material(strongerSide) == KnightValueMidgame + BishopValueMidgame); - assert(pos.piece_count(strongerSide, BISHOP) == 1); - assert(pos.piece_count(strongerSide, KNIGHT) == 1); - assert(pos.piece_count(strongerSide, PAWN) == 0); + assert(pos.count(strongerSide) == 1); + assert(pos.count(strongerSide) == 1); + assert(pos.count< PAWN>(strongerSide) == 0); + assert(pos.count< PAWN>(weakerSide ) == 0); Square winnerKSq = pos.king_square(strongerSide); Square loserKSq = pos.king_square(weakerSide); - Square bishopSquare = pos.piece_list(strongerSide, BISHOP)[0]; + Square bishopSq = pos.list(strongerSide)[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); } Value result = VALUE_KNOWN_WIN - + DistanceBonus[square_distance(winnerKSq, loserKSq)] - + KBNKMateTable[loserKSq]; + + PushClose[square_distance(winnerKSq, loserKSq)] + + PushToCorners[loserKSq]; return strongerSide == pos.side_to_move() ? result : -result; } @@ -195,40 +193,33 @@ 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.piece_count(strongerSide, PAWN) == 1); - assert(pos.piece_count(weakerSide, PAWN) == 0); + assert(pos.count(strongerSide) == 1); + assert(pos.count(weakerSide ) == 0); - Square wksq, bksq, wpsq; - Color stm; + Square wksq = pos.king_square(strongerSide); + Square bksq = pos.king_square(weakerSide); + Square psq = pos.list(strongerSide)[0]; + Color us = pos.side_to_move(); - if (strongerSide == WHITE) - { - wksq = pos.king_square(WHITE); - bksq = pos.king_square(BLACK); - wpsq = pos.piece_list(WHITE, PAWN)[0]; - stm = pos.side_to_move(); - } - else + if (strongerSide == BLACK) { - wksq = ~pos.king_square(BLACK); - bksq = ~pos.king_square(WHITE); - wpsq = ~pos.piece_list(BLACK, PAWN)[0]; - stm = ~pos.side_to_move(); + wksq = ~wksq; + bksq = ~bksq; + psq = ~psq; + us = ~us; } - if (file_of(wpsq) >= FILE_E) + if (file_of(psq) >= FILE_E) { wksq = mirror(wksq); bksq = mirror(bksq); - wpsq = mirror(wpsq); + psq = mirror(psq); } - if (!probe_kpk_bitbase(wksq, wpsq, bksq, stm)) + if (!Bitbases::probe_kpk(wksq, psq, bksq, us)) return VALUE_DRAW; - Value result = VALUE_KNOWN_WIN - + PawnValueEndgame - + Value(rank_of(wpsq)); + Value result = VALUE_KNOWN_WIN + PawnValueEg + Value(rank_of(psq)); return strongerSide == pos.side_to_move() ? result : -result; } @@ -241,53 +232,50 @@ Value Endgame::operator()(const Position& pos) const { template<> Value Endgame::operator()(const Position& pos) const { - assert(pos.non_pawn_material(strongerSide) == RookValueMidgame); - assert(pos.piece_count(strongerSide, PAWN) == 0); + assert(pos.non_pawn_material(strongerSide) == RookValueMg); assert(pos.non_pawn_material(weakerSide) == 0); - assert(pos.piece_count(weakerSide, PAWN) == 1); - - Square wksq, wrsq, bksq, bpsq; - int tempo = (pos.side_to_move() == strongerSide); + assert(pos.count(strongerSide) == 0); + assert(pos.count(weakerSide ) == 1); - wksq = pos.king_square(strongerSide); - wrsq = pos.piece_list(strongerSide, ROOK)[0]; - bksq = pos.king_square(weakerSide); - bpsq = pos.piece_list(weakerSide, PAWN)[0]; + Square wksq = pos.king_square(strongerSide); + Square bksq = pos.king_square(weakerSide); + Square rsq = pos.list(strongerSide)[0]; + Square psq = pos.list(weakerSide)[0]; if (strongerSide == BLACK) { wksq = ~wksq; - wrsq = ~wrsq; bksq = ~bksq; - bpsq = ~bpsq; + rsq = ~rsq; + psq = ~psq; } - Square queeningSq = make_square(file_of(bpsq), RANK_1); + Square queeningSq = 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 < bpsq && file_of(wksq) == file_of(bpsq)) - result = RookValueEndgame - Value(square_distance(wksq, bpsq)); + if (wksq < psq && file_of(wksq) == file_of(psq)) + result = RookValueEg - Value(square_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, bpsq) - (tempo ^ 1) >= 3 - && square_distance(bksq, wrsq) >= 3) - result = RookValueEndgame - Value(square_distance(wksq, bpsq)); + // it's a win. + else if ( square_distance(bksq, psq) >= 3 + (pos.side_to_move() == weakerSide) + && square_distance(bksq, rsq) >= 3) + result = RookValueEg - Value(square_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, bpsq) == 1 + && square_distance(bksq, psq) == 1 && rank_of(wksq) >= RANK_4 - && square_distance(wksq, bpsq) - tempo > 2) - result = Value(80 - square_distance(wksq, bpsq) * 8); + && square_distance(wksq, psq) > 2 + (pos.side_to_move() == strongerSide)) + result = Value(80 - square_distance(wksq, psq) * 8); else result = Value(200) - - Value(square_distance(wksq, bpsq + DELTA_S) * 8) - + Value(square_distance(bksq, bpsq + DELTA_S) * 8) - + Value(square_distance(bpsq, queeningSq) * 8); + - Value(square_distance(wksq, psq + DELTA_S) * 8) + + Value(square_distance(bksq, psq + DELTA_S) * 8) + + Value(square_distance(psq, queeningSq) * 8); return strongerSide == pos.side_to_move() ? result : -result; } @@ -298,13 +286,13 @@ Value Endgame::operator()(const Position& pos) const { template<> Value Endgame::operator()(const Position& pos) const { - assert(pos.non_pawn_material(strongerSide) == RookValueMidgame); - assert(pos.piece_count(strongerSide, PAWN) == 0); - assert(pos.non_pawn_material(weakerSide) == BishopValueMidgame); - assert(pos.piece_count(weakerSide, PAWN) == 0); - assert(pos.piece_count(weakerSide, BISHOP) == 1); + 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); - Value result = Value(MateTable[pos.king_square(weakerSide)]); + Value result = Value(PushToEdges[pos.king_square(weakerSide)]); return strongerSide == pos.side_to_move() ? result : -result; } @@ -314,17 +302,41 @@ Value Endgame::operator()(const Position& pos) const { template<> Value Endgame::operator()(const Position& pos) const { - assert(pos.non_pawn_material(strongerSide) == RookValueMidgame); - assert(pos.piece_count(strongerSide, PAWN) == 0); - assert(pos.non_pawn_material(weakerSide) == KnightValueMidgame); - assert(pos.piece_count(weakerSide, PAWN) == 0); - assert(pos.piece_count(weakerSide, KNIGHT) == 1); - - const int penalty[8] = { 0, 10, 14, 20, 30, 42, 58, 80 }; + 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); Square bksq = pos.king_square(weakerSide); - Square bnsq = pos.piece_list(weakerSide, KNIGHT)[0]; - Value result = Value(MateTable[bksq] + penalty[square_distance(bksq, bnsq)]); + Square bnsq = pos.list(weakerSide)[0]; + Value result = Value(PushToEdges[bksq] + PushAway[square_distance(bksq, bnsq)]); + return strongerSide == pos.side_to_move() ? result : -result; +} + + +/// 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.non_pawn_material(weakerSide ) == VALUE_ZERO); + assert(pos.count(strongerSide) == 0); + assert(pos.count(weakerSide ) == 1); + + Square winnerKSq = pos.king_square(strongerSide); + Square loserKSq = pos.king_square(weakerSide); + Square pawnSq = pos.list(weakerSide)[0]; + + Value result = Value(PushClose[square_distance(winnerKSq, loserKSq)]); + + if ( relative_rank(weakerSide, pawnSq) != RANK_7 + || square_distance(loserKSq, pawnSq) != 1 + || !((FileABB | FileCBB | FileFBB | FileHBB) & pawnSq)) + result += QueenValueEg - PawnValueEg; + return strongerSide == pos.side_to_move() ? result : -result; } @@ -337,60 +349,53 @@ Value Endgame::operator()(const Position& pos) const { template<> Value Endgame::operator()(const Position& pos) const { - assert(pos.non_pawn_material(strongerSide) == QueenValueMidgame); - assert(pos.piece_count(strongerSide, PAWN) == 0); - assert(pos.non_pawn_material(weakerSide) == RookValueMidgame); - assert(pos.piece_count(weakerSide, PAWN) == 0); + assert(pos.non_pawn_material(strongerSide) == QueenValueMg); + assert(pos.non_pawn_material(weakerSide ) == RookValueMg); + assert(pos.count(strongerSide) == 0); + assert(pos.count(weakerSide ) == 0); Square winnerKSq = pos.king_square(strongerSide); Square loserKSq = pos.king_square(weakerSide); - Value result = QueenValueEndgame - - RookValueEndgame - + MateTable[loserKSq] - + DistanceBonus[square_distance(winnerKSq, loserKSq)]; + Value result = QueenValueEg + - RookValueEg + + PushToEdges[loserKSq] + + PushClose[square_distance(winnerKSq, loserKSq)]; return strongerSide == 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.piece_count(strongerSide, BISHOP) == 2); - assert(pos.non_pawn_material(strongerSide) == 2*BishopValueMidgame); - assert(pos.piece_count(weakerSide, KNIGHT) == 1); - assert(pos.non_pawn_material(weakerSide) == KnightValueMidgame); + 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)); - Value result = BishopValueEndgame; - Square wksq = pos.king_square(strongerSide); - Square bksq = pos.king_square(weakerSide); - Square nsq = pos.piece_list(weakerSide, KNIGHT)[0]; - - // Bonus for attacking king close to defending king - result += Value(DistanceBonus[square_distance(wksq, bksq)]); - - // Bonus for driving the defending king and knight apart - result += Value(square_distance(bksq, nsq) * 32); + Square winnerKSq = pos.king_square(strongerSide); + Square loserKSq = pos.king_square(weakerSide); + Square knightSq = pos.list(weakerSide)[0]; - // Bonus for restricting the knight's mobility - result += Value((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; } -/// K and two minors vs K and one or two minors or K and two knights against -/// king alone are always draw. -template<> -Value Endgame::operator()(const Position&) const { - return VALUE_DRAW; -} +/// 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; } -template<> -Value Endgame::operator()(const Position&) const { - return VALUE_DRAW; -} /// K, bishop and one or more pawns vs K. It checks for draws with rook pawns and /// a bishop of the wrong color. If such a draw is detected, SCALE_FACTOR_DRAW @@ -399,22 +404,22 @@ Value Endgame::operator()(const Position&) const { template<> ScaleFactor Endgame::operator()(const Position& pos) const { - assert(pos.non_pawn_material(strongerSide) == BishopValueMidgame); - assert(pos.piece_count(strongerSide, BISHOP) == 1); - assert(pos.piece_count(strongerSide, PAWN) >= 1); + assert(pos.non_pawn_material(strongerSide) == BishopValueMg); + assert(pos.count(strongerSide) == 1); + assert(pos.count< PAWN>(strongerSide) >= 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); - File pawnFile = file_of(pos.piece_list(strongerSide, PAWN)[0]); + Bitboard pawns = pos.pieces(strongerSide, PAWN); + File pawnFile = file_of(pos.list(strongerSide)[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 bishopSq = pos.list(strongerSide)[0]; + Square queeningSq = relative_square(strongerSide, pawnFile | RANK_8); Square kingSq = pos.king_square(weakerSide); if ( opposite_colors(queeningSq, bishopSq) @@ -423,25 +428,37 @@ ScaleFactor Endgame::operator()(const Position& pos) const { // 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. - Rank rank; - if (strongerSide == WHITE) - { - for (rank = RANK_7; !(rank_bb(rank) & pawns); rank--) {} - assert(rank >= RANK_2 && rank <= RANK_7); - } - else - { - for (rank = RANK_2; !(rank_bb(rank) & pawns); rank++) {} - rank = Rank(rank ^ 7); // HACK to get the relative rank - assert(rank >= RANK_2 && rank <= RANK_7); - } + 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(strongerSide, kingSq) >= rank) + || relative_rank(weakerSide, kingSq) <= relative_rank(weakerSide, pawnSq)) 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) + { + // 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; + } + return SCALE_FACTOR_NONE; } @@ -451,23 +468,23 @@ ScaleFactor Endgame::operator()(const Position& pos) const { template<> ScaleFactor Endgame::operator()(const Position& pos) const { - assert(pos.non_pawn_material(strongerSide) == QueenValueMidgame); - assert(pos.piece_count(strongerSide, QUEEN) == 1); - assert(pos.piece_count(strongerSide, PAWN) == 0); - assert(pos.piece_count(weakerSide, ROOK) == 1); - assert(pos.piece_count(weakerSide, PAWN) >= 1); + 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); - 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))) - { - Square rsq = pos.piece_list(weakerSide, ROOK)[0]; - if (pos.attacks_from(rsq, strongerSide) & pos.pieces(PAWN, 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))) return SCALE_FACTOR_DRAW; - } + return SCALE_FACTOR_NONE; } @@ -481,16 +498,16 @@ ScaleFactor Endgame::operator()(const Position& pos) const { template<> ScaleFactor Endgame::operator()(const Position& pos) const { - assert(pos.non_pawn_material(strongerSide) == RookValueMidgame); - assert(pos.piece_count(strongerSide, PAWN) == 1); - assert(pos.non_pawn_material(weakerSide) == RookValueMidgame); - assert(pos.piece_count(weakerSide, PAWN) == 0); + assert(pos.non_pawn_material(strongerSide) == RookValueMg); + assert(pos.non_pawn_material(weakerSide) == RookValueMg); + assert(pos.count(strongerSide) == 1); + assert(pos.count(weakerSide ) == 0); Square wksq = pos.king_square(strongerSide); - Square wrsq = pos.piece_list(strongerSide, ROOK)[0]; - Square wpsq = pos.piece_list(strongerSide, PAWN)[0]; Square bksq = pos.king_square(weakerSide); - Square brsq = pos.piece_list(weakerSide, ROOK)[0]; + Square wrsq = pos.list(strongerSide)[0]; + Square wpsq = pos.list(strongerSide)[0]; + Square brsq = pos.list(weakerSide)[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. @@ -502,6 +519,7 @@ ScaleFactor Endgame::operator()(const Position& pos) const { bksq = ~bksq; brsq = ~brsq; } + if (file_of(wpsq) > FILE_D) { wksq = mirror(wksq); @@ -513,7 +531,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,13 +617,13 @@ ScaleFactor Endgame::operator()(const Position& pos) const { template<> ScaleFactor Endgame::operator()(const Position& pos) const { - assert(pos.non_pawn_material(strongerSide) == RookValueMidgame); - assert(pos.piece_count(strongerSide, PAWN) == 2); - assert(pos.non_pawn_material(weakerSide) == RookValueMidgame); - assert(pos.piece_count(weakerSide, PAWN) == 1); + assert(pos.non_pawn_material(strongerSide) == RookValueMg); + assert(pos.non_pawn_material(weakerSide) == RookValueMg); + assert(pos.count(strongerSide) == 2); + assert(pos.count(weakerSide ) == 1); - Square wpsq1 = pos.piece_list(strongerSide, PAWN)[0]; - Square wpsq2 = pos.piece_list(strongerSide, PAWN)[1]; + Square wpsq1 = pos.list(strongerSide)[0]; + Square wpsq2 = pos.list(strongerSide)[1]; Square bksq = pos.king_square(weakerSide); // Does the stronger side have a passed pawn? @@ -638,20 +656,20 @@ template<> ScaleFactor Endgame::operator()(const Position& pos) const { assert(pos.non_pawn_material(strongerSide) == VALUE_ZERO); - assert(pos.piece_count(strongerSide, PAWN) >= 2); - assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO); - assert(pos.piece_count(weakerSide, PAWN) == 0); + assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO); + assert(pos.count(strongerSide) >= 2); + assert(pos.count(weakerSide ) == 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, rank_of(ksq)) & pawns))) return SCALE_FACTOR_DRAW; } // Are all pawns on the 'h' file? @@ -659,8 +677,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, rank_of(ksq)) & pawns))) return SCALE_FACTOR_DRAW; } return SCALE_FACTOR_NONE; @@ -674,16 +692,16 @@ ScaleFactor Endgame::operator()(const Position& pos) const { template<> ScaleFactor Endgame::operator()(const Position& pos) const { - assert(pos.non_pawn_material(strongerSide) == BishopValueMidgame); - assert(pos.piece_count(strongerSide, BISHOP) == 1); - assert(pos.piece_count(strongerSide, PAWN) == 1); - assert(pos.non_pawn_material(weakerSide) == BishopValueMidgame); - assert(pos.piece_count(weakerSide, BISHOP) == 1); - assert(pos.piece_count(weakerSide, PAWN) == 0); + 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); - Square pawnSq = pos.piece_list(strongerSide, PAWN)[0]; - Square strongerBishopSq = pos.piece_list(strongerSide, BISHOP)[0]; - Square weakerBishopSq = pos.piece_list(weakerSide, BISHOP)[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); // Case 1: Defending king blocks the pawn, and cannot be driven away @@ -710,9 +728,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,22 +747,22 @@ ScaleFactor Endgame::operator()(const Position& pos) const { template<> ScaleFactor Endgame::operator()(const Position& pos) const { - assert(pos.non_pawn_material(strongerSide) == BishopValueMidgame); - assert(pos.piece_count(strongerSide, BISHOP) == 1); - assert(pos.piece_count(strongerSide, PAWN) == 2); - assert(pos.non_pawn_material(weakerSide) == BishopValueMidgame); - assert(pos.piece_count(weakerSide, BISHOP) == 1); - assert(pos.piece_count(weakerSide, PAWN) == 0); + 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); - Square wbsq = pos.piece_list(strongerSide, BISHOP)[0]; - Square bbsq = pos.piece_list(weakerSide, BISHOP)[0]; + Square wbsq = pos.list(strongerSide)[0]; + Square bbsq = pos.list(weakerSide)[0]; if (!opposite_colors(wbsq, bbsq)) return SCALE_FACTOR_NONE; Square ksq = pos.king_square(weakerSide); - Square psq1 = pos.piece_list(strongerSide, PAWN)[0]; - Square psq2 = pos.piece_list(strongerSide, PAWN)[1]; + Square psq1 = pos.list(strongerSide)[0]; + Square psq2 = pos.list(strongerSide)[1]; Rank r1 = rank_of(psq1); Rank r2 = rank_of(psq2); Square blockSq1, blockSq2; @@ -752,12 +770,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 +797,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,15 +822,15 @@ ScaleFactor Endgame::operator()(const Position& pos) const { template<> ScaleFactor Endgame::operator()(const Position& pos) const { - assert(pos.non_pawn_material(strongerSide) == BishopValueMidgame); - assert(pos.piece_count(strongerSide, BISHOP) == 1); - assert(pos.piece_count(strongerSide, PAWN) == 1); - assert(pos.non_pawn_material(weakerSide) == KnightValueMidgame); - assert(pos.piece_count(weakerSide, KNIGHT) == 1); - assert(pos.piece_count(weakerSide, PAWN) == 0); + 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.piece_list(strongerSide, PAWN)[0]; - Square strongerBishopSq = pos.piece_list(strongerSide, BISHOP)[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) @@ -831,13 +849,13 @@ ScaleFactor Endgame::operator()(const Position& pos) const { template<> ScaleFactor Endgame::operator()(const Position& pos) const { - assert(pos.non_pawn_material(strongerSide) == KnightValueMidgame); - assert(pos.piece_count(strongerSide, KNIGHT) == 1); - assert(pos.piece_count(strongerSide, PAWN) == 1); - assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO); - assert(pos.piece_count(weakerSide, PAWN) == 0); + 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); - Square pawnSq = pos.piece_list(strongerSide, PAWN)[0]; + Square pawnSq = pos.list(strongerSide)[0]; Square weakerKingSq = pos.king_square(weakerSide); if ( pawnSq == relative_square(strongerSide, SQ_A7) @@ -852,6 +870,24 @@ ScaleFactor Endgame::operator()(const Position& pos) const { } +/// K, knight and a pawn vs K and bishop. If knight can block bishop from taking +/// pawn, it's a win. Otherwise, drawn. +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); + + // 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)); + + return SCALE_FACTOR_NONE; +} + + /// K and a pawn vs K and a pawn. This is done by removing the weakest side's /// pawn and probing the KP vs K bitbase: If the weakest side has a draw without /// the pawn, she probably has at least a draw with the pawn as well. The exception @@ -861,37 +897,36 @@ 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.piece_count(WHITE, PAWN) == 1); - assert(pos.piece_count(BLACK, PAWN) == 1); + assert(pos.non_pawn_material(weakerSide ) == VALUE_ZERO); + assert(pos.count(WHITE) == 1); + assert(pos.count(BLACK) == 1); Square wksq = pos.king_square(strongerSide); Square bksq = pos.king_square(weakerSide); - Square wpsq = pos.piece_list(strongerSide, PAWN)[0]; - Color stm = pos.side_to_move(); + Square psq = pos.list(strongerSide)[0]; + Color us = pos.side_to_move(); if (strongerSide == BLACK) { wksq = ~wksq; bksq = ~bksq; - wpsq = ~wpsq; - stm = ~stm; + psq = ~psq; + us = ~us; } - if (file_of(wpsq) >= FILE_E) + if (file_of(psq) >= FILE_E) { wksq = mirror(wksq); bksq = mirror(bksq); - wpsq = mirror(wpsq); + psq = mirror(psq); } // 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. - if ( rank_of(wpsq) >= RANK_5 - && file_of(wpsq) != FILE_A) + if (rank_of(psq) >= RANK_5 && file_of(psq) != FILE_A) return SCALE_FACTOR_NONE; // 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, psq, bksq, us) ? SCALE_FACTOR_NONE : SCALE_FACTOR_DRAW; }