Subversion Repositories Games.Chess Giants

Rev

Rev 169 | Details | Compare with Previous | Last modification | View Log | RSS feed

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