]> git.sesse.net Git - stockfish/blobdiff - src/endgame.cpp
Add helper function verify_material
[stockfish] / src / endgame.cpp
index b26e0a9101637a58e7299ba4c2246011bb46bee5..f65a267d67a4d4f9ae3ac06509280002989cd52c 100644 (file)
@@ -59,6 +59,12 @@ namespace {
   const int PushClose[8] = { 0, 0, 100, 80, 60, 40, 20, 10 };
   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<PAWN>(c) == num_pawns;
+  }
+#endif
+
   // 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
@@ -102,6 +108,7 @@ Endgames::Endgames() {
   add<KNPK>("KNPK");
   add<KNPKB>("KNPKB");
   add<KRPKR>("KRPKR");
+  add<KRPKB>("KRPKB");
   add<KBPKB>("KBPKB");
   add<KBPKN>("KBPKN");
   add<KBPPKB>("KBPPKB");
@@ -129,8 +136,7 @@ void Endgames::add(const string& code) {
 template<>
 Value Endgame<KXK>::operator()(const Position& pos) const {
 
-  assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO);
-  assert(!pos.count<PAWN>(weakerSide));
+  assert(verify_material(pos, weakerSide, VALUE_ZERO, 0));
   assert(!pos.checkers()); // Eval is never called when in check
 
   // Stalemate detection with lone king
@@ -141,9 +147,9 @@ Value Endgame<KXK>::operator()(const Position& pos) const {
   Square loserKSq = pos.king_square(weakerSide);
 
   Value result =   pos.non_pawn_material(strongerSide)
-                 + pos.count<PAWN>(strongerSide) * PawnValueEg
-                 + PushToEdges[loserKSq]
-                 + PushClose[square_distance(winnerKSq, loserKSq)];
+                + pos.count<PAWN>(strongerSide) * PawnValueEg
+                + PushToEdges[loserKSq]
+                + PushClose[square_distance(winnerKSq, loserKSq)];
 
   if (   pos.count<QUEEN>(strongerSide)
       || pos.count<ROOK>(strongerSide)
@@ -159,12 +165,8 @@ Value Endgame<KXK>::operator()(const Position& pos) const {
 template<>
 Value Endgame<KBNK>::operator()(const Position& pos) const {
 
-  assert(pos.non_pawn_material(strongerSide) == KnightValueMg + BishopValueMg);
-  assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO);
-  assert(pos.count<BISHOP>(strongerSide) == 1);
-  assert(pos.count<KNIGHT>(strongerSide) == 1);
-  assert(pos.count<  PAWN>(strongerSide) == 0);
-  assert(pos.count<  PAWN>(weakerSide  ) == 0);
+  assert(verify_material(pos, strongerSide, KnightValueMg + BishopValueMg, 0));
+  assert(verify_material(pos, weakerSide, VALUE_ZERO, 0));
 
   Square winnerKSq = pos.king_square(strongerSide);
   Square loserKSq = pos.king_square(weakerSide);
@@ -191,10 +193,8 @@ Value Endgame<KBNK>::operator()(const Position& pos) const {
 template<>
 Value Endgame<KPK>::operator()(const Position& pos) const {
 
-  assert(pos.non_pawn_material(strongerSide) == VALUE_ZERO);
-  assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO);
-  assert(pos.count<PAWN>(strongerSide) == 1);
-  assert(pos.count<PAWN>(weakerSide  ) == 0);
+  assert(verify_material(pos, strongerSide, VALUE_ZERO, 1));
+  assert(verify_material(pos, weakerSide, VALUE_ZERO, 0));
 
   Square wksq = pos.king_square(strongerSide);
   Square bksq = pos.king_square(weakerSide);
@@ -232,10 +232,8 @@ Value Endgame<KPK>::operator()(const Position& pos) const {
 template<>
 Value Endgame<KRKP>::operator()(const Position& pos) const {
 
-  assert(pos.non_pawn_material(strongerSide) == RookValueMg);
-  assert(pos.non_pawn_material(weakerSide) == 0);
-  assert(pos.count<PAWN>(strongerSide) == 0);
-  assert(pos.count<PAWN>(weakerSide  ) == 1);
+  assert(verify_material(pos, strongerSide, RookValueMg, 0));
+  assert(verify_material(pos, weakerSide, VALUE_ZERO, 1));
 
   Square wksq = pos.king_square(strongerSide);
   Square bksq = pos.king_square(weakerSide);
@@ -286,11 +284,8 @@ Value Endgame<KRKP>::operator()(const Position& pos) const {
 template<>
 Value Endgame<KRKB>::operator()(const Position& pos) const {
 
-  assert(pos.non_pawn_material(strongerSide) == RookValueMg);
-  assert(pos.non_pawn_material(weakerSide  ) == BishopValueMg);
-  assert(pos.count<BISHOP>(weakerSide  ) == 1);
-  assert(pos.count<  PAWN>(weakerSide  ) == 0);
-  assert(pos.count<  PAWN>(strongerSide) == 0);
+  assert(verify_material(pos, strongerSide, RookValueMg, 0));
+  assert(verify_material(pos, weakerSide, BishopValueMg, 0));
 
   Value result = Value(PushToEdges[pos.king_square(weakerSide)]);
   return strongerSide == pos.side_to_move() ? result : -result;
@@ -302,11 +297,8 @@ Value Endgame<KRKB>::operator()(const Position& pos) const {
 template<>
 Value Endgame<KRKN>::operator()(const Position& pos) const {
 
-  assert(pos.non_pawn_material(strongerSide) == RookValueMg);
-  assert(pos.non_pawn_material(weakerSide  ) == KnightValueMg);
-  assert(pos.count<KNIGHT>(weakerSide  ) == 1);
-  assert(pos.count<  PAWN>(weakerSide  ) == 0);
-  assert(pos.count<  PAWN>(strongerSide) == 0);
+  assert(verify_material(pos, strongerSide, RookValueMg, 0));
+  assert(verify_material(pos, weakerSide, KnightValueMg, 0));
 
   Square bksq = pos.king_square(weakerSide);
   Square bnsq = pos.list<KNIGHT>(weakerSide)[0];
@@ -321,10 +313,8 @@ Value Endgame<KRKN>::operator()(const Position& pos) const {
 template<>
 Value Endgame<KQKP>::operator()(const Position& pos) const {
 
-  assert(pos.non_pawn_material(strongerSide) == QueenValueMg);
-  assert(pos.non_pawn_material(weakerSide  ) == VALUE_ZERO);
-  assert(pos.count<PAWN>(strongerSide) == 0);
-  assert(pos.count<PAWN>(weakerSide  ) == 1);
+  assert(verify_material(pos, strongerSide, QueenValueMg, 0));
+  assert(verify_material(pos, weakerSide, VALUE_ZERO, 1));
 
   Square winnerKSq = pos.king_square(strongerSide);
   Square loserKSq = pos.king_square(weakerSide);
@@ -349,10 +339,8 @@ Value Endgame<KQKP>::operator()(const Position& pos) const {
 template<>
 Value Endgame<KQKR>::operator()(const Position& pos) const {
 
-  assert(pos.non_pawn_material(strongerSide) == QueenValueMg);
-  assert(pos.non_pawn_material(weakerSide  ) == RookValueMg);
-  assert(pos.count<PAWN>(strongerSide) == 0);
-  assert(pos.count<PAWN>(weakerSide  ) == 0);
+  assert(verify_material(pos, strongerSide, QueenValueMg, 0));
+  assert(verify_material(pos, weakerSide, RookValueMg, 0));
 
   Square winnerKSq = pos.king_square(strongerSide);
   Square loserKSq = pos.king_square(weakerSide);
@@ -373,11 +361,8 @@ Value Endgame<KQKR>::operator()(const Position& pos) const {
 template<>
 Value Endgame<KBBKN>::operator()(const Position& pos) const {
 
-  assert(pos.non_pawn_material(strongerSide) == 2 * BishopValueMg);
-  assert(pos.non_pawn_material(weakerSide  ) == KnightValueMg);
-  assert(pos.count<BISHOP>(strongerSide) == 2);
-  assert(pos.count<KNIGHT>(weakerSide  ) == 1);
-  assert(!pos.pieces(PAWN));
+  assert(verify_material(pos, strongerSide, 2 * BishopValueMg, 0));
+  assert(verify_material(pos, weakerSide, KnightValueMg, 0));
 
   Square winnerKSq = pos.king_square(strongerSide);
   Square loserKSq = pos.king_square(weakerSide);
@@ -405,8 +390,7 @@ template<>
 ScaleFactor Endgame<KBPsK>::operator()(const Position& pos) const {
 
   assert(pos.non_pawn_material(strongerSide) == BishopValueMg);
-  assert(pos.count<BISHOP>(strongerSide) == 1);
-  assert(pos.count<  PAWN>(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.
@@ -451,12 +435,26 @@ ScaleFactor Endgame<KBPsK>::operator()(const Position& pos) const {
       Square weakerKingSq = pos.king_square(weakerSide);
       Square bishopSq = pos.list<BISHOP>(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.
+      // 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(strongerSide, weakerPawnSq) == RANK_7
-          && opposite_colors(bishopSq, weakerPawnSq)
-          && square_distance(weakerPawnSq, weakerKingSq) <= square_distance(weakerPawnSq, strongerKingSq))
-          return SCALE_FACTOR_DRAW;
+          && (pos.pieces(strongerSide, PAWN) & (weakerPawnSq + pawn_push(weakerSide)))
+          && (opposite_colors(bishopSq, weakerPawnSq) || pos.count<PAWN>(strongerSide) == 1))
+      {
+          int strongerKingDist = square_distance(weakerPawnSq, strongerKingSq);
+          int weakerKingDist = square_distance(weakerPawnSq, weakerKingSq);
+
+          // Draw if the weak king is on it's 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
+          // and positions where qsearch will immediately correct the
+          // problem such as 8/4k1p1/6P1/1K6/3B4/8/8/8 w)
+          if (   relative_rank(strongerSide, weakerKingSq) >= RANK_7
+              && weakerKingDist <= 2
+              && weakerKingDist <= strongerKingDist)
+              return SCALE_FACTOR_DRAW;
+      }
   }
 
   return SCALE_FACTOR_NONE;
@@ -468,11 +466,9 @@ ScaleFactor Endgame<KBPsK>::operator()(const Position& pos) const {
 template<>
 ScaleFactor Endgame<KQKRPs>::operator()(const Position& pos) const {
 
-  assert(pos.non_pawn_material(strongerSide) == QueenValueMg);
-  assert(pos.count<QUEEN>(strongerSide) == 1);
-  assert(pos.count< PAWN>(strongerSide) == 0);
-  assert(pos.count< ROOK>(weakerSide  ) == 1);
-  assert(pos.count< PAWN>(weakerSide  ) >= 1);
+  assert(verify_material(pos, strongerSide, QueenValueMg, 0));
+  assert(pos.count<ROOK>(weakerSide) == 1);
+  assert(pos.count<PAWN>(weakerSide) >= 1);
 
   Square kingSq = pos.king_square(weakerSide);
   Square rsq = pos.list<ROOK>(weakerSide)[0];
@@ -498,10 +494,8 @@ ScaleFactor Endgame<KQKRPs>::operator()(const Position& pos) const {
 template<>
 ScaleFactor Endgame<KRPKR>::operator()(const Position& pos) const {
 
-  assert(pos.non_pawn_material(strongerSide) == RookValueMg);
-  assert(pos.non_pawn_material(weakerSide)   == RookValueMg);
-  assert(pos.count<PAWN>(strongerSide) == 1);
-  assert(pos.count<PAWN>(weakerSide  ) == 0);
+  assert(verify_material(pos, strongerSide, RookValueMg, 1));
+  assert(verify_material(pos, weakerSide,   RookValueMg, 0));
 
   Square wksq = pos.king_square(strongerSide);
   Square bksq = pos.king_square(weakerSide);
@@ -610,6 +604,49 @@ ScaleFactor Endgame<KRPKR>::operator()(const Position& pos) const {
   return SCALE_FACTOR_NONE;
 }
 
+template<>
+ScaleFactor Endgame<KRPKB>::operator()(const Position& pos) const {
+
+  assert(verify_material(pos, strongerSide, RookValueMg, 1));
+  assert(verify_material(pos, weakerSide, BishopValueMg, 0));
+
+  // Test for a rook pawn
+  if (pos.pieces(PAWN) & (FileABB | FileHBB))
+  {
+      Square ksq = pos.king_square(weakerSide);
+      Square bsq = pos.list<BISHOP>(weakerSide)[0];
+      Square psq = pos.list<PAWN>(strongerSide)[0];
+      Rank rk = relative_rank(strongerSide, psq);
+      Square push = pawn_push(strongerSide);
+
+      // If the pawn is on the 5th rank and the pawn (currently) is on
+      // the same color square as the bishop then there is a chance of
+      // a fortress. Depending on the king position give a moderate
+      // reduction or a stronger one if the defending king is near the
+      // corner but not trapped there.
+      if (rk == RANK_5 && !opposite_colors(bsq, psq))
+      {
+          int d = square_distance(psq + 3 * push, ksq);
+
+          if (d <= 2 && !(d == 0 && ksq == pos.king_square(strongerSide) + 2 * push))
+              return ScaleFactor(24);
+          else
+              return ScaleFactor(48);
+      }
+
+      // When the pawn has moved to the 6th rank we can be fairly sure
+      // it's drawn if the bishop attacks the square in front of the
+      // pawn from a reasonable distance and the defending king is near
+      // the corner
+      if (   rk == RANK_6
+          && square_distance(psq + 2 * push, ksq) <= 1
+          && (PseudoAttacks[BISHOP][bsq] & (psq + push))
+          && file_distance(bsq, psq) >= 2)
+          return ScaleFactor(8);
+  }
+
+  return SCALE_FACTOR_NONE;
+}
 
 /// K, rook and two pawns vs K, rook and one pawn. There is only a single
 /// pattern: If the stronger side has no passed pawns and the defending king
@@ -617,10 +654,8 @@ ScaleFactor Endgame<KRPKR>::operator()(const Position& pos) const {
 template<>
 ScaleFactor Endgame<KRPPKRP>::operator()(const Position& pos) const {
 
-  assert(pos.non_pawn_material(strongerSide) == RookValueMg);
-  assert(pos.non_pawn_material(weakerSide)   == RookValueMg);
-  assert(pos.count<PAWN>(strongerSide) == 2);
-  assert(pos.count<PAWN>(weakerSide  ) == 1);
+  assert(verify_material(pos, strongerSide, RookValueMg, 2));
+  assert(verify_material(pos, weakerSide,   RookValueMg, 1));
 
   Square wpsq1 = pos.list<PAWN>(strongerSide)[0];
   Square wpsq2 = pos.list<PAWN>(strongerSide)[1];
@@ -655,9 +690,8 @@ template<>
 ScaleFactor Endgame<KPsK>::operator()(const Position& pos) const {
 
   assert(pos.non_pawn_material(strongerSide) == VALUE_ZERO);
-  assert(pos.non_pawn_material(weakerSide)   == VALUE_ZERO);
   assert(pos.count<PAWN>(strongerSide) >= 2);
-  assert(pos.count<PAWN>(weakerSide  ) == 0);
+  assert(verify_material(pos, weakerSide, VALUE_ZERO, 0));
 
   Square ksq = pos.king_square(weakerSide);
   Bitboard pawns = pos.pieces(strongerSide, PAWN);
@@ -691,12 +725,8 @@ ScaleFactor Endgame<KPsK>::operator()(const Position& pos) const {
 template<>
 ScaleFactor Endgame<KBPKB>::operator()(const Position& pos) const {
 
-  assert(pos.non_pawn_material(strongerSide) == BishopValueMg);
-  assert(pos.non_pawn_material(weakerSide  ) == BishopValueMg);
-  assert(pos.count<BISHOP>(strongerSide) == 1);
-  assert(pos.count<BISHOP>(weakerSide  ) == 1);
-  assert(pos.count<  PAWN>(strongerSide) == 1);
-  assert(pos.count<  PAWN>(weakerSide  ) == 0);
+  assert(verify_material(pos, strongerSide, BishopValueMg, 1));
+  assert(verify_material(pos, weakerSide,   BishopValueMg, 0));
 
   Square pawnSq = pos.list<PAWN>(strongerSide)[0];
   Square strongerBishopSq = pos.list<BISHOP>(strongerSide)[0];
@@ -746,12 +776,8 @@ ScaleFactor Endgame<KBPKB>::operator()(const Position& pos) const {
 template<>
 ScaleFactor Endgame<KBPPKB>::operator()(const Position& pos) const {
 
-  assert(pos.non_pawn_material(strongerSide) == BishopValueMg);
-  assert(pos.non_pawn_material(weakerSide  ) == BishopValueMg);
-  assert(pos.count<BISHOP>(strongerSide) == 1);
-  assert(pos.count<BISHOP>(weakerSide  ) == 1);
-  assert(pos.count<  PAWN>(strongerSide) == 2);
-  assert(pos.count<  PAWN>(weakerSide  ) == 0);
+  assert(verify_material(pos, strongerSide, BishopValueMg, 2));
+  assert(verify_material(pos, weakerSide,   BishopValueMg, 0));
 
   Square wbsq = pos.list<BISHOP>(strongerSide)[0];
   Square bbsq = pos.list<BISHOP>(weakerSide)[0];
@@ -821,12 +847,8 @@ ScaleFactor Endgame<KBPPKB>::operator()(const Position& pos) const {
 template<>
 ScaleFactor Endgame<KBPKN>::operator()(const Position& pos) const {
 
-  assert(pos.non_pawn_material(strongerSide) == BishopValueMg);
-  assert(pos.non_pawn_material(weakerSide  ) == KnightValueMg);
-  assert(pos.count<BISHOP>(strongerSide) == 1);
-  assert(pos.count<KNIGHT>(weakerSide  ) == 1);
-  assert(pos.count<  PAWN>(strongerSide) == 1);
-  assert(pos.count<  PAWN>(weakerSide  ) == 0);
+  assert(verify_material(pos, strongerSide, BishopValueMg, 1));
+  assert(verify_material(pos, weakerSide, KnightValueMg, 0));
 
   Square pawnSq = pos.list<PAWN>(strongerSide)[0];
   Square strongerBishopSq = pos.list<BISHOP>(strongerSide)[0];
@@ -848,11 +870,8 @@ ScaleFactor Endgame<KBPKN>::operator()(const Position& pos) const {
 template<>
 ScaleFactor Endgame<KNPK>::operator()(const Position& pos) const {
 
-  assert(pos.non_pawn_material(strongerSide) == KnightValueMg);
-  assert(pos.non_pawn_material(weakerSide  ) == VALUE_ZERO);
-  assert(pos.count<KNIGHT>(strongerSide) == 1);
-  assert(pos.count<  PAWN>(strongerSide) == 1);
-  assert(pos.count<  PAWN>(weakerSide  ) == 0);
+  assert(verify_material(pos, strongerSide, KnightValueMg, 1));
+  assert(verify_material(pos, weakerSide, VALUE_ZERO, 0));
 
   Square pawnSq = pos.list<PAWN>(strongerSide)[0];
   Square weakerKingSq = pos.king_square(weakerSide);
@@ -895,10 +914,8 @@ ScaleFactor Endgame<KNPKB>::operator()(const Position& pos) const {
 template<>
 ScaleFactor Endgame<KPKP>::operator()(const Position& pos) const {
 
-  assert(pos.non_pawn_material(strongerSide) == VALUE_ZERO);
-  assert(pos.non_pawn_material(weakerSide  ) == VALUE_ZERO);
-  assert(pos.count<PAWN>(WHITE) == 1);
-  assert(pos.count<PAWN>(BLACK) == 1);
+  assert(verify_material(pos, strongerSide, VALUE_ZERO, 1));
+  assert(verify_material(pos, weakerSide,   VALUE_ZERO, 1));
 
   Square wksq = pos.king_square(strongerSide);
   Square bksq = pos.king_square(weakerSide);