4160, 4480, 4800, 5120, 5440, 5760, 6080, 6400
};
+ // Table used to drive the king towards the edge of the board
+ // in KSF vs K.
+ constexpr int PushToOpposingSideEdges[SQUARE_NB] = {
+ 20, 10, 5, 0, 0, 5, 10, 20,
+ 20, 10, 5, 0, 0, 5, 10, 20,
+ 30, 20, 10, 0, 0, 10, 20, 30,
+ 50, 40, 20, 10, 10, 20, 40, 50,
+ 60, 50, 40, 30, 30, 40, 50, 60,
+ 70, 60, 50, 40, 40, 50, 60, 70,
+ 90, 70, 60, 50, 50, 60, 70, 90,
+ 100, 90, 80, 70, 70, 80, 90, 100
+ };
+
- // Tables used to drive a piece towards or away from another piece
- constexpr int PushClose[FILE_NB] = { 0, 0, 100, 80, 60, 40, 20, 10 };
- constexpr int PushAway [FILE_NB] = { 0, 5, 20, 40, 60, 80, 90, 100 };
+ // Drive a piece close to or away from another piece
+ inline int push_close(Square s1, Square s2) { return 140 - 20 * distance(s1, s2); }
+ inline int push_away(Square s1, Square s2) { return 120 - push_close(s1, s2); }
// Pawn Rank based scaling factors used in KRPPKRP endgame
constexpr int KRPPKRPScaleFactors[RANK_NB] = { 0, 9, 10, 14, 21, 44, 0, 0 };
Value result = pos.non_pawn_material(strongSide)
+ pos.count<PAWN>(strongSide) * PawnValueEg
- + PushToEdges[loserKSq]
+ + PushToEdges[map_to_standard_board(pos, loserKSq)]
- + PushClose[distance(winnerKSq, loserKSq)];
+ + push_close(winnerKSq, loserKSq);
if ( pos.count<QUEEN>(strongSide)
|| pos.count<ROOK>(strongSide)
// to drive to opposite corners (A8/H1).
Value result = VALUE_KNOWN_WIN
- + PushClose[distance(winnerKSq, loserKSq)]
+ + push_close(winnerKSq, loserKSq)
- + PushToCorners[opposite_colors(bishopSq, SQ_A1) ? ~loserKSq : loserKSq];
+ + PushToCorners[map_to_standard_board(pos, relative_square(opposite_colors(bishopSq, SQ_A1) ? BLACK : WHITE, loserKSq, pos.max_rank()))];
assert(abs(result) < VALUE_TB_WIN_IN_MAX_PLY);
return strongSide == pos.side_to_move() ? result : -result;
Square bksq = pos.square<KING>(weakSide);
Square bnsq = pos.square<KNIGHT>(weakSide);
- Value result = Value(PushToEdges[map_to_standard_board(pos, bksq)] + PushAway[distance(bksq, bnsq)]);
- Value result = Value(PushToEdges[bksq] + push_away(bksq, bnsq));
++ Value result = Value(PushToEdges[map_to_standard_board(pos, bksq)] + push_away(bksq, bnsq));
return strongSide == pos.side_to_move() ? result : -result;
}
Value result = QueenValueEg
- RookValueEg
- + PushToEdges[loserKSq]
+ + PushToEdges[map_to_standard_board(pos, loserKSq)]
- + PushClose[distance(winnerKSq, loserKSq)];
+ + push_close(winnerKSq, loserKSq);
return strongSide == pos.side_to_move() ? result : -result;
}
template<> Value Endgame<KNNK>::operator()(const Position&) const { return VALUE_DRAW; }
+/// KFsPs vs K.
+template<>
+Value Endgame<KFsPsK>::operator()(const Position& pos) const {
+
+ assert(verify_material(pos, weakSide, VALUE_ZERO, 0));
+
+ Square winnerKSq = pos.square<KING>(strongSide);
+ Square loserKSq = pos.square<KING>(weakSide);
+
+ Value result = pos.non_pawn_material(strongSide)
+ + pos.count<PAWN>(strongSide) * PawnValueEg
+ + PushToEdges[map_to_standard_board(pos, loserKSq)]
- + PushClose[distance(winnerKSq, loserKSq)];
++ + push_close(winnerKSq, loserKSq);
+
+ if ( pos.count<FERS>(strongSide) >= 3
+ && ( DarkSquares & pos.pieces(strongSide, FERS))
+ && (~DarkSquares & pos.pieces(strongSide, FERS)))
+ result = std::min(result + VALUE_KNOWN_WIN, VALUE_TB_WIN_IN_MAX_PLY - 1);
+ else if (pos.count<FERS>(strongSide) + pos.count<PAWN>(strongSide) < 3)
+ return VALUE_DRAW;
+ else
+ {
+ bool dark = DarkSquares & pos.pieces(strongSide, FERS);
+ bool light = ~DarkSquares & pos.pieces(strongSide, FERS);
+
+ // Determine the color of ferzes from promoting pawns
+ Bitboard b = pos.pieces(strongSide, PAWN);
+ while (b && (!dark || !light))
+ {
+ if (file_of(pop_lsb(&b)) % 2 != relative_rank(strongSide, pos.promotion_rank(), pos.max_rank()) % 2)
+ light = true;
+ else
+ dark = true;
+ }
+ if (!dark || !light)
+ return VALUE_DRAW; // we can not checkmate with same colored ferzes
+ }
+
+ return strongSide == pos.side_to_move() ? result : -result;
+}
+
+
+/// Mate with KNS vs K.
+template<>
+Value Endgame<KNSK>::operator()(const Position& pos) const {
+
+ assert(verify_material(pos, strongSide, KnightValueMg + SilverValueMg, 0));
+ assert(verify_material(pos, weakSide, VALUE_ZERO, 0));
+
+ Square winnerKSq = pos.square<KING>(strongSide);
+ Square loserKSq = pos.square<KING>(weakSide);
+
+ Value result = VALUE_KNOWN_WIN
- + PushClose[distance(winnerKSq, loserKSq)]
++ + push_close(winnerKSq, loserKSq)
+ + PushToOpposingSideEdges[map_to_standard_board(pos, relative_square(strongSide, loserKSq, pos.max_rank()))];
+
+ return strongSide == pos.side_to_move() ? result : -result;
+}
+
+
+/// KNF vs K. Can only be won if the weaker side's king
+/// is close to a corner of the same color as the fers.
+template<>
+Value Endgame<KNFK>::operator()(const Position& pos) const {
+
+ assert(verify_material(pos, strongSide, KnightValueMg + FersValueMg, 0));
+ assert(verify_material(pos, weakSide, VALUE_ZERO, 0));
+
+ Square winnerKSq = pos.square<KING>(strongSide);
+ Square loserKSq = pos.square<KING>(weakSide);
+ Square fersSq = pos.square<FERS>(strongSide);
+
+ // tries to drive toward corners A1 or H8. If we have a
+ // fers that cannot reach the above squares, we flip the kings in order
+ // to drive the enemy toward corners A8 or H1.
+ if (opposite_colors(fersSq, SQ_A1))
+ {
+ winnerKSq = relative_square(BLACK, winnerKSq, pos.max_rank());
+ loserKSq = relative_square(BLACK, loserKSq, pos.max_rank());
+ }
+
- Value result = Value(PushClose[distance(winnerKSq, loserKSq)])
++ Value result = Value(push_close(winnerKSq, loserKSq))
+ + (PushToCorners[map_to_standard_board(pos, loserKSq)] - 3000) / 10;
+
+ return strongSide == pos.side_to_move() ? result : -result;
+}
+
+
+/// KNSFKR vs K.
+template<>
+Value Endgame<KNSFKR>::operator()(const Position& pos) const {
+
+ assert(verify_material(pos, strongSide, KnightValueMg + SilverValueMg + FersValueMg, 0));
+ assert(verify_material(pos, weakSide, RookValueMg, 0));
+
+ Square winnerKSq = pos.square<KING>(strongSide);
+ Square loserKSq = pos.square<KING>(weakSide);
+
+ Value result = KnightValueEg + SilverValueEg + FersValueEg - RookValueEg
- + PushClose[distance(winnerKSq, loserKSq)]
++ + push_close(winnerKSq, loserKSq)
+ + PushToOpposingSideEdges[map_to_standard_board(pos, relative_square(strongSide, loserKSq, pos.max_rank()))];
+
+ return strongSide == pos.side_to_move() ? result : -result;
+}
+
+
+/// Mate with KSF vs K.
+template<>
+Value Endgame<KSFK>::operator()(const Position& pos) const {
+
+ assert(verify_material(pos, strongSide, SilverValueMg + FersValueMg, 0));
+ assert(verify_material(pos, weakSide, VALUE_ZERO, 0));
+
+ Square winnerKSq = pos.square<KING>(strongSide);
+ Square loserKSq = pos.square<KING>(weakSide);
+
+ Value result = VALUE_KNOWN_WIN
- + PushClose[distance(winnerKSq, loserKSq)]
++ + push_close(winnerKSq, loserKSq)
+ + PushToOpposingSideEdges[map_to_standard_board(pos, relative_square(strongSide, loserKSq, pos.max_rank()))];
+
+ return strongSide == pos.side_to_move() ? result : -result;
+}
+
+
+/// Mate with KSF vs KF.
+template<>
+Value Endgame<KSFKF>::operator()(const Position& pos) const {
+
+ assert(verify_material(pos, strongSide, SilverValueMg + FersValueMg, 0));
+ assert(verify_material(pos, weakSide, FersValueMg, 0));
+
+ Square winnerKSq = pos.square<KING>(strongSide);
+ Square loserKSq = pos.square<KING>(weakSide);
+ Square fersSq = pos.square<FERS>(weakSide);
+
+ Value result = SilverValueEg
- + PushClose[distance(winnerKSq, loserKSq)]
- + PushAway[distance(fersSq, loserKSq)]
++ + push_close(winnerKSq, loserKSq)
++ + push_away(fersSq, loserKSq)
+ + PushToOpposingSideEdges[map_to_standard_board(pos, relative_square(strongSide, loserKSq, pos.max_rank()))];
+
+ return strongSide == pos.side_to_move() ? result : -result;
+}
+
+
+/// KR vs KS
+template<>
+Value Endgame<KRKS>::operator()(const Position& pos) const {
+
+ assert(verify_material(pos, strongSide, RookValueMg, 0));
+ assert(verify_material(pos, weakSide, SilverValueMg, 0));
+
+ Square winnerKSq = pos.square<KING>(strongSide);
+ Square loserKSq = pos.square<KING>(weakSide);
+
+ Value result = RookValueEg
+ - SilverValueEg
+ + PushToEdges[map_to_standard_board(pos, loserKSq)]
- + PushClose[distance(winnerKSq, loserKSq)];
++ + push_close(winnerKSq, loserKSq);
+
+ return strongSide == pos.side_to_move() ? result : -result;
+}
+
+
/// KB 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
/// is returned. If not, the return value is SCALE_FACTOR_NONE, i.e. no scaling