1 | /* |
2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 |
3 | Copyright (C) 2004-2008 Tord Romstad (Glaurung author) |
4 | Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad |
5 | Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad |
6 | |
7 | Stockfish is free software: you can redistribute it and/or modify |
8 | it under the terms of the GNU General Public License as published by |
9 | the Free Software Foundation, either version 3 of the License, or |
10 | (at your option) any later version. |
11 | |
12 | Stockfish is distributed in the hope that it will be useful, |
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
15 | GNU General Public License for more details. |
16 | |
17 | You should have received a copy of the GNU General Public License |
18 | along with this program. If not, see <http://www.gnu.org/licenses/>. |
19 | */ |
20 | |
21 | #include <cassert> |
22 | |
23 | #include "bitboard.h" |
24 | #include "endgame.h" |
25 | #include "movegen.h" |
26 | |
27 | using std::string; |
28 | |
29 | namespace { |
30 | |
31 | // Table used to drive the king towards the edge of the board |
32 | // in KX vs K and KQ vs KR endgames. |
33 | constexpr int PushToEdges[SQUARE_NB] = { |
34 | 100, 90, 80, 70, 70, 80, 90, 100, |
35 | 90, 70, 60, 50, 50, 60, 70, 90, |
36 | 80, 60, 40, 30, 30, 40, 60, 80, |
37 | 70, 50, 30, 20, 20, 30, 50, 70, |
38 | 70, 50, 30, 20, 20, 30, 50, 70, |
39 | 80, 60, 40, 30, 30, 40, 60, 80, |
40 | 90, 70, 60, 50, 50, 60, 70, 90, |
41 | 100, 90, 80, 70, 70, 80, 90, 100 |
42 | }; |
43 | |
44 | // Table used to drive the king towards a corner square of the |
45 | // right color in KBN vs K endgames. |
46 | constexpr int PushToCorners[SQUARE_NB] = { |
47 | 6400, 6080, 5760, 5440, 5120, 4800, 4480, 4160, |
48 | 6080, 5760, 5440, 5120, 4800, 4480, 4160, 4480, |
49 | 5760, 5440, 4960, 4480, 4480, 4000, 4480, 4800, |
50 | 5440, 5120, 4480, 3840, 3520, 4480, 4800, 5120, |
51 | 5120, 4800, 4480, 3520, 3840, 4480, 5120, 5440, |
52 | 4800, 4480, 4000, 4480, 4480, 4960, 5440, 5760, |
53 | 4480, 4160, 4480, 4800, 5120, 5440, 5760, 6080, |
54 | 4160, 4480, 4800, 5120, 5440, 5760, 6080, 6400 |
55 | }; |
56 | |
57 | // Tables used to drive a piece towards or away from another piece |
58 | constexpr int PushClose[8] = { 0, 0, 100, 80, 60, 40, 20, 10 }; |
59 | constexpr int PushAway [8] = { 0, 5, 20, 40, 60, 80, 90, 100 }; |
60 | |
61 | // Pawn Rank based scaling factors used in KRPPKRP endgame |
62 | constexpr int KRPPKRPScaleFactors[RANK_NB] = { 0, 9, 10, 14, 21, 44, 0, 0 }; |
63 | |
64 | #ifndef NDEBUG |
65 | bool verify_material(const Position& pos, Color c, Value npm, int pawnsCnt) { |
66 | return pos.non_pawn_material(c) == npm && pos.count<PAWN>(c) == pawnsCnt; |
67 | } |
68 | #endif |
69 | |
70 | // Map the square as if strongSide is white and strongSide's only pawn |
71 | // is on the left half of the board. |
72 | Square normalize(const Position& pos, Color strongSide, Square sq) { |
73 | |
74 | assert(pos.count<PAWN>(strongSide) == 1); |
75 | |
76 | if (file_of(pos.square<PAWN>(strongSide)) >= FILE_E) |
77 | sq = Square(sq ^ 7); // Mirror SQ_H1 -> SQ_A1 |
78 | |
79 | return strongSide == WHITE ? sq : ~sq; |
80 | } |
81 | |
82 | } // namespace |
83 | |
84 | |
85 | namespace Endgames { |
86 | |
87 | std::pair<Map<Value>, Map<ScaleFactor>> maps; |
88 | |
89 | void init() { |
90 | |
91 | add<KPK>("KPK" ); |
92 | add<KNNK>("KNNK" ); |
93 | add<KBNK>("KBNK" ); |
94 | add<KRKP>("KRKP" ); |
95 | add<KRKB>("KRKB" ); |
96 | add<KRKN>("KRKN" ); |
97 | add<KQKP>("KQKP" ); |
98 | add<KQKR>("KQKR" ); |
99 | add<KNNKP>("KNNKP" ); |
100 | |
101 | add<KNPK>("KNPK" ); |
102 | add<KNPKB>("KNPKB" ); |
103 | add<KRPKR>("KRPKR" ); |
104 | add<KRPKB>("KRPKB" ); |
105 | add<KBPKB>("KBPKB" ); |
106 | add<KBPKN>("KBPKN" ); |
107 | add<KBPPKB>("KBPPKB" ); |
108 | add<KRPPKRP>("KRPPKRP" ); |
109 | } |
110 | } |
111 | |
112 | |
113 | /// Mate with KX vs K. This function is used to evaluate positions with |
114 | /// king and plenty of material vs a lone king. It simply gives the |
115 | /// attacking side a bonus for driving the defending king towards the edge |
116 | /// of the board, and for keeping the distance between the two kings small. |
117 | template<> |
118 | Value Endgame<KXK>::operator()(const Position& pos) const { |
119 | |
120 | assert(verify_material(pos, weakSide, VALUE_ZERO, 0)); |
121 | assert(!pos.checkers()); // Eval is never called when in check |
122 | |
123 | // Stalemate detection with lone king |
124 | if (pos.side_to_move() == weakSide && !MoveList<LEGAL>(pos).size()) |
125 | return VALUE_DRAW; |
126 | |
127 | Square winnerKSq = pos.square<KING>(strongSide); |
128 | Square loserKSq = pos.square<KING>(weakSide); |
129 | |
130 | Value result = pos.non_pawn_material(strongSide) |
131 | + pos.count<PAWN>(strongSide) * PawnValueEg |
132 | + PushToEdges[loserKSq] |
133 | + PushClose[distance(winnerKSq, loserKSq)]; |
134 | |
135 | if ( pos.count<QUEEN>(strongSide) |
136 | || pos.count<ROOK>(strongSide) |
137 | ||(pos.count<BISHOP>(strongSide) && pos.count<KNIGHT>(strongSide)) |
138 | || ( (pos.pieces(strongSide, BISHOP) & ~DarkSquares) |
139 | && (pos.pieces(strongSide, BISHOP) & DarkSquares))) |
140 | result = std::min(result + VALUE_KNOWN_WIN, VALUE_MATE_IN_MAX_PLY - 1); |
141 | |
142 | return strongSide == pos.side_to_move() ? result : -result; |
143 | } |
144 | |
145 | |
146 | /// Mate with KBN vs K. This is similar to KX vs K, but we have to drive the |
147 | /// defending king towards a corner square that our bishop attacks. |
148 | template<> |
149 | Value Endgame<KBNK>::operator()(const Position& pos) const { |
150 | |
151 | assert(verify_material(pos, strongSide, KnightValueMg + BishopValueMg, 0)); |
152 | assert(verify_material(pos, weakSide, VALUE_ZERO, 0)); |
153 | |
154 | Square winnerKSq = pos.square<KING>(strongSide); |
155 | Square loserKSq = pos.square<KING>(weakSide); |
156 | Square bishopSq = pos.square<BISHOP>(strongSide); |
157 | |
158 | // If our Bishop does not attack A1/H8, we flip the enemy king square |
159 | // to drive to opposite corners (A8/H1). |
160 | |
161 | Value result = VALUE_KNOWN_WIN |
162 | + PushClose[distance(winnerKSq, loserKSq)] |
163 | + PushToCorners[opposite_colors(bishopSq, SQ_A1) ? ~loserKSq : loserKSq]; |
164 | |
165 | assert(abs(result) < VALUE_MATE_IN_MAX_PLY); |
166 | return strongSide == pos.side_to_move() ? result : -result; |
167 | } |
168 | |
169 | |
170 | /// KP vs K. This endgame is evaluated with the help of a bitbase. |
171 | template<> |
172 | Value Endgame<KPK>::operator()(const Position& pos) const { |
173 | |
174 | assert(verify_material(pos, strongSide, VALUE_ZERO, 1)); |
175 | assert(verify_material(pos, weakSide, VALUE_ZERO, 0)); |
176 | |
177 | // Assume strongSide is white and the pawn is on files A-D |
178 | Square wksq = normalize(pos, strongSide, pos.square<KING>(strongSide)); |
179 | Square bksq = normalize(pos, strongSide, pos.square<KING>(weakSide)); |
180 | Square psq = normalize(pos, strongSide, pos.square<PAWN>(strongSide)); |
181 | |
182 | Color us = strongSide == pos.side_to_move() ? WHITE : BLACK; |
183 | |
184 | if (!Bitbases::probe(wksq, psq, bksq, us)) |
185 | return VALUE_DRAW; |
186 | |
187 | Value result = VALUE_KNOWN_WIN + PawnValueEg + Value(rank_of(psq)); |
188 | |
189 | return strongSide == pos.side_to_move() ? result : -result; |
190 | } |
191 | |
192 | |
193 | /// KR vs KP. This is a somewhat tricky endgame to evaluate precisely without |
194 | /// a bitbase. The function below returns drawish scores when the pawn is |
195 | /// far advanced with support of the king, while the attacking king is far |
196 | /// away. |
197 | template<> |
198 | Value Endgame<KRKP>::operator()(const Position& pos) const { |
199 | |
200 | assert(verify_material(pos, strongSide, RookValueMg, 0)); |
201 | assert(verify_material(pos, weakSide, VALUE_ZERO, 1)); |
202 | |
203 | Square wksq = relative_square(strongSide, pos.square<KING>(strongSide)); |
204 | Square bksq = relative_square(strongSide, pos.square<KING>(weakSide)); |
205 | Square rsq = relative_square(strongSide, pos.square<ROOK>(strongSide)); |
206 | Square psq = relative_square(strongSide, pos.square<PAWN>(weakSide)); |
207 | |
208 | Square queeningSq = make_square(file_of(psq), RANK_1); |
209 | Value result; |
210 | |
211 | // If the stronger side's king is in front of the pawn, it's a win |
212 | if (forward_file_bb(WHITE, wksq) & psq) |
213 | result = RookValueEg - distance(wksq, psq); |
214 | |
215 | // If the weaker side's king is too far from the pawn and the rook, |
216 | // it's a win. |
217 | else if ( distance(bksq, psq) >= 3 + (pos.side_to_move() == weakSide) |
218 | && distance(bksq, rsq) >= 3) |
219 | result = RookValueEg - distance(wksq, psq); |
220 | |
221 | // If the pawn is far advanced and supported by the defending king, |
222 | // the position is drawish |
223 | else if ( rank_of(bksq) <= RANK_3 |
224 | && distance(bksq, psq) == 1 |
225 | && rank_of(wksq) >= RANK_4 |
226 | && distance(wksq, psq) > 2 + (pos.side_to_move() == strongSide)) |
227 | result = Value(80) - 8 * distance(wksq, psq); |
228 | |
229 | else |
230 | result = Value(200) - 8 * ( distance(wksq, psq + SOUTH) |
231 | - distance(bksq, psq + SOUTH) |
232 | - distance(psq, queeningSq)); |
233 | |
234 | return strongSide == pos.side_to_move() ? result : -result; |
235 | } |
236 | |
237 | |
238 | /// KR vs KB. This is very simple, and always returns drawish scores. The |
239 | /// score is slightly bigger when the defending king is close to the edge. |
240 | template<> |
241 | Value Endgame<KRKB>::operator()(const Position& pos) const { |
242 | |
243 | assert(verify_material(pos, strongSide, RookValueMg, 0)); |
244 | assert(verify_material(pos, weakSide, BishopValueMg, 0)); |
245 | |
246 | Value result = Value(PushToEdges[pos.square<KING>(weakSide)]); |
247 | return strongSide == pos.side_to_move() ? result : -result; |
248 | } |
249 | |
250 | |
251 | /// KR vs KN. The attacking side has slightly better winning chances than |
252 | /// in KR vs KB, particularly if the king and the knight are far apart. |
253 | template<> |
254 | Value Endgame<KRKN>::operator()(const Position& pos) const { |
255 | |
256 | assert(verify_material(pos, strongSide, RookValueMg, 0)); |
257 | assert(verify_material(pos, weakSide, KnightValueMg, 0)); |
258 | |
259 | Square bksq = pos.square<KING>(weakSide); |
260 | Square bnsq = pos.square<KNIGHT>(weakSide); |
261 | Value result = Value(PushToEdges[bksq] + PushAway[distance(bksq, bnsq)]); |
262 | return strongSide == pos.side_to_move() ? result : -result; |
263 | } |
264 | |
265 | |
266 | /// KQ vs KP. In general, this is a win for the stronger side, but there are a |
267 | /// few important exceptions. A pawn on 7th rank and on the A,C,F or H files |
268 | /// with a king positioned next to it can be a draw, so in that case, we only |
269 | /// use the distance between the kings. |
270 | template<> |
271 | Value Endgame<KQKP>::operator()(const Position& pos) const { |
272 | |
273 | assert(verify_material(pos, strongSide, QueenValueMg, 0)); |
274 | assert(verify_material(pos, weakSide, VALUE_ZERO, 1)); |
275 | |
276 | Square winnerKSq = pos.square<KING>(strongSide); |
277 | Square loserKSq = pos.square<KING>(weakSide); |
278 | Square pawnSq = pos.square<PAWN>(weakSide); |
279 | |
280 | Value result = Value(PushClose[distance(winnerKSq, loserKSq)]); |
281 | |
282 | if ( relative_rank(weakSide, pawnSq) != RANK_7 |
283 | || distance(loserKSq, pawnSq) != 1 |
284 | || !((FileABB | FileCBB | FileFBB | FileHBB) & pawnSq)) |
285 | result += QueenValueEg - PawnValueEg; |
286 | |
287 | return strongSide == pos.side_to_move() ? result : -result; |
288 | } |
289 | |
290 | |
291 | /// KQ vs KR. This is almost identical to KX vs K: We give the attacking |
292 | /// king a bonus for having the kings close together, and for forcing the |
293 | /// defending king towards the edge. If we also take care to avoid null move for |
294 | /// the defending side in the search, this is usually sufficient to win KQ vs KR. |
295 | template<> |
296 | Value Endgame<KQKR>::operator()(const Position& pos) const { |
297 | |
298 | assert(verify_material(pos, strongSide, QueenValueMg, 0)); |
299 | assert(verify_material(pos, weakSide, RookValueMg, 0)); |
300 | |
301 | Square winnerKSq = pos.square<KING>(strongSide); |
302 | Square loserKSq = pos.square<KING>(weakSide); |
303 | |
304 | Value result = QueenValueEg |
305 | - RookValueEg |
306 | + PushToEdges[loserKSq] |
307 | + PushClose[distance(winnerKSq, loserKSq)]; |
308 | |
309 | return strongSide == pos.side_to_move() ? result : -result; |
310 | } |
311 | |
312 | |
313 | /// KNN vs KP. Simply push the opposing king to the corner |
314 | template<> |
315 | Value Endgame<KNNKP>::operator()(const Position& pos) const { |
316 | |
317 | assert(verify_material(pos, strongSide, 2 * KnightValueMg, 0)); |
318 | assert(verify_material(pos, weakSide, VALUE_ZERO, 1)); |
319 | |
320 | Value result = 2 * KnightValueEg |
321 | - PawnValueEg |
322 | + PushToEdges[pos.square<KING>(weakSide)]; |
323 | |
324 | return strongSide == pos.side_to_move() ? result : -result; |
325 | } |
326 | |
327 | |
328 | /// Some cases of trivial draws |
329 | template<> Value Endgame<KNNK>::operator()(const Position&) const { return VALUE_DRAW; } |
330 | |
331 | |
332 | /// KB and one or more pawns vs K. It checks for draws with rook pawns and |
333 | /// a bishop of the wrong color. If such a draw is detected, SCALE_FACTOR_DRAW |
334 | /// is returned. If not, the return value is SCALE_FACTOR_NONE, i.e. no scaling |
335 | /// will be used. |
336 | template<> |
337 | ScaleFactor Endgame<KBPsK>::operator()(const Position& pos) const { |
338 | |
339 | assert(pos.non_pawn_material(strongSide) == BishopValueMg); |
340 | assert(pos.count<PAWN>(strongSide) >= 1); |
341 | |
342 | // No assertions about the material of weakSide, because we want draws to |
343 | // be detected even when the weaker side has some pawns. |
344 | |
345 | Bitboard pawns = pos.pieces(strongSide, PAWN); |
346 | File pawnsFile = file_of(lsb(pawns)); |
347 | |
348 | // All pawns are on a single rook file? |
349 | if ( (pawnsFile == FILE_A || pawnsFile == FILE_H) |
350 | && !(pawns & ~file_bb(pawnsFile))) |
351 | { |
352 | Square bishopSq = pos.square<BISHOP>(strongSide); |
353 | Square queeningSq = relative_square(strongSide, make_square(pawnsFile, RANK_8)); |
354 | Square kingSq = pos.square<KING>(weakSide); |
355 | |
356 | if ( opposite_colors(queeningSq, bishopSq) |
357 | && distance(queeningSq, kingSq) <= 1) |
358 | return SCALE_FACTOR_DRAW; |
359 | } |
360 | |
361 | // If all the pawns are on the same B or G file, then it's potentially a draw |
362 | if ( (pawnsFile == FILE_B || pawnsFile == FILE_G) |
363 | && !(pos.pieces(PAWN) & ~file_bb(pawnsFile)) |
364 | && pos.non_pawn_material(weakSide) == 0 |
365 | && pos.count<PAWN>(weakSide) >= 1) |
366 | { |
367 | // Get weakSide pawn that is closest to the home rank |
368 | Square weakPawnSq = frontmost_sq(strongSide, pos.pieces(weakSide, PAWN)); |
369 | |
370 | Square strongKingSq = pos.square<KING>(strongSide); |
371 | Square weakKingSq = pos.square<KING>(weakSide); |
372 | Square bishopSq = pos.square<BISHOP>(strongSide); |
373 | |
374 | // There's potential for a draw if our pawn is blocked on the 7th rank, |
375 | // the bishop cannot attack it or they only have one pawn left |
376 | if ( relative_rank(strongSide, weakPawnSq) == RANK_7 |
377 | && (pos.pieces(strongSide, PAWN) & (weakPawnSq + pawn_push(weakSide))) |
378 | && (opposite_colors(bishopSq, weakPawnSq) || pos.count<PAWN>(strongSide) == 1)) |
379 | { |
380 | int strongKingDist = distance(weakPawnSq, strongKingSq); |
381 | int weakKingDist = distance(weakPawnSq, weakKingSq); |
382 | |
383 | // It's a draw if the weak king is on its back two ranks, within 2 |
384 | // squares of the blocking pawn and the strong king is not |
385 | // closer. (I think this rule only fails in practically |
386 | // unreachable positions such as 5k1K/6p1/6P1/8/8/3B4/8/8 w |
387 | // and positions where qsearch will immediately correct the |
388 | // problem such as 8/4k1p1/6P1/1K6/3B4/8/8/8 w) |
389 | if ( relative_rank(strongSide, weakKingSq) >= RANK_7 |
390 | && weakKingDist <= 2 |
391 | && weakKingDist <= strongKingDist) |
392 | return SCALE_FACTOR_DRAW; |
393 | } |
394 | } |
395 | |
396 | return SCALE_FACTOR_NONE; |
397 | } |
398 | |
399 | |
400 | /// KQ vs KR and one or more pawns. It tests for fortress draws with a rook on |
401 | /// the third rank defended by a pawn. |
402 | template<> |
403 | ScaleFactor Endgame<KQKRPs>::operator()(const Position& pos) const { |
404 | |
405 | assert(verify_material(pos, strongSide, QueenValueMg, 0)); |
406 | assert(pos.count<ROOK>(weakSide) == 1); |
407 | assert(pos.count<PAWN>(weakSide) >= 1); |
408 | |
409 | Square kingSq = pos.square<KING>(weakSide); |
410 | Square rsq = pos.square<ROOK>(weakSide); |
411 | |
412 | if ( relative_rank(weakSide, kingSq) <= RANK_2 |
413 | && relative_rank(weakSide, pos.square<KING>(strongSide)) >= RANK_4 |
414 | && relative_rank(weakSide, rsq) == RANK_3 |
415 | && ( pos.pieces(weakSide, PAWN) |
416 | & pos.attacks_from<KING>(kingSq) |
417 | & pos.attacks_from<PAWN>(rsq, strongSide))) |
418 | return SCALE_FACTOR_DRAW; |
419 | |
420 | return SCALE_FACTOR_NONE; |
421 | } |
422 | |
423 | |
424 | /// KRP vs KR. This function knows a handful of the most important classes of |
425 | /// drawn positions, but is far from perfect. It would probably be a good idea |
426 | /// to add more knowledge in the future. |
427 | /// |
428 | /// It would also be nice to rewrite the actual code for this function, |
429 | /// which is mostly copied from Glaurung 1.x, and isn't very pretty. |
430 | template<> |
431 | ScaleFactor Endgame<KRPKR>::operator()(const Position& pos) const { |
432 | |
433 | assert(verify_material(pos, strongSide, RookValueMg, 1)); |
434 | assert(verify_material(pos, weakSide, RookValueMg, 0)); |
435 | |
436 | // Assume strongSide is white and the pawn is on files A-D |
437 | Square wksq = normalize(pos, strongSide, pos.square<KING>(strongSide)); |
438 | Square bksq = normalize(pos, strongSide, pos.square<KING>(weakSide)); |
439 | Square wrsq = normalize(pos, strongSide, pos.square<ROOK>(strongSide)); |
440 | Square wpsq = normalize(pos, strongSide, pos.square<PAWN>(strongSide)); |
441 | Square brsq = normalize(pos, strongSide, pos.square<ROOK>(weakSide)); |
442 | |
443 | File f = file_of(wpsq); |
444 | Rank r = rank_of(wpsq); |
445 | Square queeningSq = make_square(f, RANK_8); |
446 | int tempo = (pos.side_to_move() == strongSide); |
447 | |
448 | // If the pawn is not too far advanced and the defending king defends the |
449 | // queening square, use the third-rank defence. |
450 | if ( r <= RANK_5 |
451 | && distance(bksq, queeningSq) <= 1 |
452 | && wksq <= SQ_H5 |
453 | && (rank_of(brsq) == RANK_6 || (r <= RANK_3 && rank_of(wrsq) != RANK_6))) |
454 | return SCALE_FACTOR_DRAW; |
455 | |
456 | // The defending side saves a draw by checking from behind in case the pawn |
457 | // has advanced to the 6th rank with the king behind. |
458 | if ( r == RANK_6 |
459 | && distance(bksq, queeningSq) <= 1 |
460 | && rank_of(wksq) + tempo <= RANK_6 |
461 | && (rank_of(brsq) == RANK_1 || (!tempo && distance<File>(brsq, wpsq) >= 3))) |
462 | return SCALE_FACTOR_DRAW; |
463 | |
464 | if ( r >= RANK_6 |
465 | && bksq == queeningSq |
466 | && rank_of(brsq) == RANK_1 |
467 | && (!tempo || distance(wksq, wpsq) >= 2)) |
468 | return SCALE_FACTOR_DRAW; |
469 | |
470 | // White pawn on a7 and rook on a8 is a draw if black's king is on g7 or h7 |
471 | // and the black rook is behind the pawn. |
472 | if ( wpsq == SQ_A7 |
473 | && wrsq == SQ_A8 |
474 | && (bksq == SQ_H7 || bksq == SQ_G7) |
475 | && file_of(brsq) == FILE_A |
476 | && (rank_of(brsq) <= RANK_3 || file_of(wksq) >= FILE_D || rank_of(wksq) <= RANK_5)) |
477 | return SCALE_FACTOR_DRAW; |
478 | |
479 | // If the defending king blocks the pawn and the attacking king is too far |
480 | // away, it's a draw. |
481 | if ( r <= RANK_5 |
482 | && bksq == wpsq + NORTH |
483 | && distance(wksq, wpsq) - tempo >= 2 |
484 | && distance(wksq, brsq) - tempo >= 2) |
485 | return SCALE_FACTOR_DRAW; |
486 | |
487 | // Pawn on the 7th rank supported by the rook from behind usually wins if the |
488 | // attacking king is closer to the queening square than the defending king, |
489 | // and the defending king cannot gain tempi by threatening the attacking rook. |
490 | if ( r == RANK_7 |
491 | && f != FILE_A |
492 | && file_of(wrsq) == f |
493 | && wrsq != queeningSq |
494 | && (distance(wksq, queeningSq) < distance(bksq, queeningSq) - 2 + tempo) |
495 | && (distance(wksq, queeningSq) < distance(bksq, wrsq) + tempo)) |
496 | return ScaleFactor(SCALE_FACTOR_MAX - 2 * distance(wksq, queeningSq)); |
497 | |
498 | // Similar to the above, but with the pawn further back |
499 | if ( f != FILE_A |
500 | && file_of(wrsq) == f |
501 | && wrsq < wpsq |
502 | && (distance(wksq, queeningSq) < distance(bksq, queeningSq) - 2 + tempo) |
503 | && (distance(wksq, wpsq + NORTH) < distance(bksq, wpsq + NORTH) - 2 + tempo) |
504 | && ( distance(bksq, wrsq) + tempo >= 3 |
505 | || ( distance(wksq, queeningSq) < distance(bksq, wrsq) + tempo |
506 | && (distance(wksq, wpsq + NORTH) < distance(bksq, wrsq) + tempo)))) |
507 | return ScaleFactor( SCALE_FACTOR_MAX |
508 | - 8 * distance(wpsq, queeningSq) |
509 | - 2 * distance(wksq, queeningSq)); |
510 | |
511 | // If the pawn is not far advanced and the defending king is somewhere in |
512 | // the pawn's path, it's probably a draw. |
513 | if (r <= RANK_4 && bksq > wpsq) |
514 | { |
515 | if (file_of(bksq) == file_of(wpsq)) |
516 | return ScaleFactor(10); |
517 | if ( distance<File>(bksq, wpsq) == 1 |
518 | && distance(wksq, bksq) > 2) |
519 | return ScaleFactor(24 - 2 * distance(wksq, bksq)); |
520 | } |
521 | return SCALE_FACTOR_NONE; |
522 | } |
523 | |
524 | template<> |
525 | ScaleFactor Endgame<KRPKB>::operator()(const Position& pos) const { |
526 | |
527 | assert(verify_material(pos, strongSide, RookValueMg, 1)); |
528 | assert(verify_material(pos, weakSide, BishopValueMg, 0)); |
529 | |
530 | // Test for a rook pawn |
531 | if (pos.pieces(PAWN) & (FileABB | FileHBB)) |
532 | { |
533 | Square ksq = pos.square<KING>(weakSide); |
534 | Square bsq = pos.square<BISHOP>(weakSide); |
535 | Square psq = pos.square<PAWN>(strongSide); |
536 | Rank rk = relative_rank(strongSide, psq); |
537 | Direction push = pawn_push(strongSide); |
538 | |
539 | // If the pawn is on the 5th rank and the pawn (currently) is on |
540 | // the same color square as the bishop then there is a chance of |
541 | // a fortress. Depending on the king position give a moderate |
542 | // reduction or a stronger one if the defending king is near the |
543 | // corner but not trapped there. |
544 | if (rk == RANK_5 && !opposite_colors(bsq, psq)) |
545 | { |
546 | int d = distance(psq + 3 * push, ksq); |
547 | |
548 | if (d <= 2 && !(d == 0 && ksq == pos.square<KING>(strongSide) + 2 * push)) |
549 | return ScaleFactor(24); |
550 | else |
551 | return ScaleFactor(48); |
552 | } |
553 | |
554 | // When the pawn has moved to the 6th rank we can be fairly sure |
555 | // it's drawn if the bishop attacks the square in front of the |
556 | // pawn from a reasonable distance and the defending king is near |
557 | // the corner |
558 | if ( rk == RANK_6 |
559 | && distance(psq + 2 * push, ksq) <= 1 |
560 | && (PseudoAttacks[BISHOP][bsq] & (psq + push)) |
561 | && distance<File>(bsq, psq) >= 2) |
562 | return ScaleFactor(8); |
563 | } |
564 | |
565 | return SCALE_FACTOR_NONE; |
566 | } |
567 | |
568 | /// KRPP vs KRP. There is just a single rule: if the stronger side has no passed |
569 | /// pawns and the defending king is actively placed, the position is drawish. |
570 | template<> |
571 | ScaleFactor Endgame<KRPPKRP>::operator()(const Position& pos) const { |
572 | |
573 | assert(verify_material(pos, strongSide, RookValueMg, 2)); |
574 | assert(verify_material(pos, weakSide, RookValueMg, 1)); |
575 | |
576 | Square wpsq1 = pos.squares<PAWN>(strongSide)[0]; |
577 | Square wpsq2 = pos.squares<PAWN>(strongSide)[1]; |
578 | Square bksq = pos.square<KING>(weakSide); |
579 | |
580 | // Does the stronger side have a passed pawn? |
581 | if (pos.pawn_passed(strongSide, wpsq1) || pos.pawn_passed(strongSide, wpsq2)) |
582 | return SCALE_FACTOR_NONE; |
583 | |
584 | Rank r = std::max(relative_rank(strongSide, wpsq1), relative_rank(strongSide, wpsq2)); |
585 | |
586 | if ( distance<File>(bksq, wpsq1) <= 1 |
587 | && distance<File>(bksq, wpsq2) <= 1 |
588 | && relative_rank(strongSide, bksq) > r) |
589 | { |
590 | assert(r > RANK_1 && r < RANK_7); |
591 | return ScaleFactor(KRPPKRPScaleFactors[r]); |
592 | } |
593 | return SCALE_FACTOR_NONE; |
594 | } |
595 | |
596 | |
597 | /// K and two or more pawns vs K. There is just a single rule here: If all pawns |
598 | /// are on the same rook file and are blocked by the defending king, it's a draw. |
599 | template<> |
600 | ScaleFactor Endgame<KPsK>::operator()(const Position& pos) const { |
601 | |
602 | assert(pos.non_pawn_material(strongSide) == VALUE_ZERO); |
603 | assert(pos.count<PAWN>(strongSide) >= 2); |
604 | assert(verify_material(pos, weakSide, VALUE_ZERO, 0)); |
605 | |
606 | Square ksq = pos.square<KING>(weakSide); |
607 | Bitboard pawns = pos.pieces(strongSide, PAWN); |
608 | |
609 | // If all pawns are ahead of the king, on a single rook file and |
610 | // the king is within one file of the pawns, it's a draw. |
611 | if ( !(pawns & ~forward_ranks_bb(weakSide, ksq)) |
612 | && !((pawns & ~FileABB) && (pawns & ~FileHBB)) |
613 | && distance<File>(ksq, lsb(pawns)) <= 1) |
614 | return SCALE_FACTOR_DRAW; |
615 | |
616 | return SCALE_FACTOR_NONE; |
617 | } |
618 | |
619 | |
620 | /// KBP vs KB. There are two rules: if the defending king is somewhere along the |
621 | /// path of the pawn, and the square of the king is not of the same color as the |
622 | /// stronger side's bishop, it's a draw. If the two bishops have opposite color, |
623 | /// it's almost always a draw. |
624 | template<> |
625 | ScaleFactor Endgame<KBPKB>::operator()(const Position& pos) const { |
626 | |
627 | assert(verify_material(pos, strongSide, BishopValueMg, 1)); |
628 | assert(verify_material(pos, weakSide, BishopValueMg, 0)); |
629 | |
630 | Square pawnSq = pos.square<PAWN>(strongSide); |
631 | Square strongBishopSq = pos.square<BISHOP>(strongSide); |
632 | Square weakBishopSq = pos.square<BISHOP>(weakSide); |
633 | Square weakKingSq = pos.square<KING>(weakSide); |
634 | |
635 | // Case 1: Defending king blocks the pawn, and cannot be driven away |
636 | if ( file_of(weakKingSq) == file_of(pawnSq) |
637 | && relative_rank(strongSide, pawnSq) < relative_rank(strongSide, weakKingSq) |
638 | && ( opposite_colors(weakKingSq, strongBishopSq) |
639 | || relative_rank(strongSide, weakKingSq) <= RANK_6)) |
640 | return SCALE_FACTOR_DRAW; |
641 | |
642 | // Case 2: Opposite colored bishops |
643 | if (opposite_colors(strongBishopSq, weakBishopSq)) |
644 | return SCALE_FACTOR_DRAW; |
645 | |
646 | return SCALE_FACTOR_NONE; |
647 | } |
648 | |
649 | |
650 | /// KBPP vs KB. It detects a few basic draws with opposite-colored bishops |
651 | template<> |
652 | ScaleFactor Endgame<KBPPKB>::operator()(const Position& pos) const { |
653 | |
654 | assert(verify_material(pos, strongSide, BishopValueMg, 2)); |
655 | assert(verify_material(pos, weakSide, BishopValueMg, 0)); |
656 | |
657 | Square wbsq = pos.square<BISHOP>(strongSide); |
658 | Square bbsq = pos.square<BISHOP>(weakSide); |
659 | |
660 | if (!opposite_colors(wbsq, bbsq)) |
661 | return SCALE_FACTOR_NONE; |
662 | |
663 | Square ksq = pos.square<KING>(weakSide); |
664 | Square psq1 = pos.squares<PAWN>(strongSide)[0]; |
665 | Square psq2 = pos.squares<PAWN>(strongSide)[1]; |
666 | Square blockSq1, blockSq2; |
667 | |
668 | if (relative_rank(strongSide, psq1) > relative_rank(strongSide, psq2)) |
669 | { |
670 | blockSq1 = psq1 + pawn_push(strongSide); |
671 | blockSq2 = make_square(file_of(psq2), rank_of(psq1)); |
672 | } |
673 | else |
674 | { |
675 | blockSq1 = psq2 + pawn_push(strongSide); |
676 | blockSq2 = make_square(file_of(psq1), rank_of(psq2)); |
677 | } |
678 | |
679 | switch (distance<File>(psq1, psq2)) |
680 | { |
681 | case 0: |
682 | // Both pawns are on the same file. It's an easy draw if the defender firmly |
683 | // controls some square in the frontmost pawn's path. |
684 | if ( file_of(ksq) == file_of(blockSq1) |
685 | && relative_rank(strongSide, ksq) >= relative_rank(strongSide, blockSq1) |
686 | && opposite_colors(ksq, wbsq)) |
687 | return SCALE_FACTOR_DRAW; |
688 | else |
689 | return SCALE_FACTOR_NONE; |
690 | |
691 | case 1: |
692 | // Pawns on adjacent files. It's a draw if the defender firmly controls the |
693 | // square in front of the frontmost pawn's path, and the square diagonally |
694 | // behind this square on the file of the other pawn. |
695 | if ( ksq == blockSq1 |
696 | && opposite_colors(ksq, wbsq) |
697 | && ( bbsq == blockSq2 |
698 | || (pos.attacks_from<BISHOP>(blockSq2) & pos.pieces(weakSide, BISHOP)) |
699 | || distance<Rank>(psq1, psq2) >= 2)) |
700 | return SCALE_FACTOR_DRAW; |
701 | |
702 | else if ( ksq == blockSq2 |
703 | && opposite_colors(ksq, wbsq) |
704 | && ( bbsq == blockSq1 |
705 | || (pos.attacks_from<BISHOP>(blockSq1) & pos.pieces(weakSide, BISHOP)))) |
706 | return SCALE_FACTOR_DRAW; |
707 | else |
708 | return SCALE_FACTOR_NONE; |
709 | |
710 | default: |
711 | // The pawns are not on the same file or adjacent files. No scaling. |
712 | return SCALE_FACTOR_NONE; |
713 | } |
714 | } |
715 | |
716 | |
717 | /// KBP vs KN. There is a single rule: If the defending king is somewhere along |
718 | /// the path of the pawn, and the square of the king is not of the same color as |
719 | /// the stronger side's bishop, it's a draw. |
720 | template<> |
721 | ScaleFactor Endgame<KBPKN>::operator()(const Position& pos) const { |
722 | |
723 | assert(verify_material(pos, strongSide, BishopValueMg, 1)); |
724 | assert(verify_material(pos, weakSide, KnightValueMg, 0)); |
725 | |
726 | Square pawnSq = pos.square<PAWN>(strongSide); |
727 | Square strongBishopSq = pos.square<BISHOP>(strongSide); |
728 | Square weakKingSq = pos.square<KING>(weakSide); |
729 | |
730 | if ( file_of(weakKingSq) == file_of(pawnSq) |
731 | && relative_rank(strongSide, pawnSq) < relative_rank(strongSide, weakKingSq) |
732 | && ( opposite_colors(weakKingSq, strongBishopSq) |
733 | || relative_rank(strongSide, weakKingSq) <= RANK_6)) |
734 | return SCALE_FACTOR_DRAW; |
735 | |
736 | return SCALE_FACTOR_NONE; |
737 | } |
738 | |
739 | |
740 | /// KNP vs K. There is a single rule: if the pawn is a rook pawn on the 7th rank |
741 | /// and the defending king prevents the pawn from advancing, the position is drawn. |
742 | template<> |
743 | ScaleFactor Endgame<KNPK>::operator()(const Position& pos) const { |
744 | |
745 | assert(verify_material(pos, strongSide, KnightValueMg, 1)); |
746 | assert(verify_material(pos, weakSide, VALUE_ZERO, 0)); |
747 | |
748 | // Assume strongSide is white and the pawn is on files A-D |
749 | Square pawnSq = normalize(pos, strongSide, pos.square<PAWN>(strongSide)); |
750 | Square weakKingSq = normalize(pos, strongSide, pos.square<KING>(weakSide)); |
751 | |
752 | if (pawnSq == SQ_A7 && distance(SQ_A8, weakKingSq) <= 1) |
753 | return SCALE_FACTOR_DRAW; |
754 | |
755 | return SCALE_FACTOR_NONE; |
756 | } |
757 | |
758 | |
759 | /// KNP vs KB. If knight can block bishop from taking pawn, it's a win. |
760 | /// Otherwise the position is drawn. |
761 | template<> |
762 | ScaleFactor Endgame<KNPKB>::operator()(const Position& pos) const { |
763 | |
764 | assert(verify_material(pos, strongSide, KnightValueMg, 1)); |
765 | assert(verify_material(pos, weakSide, BishopValueMg, 0)); |
766 | |
767 | Square pawnSq = pos.square<PAWN>(strongSide); |
768 | Square bishopSq = pos.square<BISHOP>(weakSide); |
769 | Square weakKingSq = pos.square<KING>(weakSide); |
770 | |
771 | // King needs to get close to promoting pawn to prevent knight from blocking. |
772 | // Rules for this are very tricky, so just approximate. |
773 | if (forward_file_bb(strongSide, pawnSq) & pos.attacks_from<BISHOP>(bishopSq)) |
774 | return ScaleFactor(distance(weakKingSq, pawnSq)); |
775 | |
776 | return SCALE_FACTOR_NONE; |
777 | } |
778 | |
779 | |
780 | /// KP vs KP. This is done by removing the weakest side's pawn and probing the |
781 | /// KP vs K bitbase: If the weakest side has a draw without the pawn, it probably |
782 | /// has at least a draw with the pawn as well. The exception is when the stronger |
783 | /// side's pawn is far advanced and not on a rook file; in this case it is often |
784 | /// possible to win (e.g. 8/4k3/3p4/3P4/6K1/8/8/8 w - - 0 1). |
785 | template<> |
786 | ScaleFactor Endgame<KPKP>::operator()(const Position& pos) const { |
787 | |
788 | assert(verify_material(pos, strongSide, VALUE_ZERO, 1)); |
789 | assert(verify_material(pos, weakSide, VALUE_ZERO, 1)); |
790 | |
791 | // Assume strongSide is white and the pawn is on files A-D |
792 | Square wksq = normalize(pos, strongSide, pos.square<KING>(strongSide)); |
793 | Square bksq = normalize(pos, strongSide, pos.square<KING>(weakSide)); |
794 | Square psq = normalize(pos, strongSide, pos.square<PAWN>(strongSide)); |
795 | |
796 | Color us = strongSide == pos.side_to_move() ? WHITE : BLACK; |
797 | |
798 | // If the pawn has advanced to the fifth rank or further, and is not a |
799 | // rook pawn, it's too dangerous to assume that it's at least a draw. |
800 | if (rank_of(psq) >= RANK_5 && file_of(psq) != FILE_A) |
801 | return SCALE_FACTOR_NONE; |
802 | |
803 | // Probe the KPK bitbase with the weakest side's pawn removed. If it's a draw, |
804 | // it's probably at least a draw even with the pawn. |
805 | return Bitbases::probe(wksq, psq, bksq, us) ? SCALE_FACTOR_NONE : SCALE_FACTOR_DRAW; |
806 | } |
807 | |