Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
99 | pmbaty | 1 | /* |
2 | Texel - A UCI chess engine. |
||
3 | Copyright (C) 2012-2014 Peter Ă–sterlund, peterosterlund2@gmail.com |
||
4 | |||
5 | This program is free software: you can redistribute it and/or modify |
||
6 | it under the terms of the GNU General Public License as published by |
||
7 | the Free Software Foundation, either version 3 of the License, or |
||
8 | (at your option) any later version. |
||
9 | |||
10 | This program is distributed in the hope that it will be useful, |
||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
13 | GNU General Public License for more details. |
||
14 | |||
15 | You should have received a copy of the GNU General Public License |
||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. |
||
17 | */ |
||
18 | |||
19 | /* |
||
20 | * endGameEval.cpp |
||
21 | * |
||
22 | * Created on: Dec 26, 2014 |
||
23 | * Author: petero |
||
24 | */ |
||
25 | |||
26 | #include "endGameEval.hpp" |
||
27 | #include "position.hpp" |
||
28 | #include "piece.hpp" |
||
29 | #include "parameters.hpp" |
||
30 | |||
31 | const int EndGameEval::distToH1A8[8][8] = { { 0, 1, 2, 3, 4, 5, 6, 7 }, |
||
32 | { 1, 2, 3, 4, 5, 6, 7, 6 }, |
||
33 | { 2, 3, 4, 5, 6, 7, 6, 5 }, |
||
34 | { 3, 4, 5, 6, 7, 6, 5, 4 }, |
||
35 | { 4, 5, 6, 7, 6, 5, 4, 3 }, |
||
36 | { 5, 6, 7, 6, 5, 4, 3, 2 }, |
||
37 | { 6, 7, 6, 5, 4, 3, 2, 1 }, |
||
38 | { 7, 6, 5, 4, 3, 2, 1, 0 } }; |
||
39 | |||
40 | const int EndGameEval::winKingTable[64] = { |
||
41 | 0, 4, 10, 10, 10, 10, 4, 0, |
||
42 | 4, 15, 19, 20, 20, 19, 15, 4, |
||
43 | 10, 19, 25, 25, 25, 25, 19, 10, |
||
44 | 10, 20, 25, 25, 25, 25, 20, 10, |
||
45 | 10, 20, 25, 25, 25, 25, 20, 10, |
||
46 | 10, 19, 25, 25, 25, 25, 19, 10, |
||
47 | 4, 15, 19, 20, 20, 19, 15, 4, |
||
48 | 0, 4, 10, 10, 10, 10, 4, 0 |
||
49 | }; |
||
50 | |||
51 | |||
52 | template int EndGameEval::endGameEval<false>(const Position&, U64, int); |
||
53 | template int EndGameEval::endGameEval<true>(const Position&, U64, int); |
||
54 | |||
55 | /** Implements special knowledge for some endgame situations. */ |
||
56 | template <bool doEval> |
||
57 | int |
||
58 | EndGameEval::endGameEval(const Position& pos, U64 passedPawns, int oldScore) { |
||
59 | int score = oldScore; |
||
60 | const int wMtrlPawns = pos.wMtrlPawns(); |
||
61 | const int bMtrlPawns = pos.bMtrlPawns(); |
||
62 | const int wMtrlNoPawns = pos.wMtrl() - wMtrlPawns; |
||
63 | const int bMtrlNoPawns = pos.bMtrl() - bMtrlPawns; |
||
64 | |||
65 | // Handle special endgames |
||
66 | using MI = MatId; |
||
67 | switch (pos.materialId()) { |
||
68 | case 0: |
||
69 | case MI::WN: case MI::BN: case MI::WB: case MI::BB: |
||
70 | case MI::WN + MI::BN: case MI::WN + MI::BB: |
||
71 | case MI::WB + MI::BN: case MI::WB + MI::BB: |
||
72 | if (!doEval) return 1; |
||
73 | return 0; // King + minor piece vs king + minor piece is a draw |
||
74 | case MI::WQ + MI::BP: { |
||
75 | if (!doEval) return 1; |
||
76 | int wk = pos.getKingSq(true); |
||
77 | int wq = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::WQUEEN)); |
||
78 | int bk = pos.getKingSq(false); |
||
79 | int bp = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::BPAWN)); |
||
80 | return kqkpEval(wk, wq, bk, bp, pos.isWhiteMove(), score); |
||
81 | } |
||
82 | case MI::BQ + MI::WP: { |
||
83 | if (!doEval) return 1; |
||
84 | int bk = pos.getKingSq(false); |
||
85 | int bq = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::BQUEEN)); |
||
86 | int wk = pos.getKingSq(true); |
||
87 | int wp = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::WPAWN)); |
||
88 | return -kqkpEval(63-bk, 63-bq, 63-wk, 63-wp, !pos.isWhiteMove(), -score); |
||
89 | } |
||
90 | case MI::WQ: { |
||
91 | if (!doEval) return 1; |
||
92 | if (!pos.isWhiteMove() && |
||
93 | (pos.pieceTypeBB(Piece::BKING) & BitBoard::maskCorners) && |
||
94 | (pos.pieceTypeBB(Piece::WQUEEN) & BitBoard::sqMask(C2,B3,F2,G3,B6,C7,G6,F7)) && |
||
95 | (BitBoard::getTaxiDistance(pos.getKingSq(false), |
||
96 | BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::WQUEEN))) == 3)) |
||
97 | return 0; |
||
98 | break; |
||
99 | } |
||
100 | case MI::BQ: { |
||
101 | if (!doEval) return 1; |
||
102 | if (pos.isWhiteMove() && |
||
103 | (pos.pieceTypeBB(Piece::WKING) & BitBoard::maskCorners) && |
||
104 | (pos.pieceTypeBB(Piece::BQUEEN) & BitBoard::sqMask(C2,B3,F2,G3,B6,C7,G6,F7)) && |
||
105 | (BitBoard::getTaxiDistance(pos.getKingSq(true), |
||
106 | BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::BQUEEN))) == 3)) |
||
107 | return 0; |
||
108 | break; |
||
109 | } |
||
110 | case MI::WR + MI::BP: { |
||
111 | if (!doEval) return 1; |
||
112 | int bp = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::BPAWN)); |
||
113 | return krkpEval(pos.getKingSq(true), pos.getKingSq(false), |
||
114 | bp, pos.isWhiteMove(), score); |
||
115 | } |
||
116 | case MI::BR + MI::WP: { |
||
117 | if (!doEval) return 1; |
||
118 | int wp = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::WPAWN)); |
||
119 | return -krkpEval(63-pos.getKingSq(false), 63-pos.getKingSq(true), |
||
120 | 63-wp, !pos.isWhiteMove(), -score); |
||
121 | } |
||
122 | case MI::WR + MI::BB: { |
||
123 | if (!doEval) return 1; |
||
124 | score /= 8; |
||
125 | const int kSq = pos.getKingSq(false); |
||
126 | const int x = Position::getX(kSq); |
||
127 | const int y = Position::getY(kSq); |
||
128 | if ((pos.pieceTypeBB(Piece::BBISHOP) & BitBoard::maskDarkSq) != 0) |
||
129 | score += (7 - distToH1A8[7-y][7-x]) * 7; |
||
130 | else |
||
131 | score += (7 - distToH1A8[7-y][x]) * 7; |
||
132 | return score; |
||
133 | } |
||
134 | case MI::BR + MI::WB: { |
||
135 | if (!doEval) return 1; |
||
136 | score /= 8; |
||
137 | const int kSq = pos.getKingSq(true); |
||
138 | const int x = Position::getX(kSq); |
||
139 | const int y = Position::getY(kSq); |
||
140 | if ((pos.pieceTypeBB(Piece::WBISHOP) & BitBoard::maskDarkSq) != 0) |
||
141 | score -= (7 - distToH1A8[7-y][7-x]) * 7; |
||
142 | else |
||
143 | score -= (7 - distToH1A8[7-y][x]) * 7; |
||
144 | return score; |
||
145 | } |
||
146 | case MI::WR + MI::WP + MI::BR: { |
||
147 | if (!doEval) return 1; |
||
148 | int wk = pos.getKingSq(true); |
||
149 | int bk = pos.getKingSq(false); |
||
150 | int wp = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::WPAWN)); |
||
151 | int wr = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::WROOK)); |
||
152 | int br = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::BROOK)); |
||
153 | return krpkrEval(wk, bk, wp, wr, br, pos.isWhiteMove()); |
||
154 | } |
||
155 | case MI::BR + MI::BP + MI::WR: { |
||
156 | if (!doEval) return 1; |
||
157 | int wk = pos.getKingSq(true); |
||
158 | int bk = pos.getKingSq(false); |
||
159 | int bp = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::BPAWN)); |
||
160 | int wr = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::WROOK)); |
||
161 | int br = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::BROOK)); |
||
162 | return -krpkrEval(63-bk, 63-wk, 63-bp, 63-br, 63-wr, !pos.isWhiteMove()); |
||
163 | } |
||
164 | case MI::WR + MI::WP + MI::BR + MI::BP: { |
||
165 | if (!doEval) return 1; |
||
166 | int wk = pos.getKingSq(true); |
||
167 | int bk = pos.getKingSq(false); |
||
168 | int wp = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::WPAWN)); |
||
169 | int wr = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::WROOK)); |
||
170 | int br = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::BROOK)); |
||
171 | int bp = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::BPAWN)); |
||
172 | return krpkrpEval(wk, bk, wp, wr, br, bp, pos.isWhiteMove(), score); |
||
173 | } |
||
174 | case MI::WN * 2: |
||
175 | case MI::BN * 2: |
||
176 | if (!doEval) return 1; |
||
177 | return 0; // KNNK is a draw |
||
178 | case MI::WN + MI::WB: { |
||
179 | if (!doEval) return 1; |
||
180 | bool darkBishop = (pos.pieceTypeBB(Piece::WBISHOP) & BitBoard::maskDarkSq) != 0; |
||
181 | return kbnkEval(pos.getKingSq(true), pos.getKingSq(false), darkBishop); |
||
182 | } |
||
183 | case MI::BN + MI::BB: { |
||
184 | if (!doEval) return 1; |
||
185 | bool darkBishop = (pos.pieceTypeBB(Piece::BBISHOP) & BitBoard::maskDarkSq) != 0; |
||
186 | return -kbnkEval(63-pos.getKingSq(false), 63-pos.getKingSq(true), darkBishop); |
||
187 | } |
||
188 | case MI::WP: { |
||
189 | if (!doEval) return 1; |
||
190 | int wp = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::WPAWN)); |
||
191 | return kpkEval(pos.getKingSq(true), pos.getKingSq(false), |
||
192 | wp, pos.isWhiteMove()); |
||
193 | } |
||
194 | case MI::BP: { |
||
195 | if (!doEval) return 1; |
||
196 | int bp = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::BPAWN)); |
||
197 | return -kpkEval(63-pos.getKingSq(false), 63-pos.getKingSq(true), |
||
198 | 63-bp, !pos.isWhiteMove()); |
||
199 | } |
||
200 | case MI::WP + MI::BP: { |
||
201 | if (!doEval) return 1; |
||
202 | int wk = pos.getKingSq(true); |
||
203 | int bk = pos.getKingSq(false); |
||
204 | int wp = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::WPAWN)); |
||
205 | int bp = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::BPAWN)); |
||
206 | if (kpkpEval(wk, bk, wp, bp, score)) |
||
207 | return score; |
||
208 | break; |
||
209 | } |
||
210 | case MI::WB + MI::WP + MI::BB: { |
||
211 | if (!doEval) return 1; |
||
212 | int wb = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::WBISHOP)); |
||
213 | int wp = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::WPAWN)); |
||
214 | int bb = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::BBISHOP)); |
||
215 | return kbpkbEval(pos.getKingSq(true), wb, wp, pos.getKingSq(false), bb, score); |
||
216 | } |
||
217 | case MI::BB + MI::BP + MI::WB: { |
||
218 | if (!doEval) return 1; |
||
219 | int bb = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::BBISHOP)); |
||
220 | int bp = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::BPAWN)); |
||
221 | int wb = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::WBISHOP)); |
||
222 | return -kbpkbEval(63-pos.getKingSq(false), 63-bb, 63-bp, 63-pos.getKingSq(true), 63-wb, -score); |
||
223 | } |
||
224 | case MI::WB + MI::WP + MI::BN: { |
||
225 | if (!doEval) return 1; |
||
226 | int wb = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::WBISHOP)); |
||
227 | int wp = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::WPAWN)); |
||
228 | int bn = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::BKNIGHT)); |
||
229 | return kbpknEval(pos.getKingSq(true), wb, wp, pos.getKingSq(false), bn, score); |
||
230 | } |
||
231 | case MI::BB + MI::BP + MI::WN: { |
||
232 | if (!doEval) return 1; |
||
233 | int bb = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::BBISHOP)); |
||
234 | int bp = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::BPAWN)); |
||
235 | int wn = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::WKNIGHT)); |
||
236 | return -kbpknEval(63-pos.getKingSq(false), 63-bb, 63-bp, 63-pos.getKingSq(true), 63-wn, -score); |
||
237 | } |
||
238 | case MI::WN + MI::WP + MI::BB: { |
||
239 | if (!doEval) return 1; |
||
240 | int wn = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::WKNIGHT)); |
||
241 | int wp = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::WPAWN)); |
||
242 | int bb = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::BBISHOP)); |
||
243 | return knpkbEval(pos.getKingSq(true), wn, wp, pos.getKingSq(false), bb, |
||
244 | score, pos.isWhiteMove()); |
||
245 | } |
||
246 | case MI::BN + MI::BP + MI::WB: { |
||
247 | if (!doEval) return 1; |
||
248 | int bn = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::BKNIGHT)); |
||
249 | int bp = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::BPAWN)); |
||
250 | int wb = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::WBISHOP)); |
||
251 | return -knpkbEval(63-pos.getKingSq(false), 63-bn, 63-bp, 63-pos.getKingSq(true), 63-wb, |
||
252 | -score, !pos.isWhiteMove()); |
||
253 | } |
||
254 | case MI::WN + MI::WP: { |
||
255 | if (!doEval) return 1; |
||
256 | int wn = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::WKNIGHT)); |
||
257 | int wp = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::WPAWN)); |
||
258 | return knpkEval(pos.getKingSq(true), wn, wp, pos.getKingSq(false), |
||
259 | score, pos.isWhiteMove()); |
||
260 | } |
||
261 | case MI::BN + MI::BP: { |
||
262 | if (!doEval) return 1; |
||
263 | int bn = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::BKNIGHT)); |
||
264 | int bp = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::BPAWN)); |
||
265 | return -knpkEval(63-pos.getKingSq(false), 63-bn, 63-bp, 63-pos.getKingSq(true), |
||
266 | -score, !pos.isWhiteMove()); |
||
267 | } |
||
268 | } |
||
269 | |||
270 | // QvsRP fortress detection |
||
271 | if (pos.pieceTypeBB(Piece::WQUEEN) && (pos.wMtrl() == qV) && |
||
272 | pos.pieceTypeBB(Piece::BROOK) && pos.pieceTypeBB(Piece::BPAWN) && |
||
273 | (pos.bMtrl() - pos.bMtrlPawns() < rV * 2)) { |
||
274 | if (!doEval) return 1; |
||
275 | if (score > 0) { |
||
276 | int wk = pos.getKingSq(true); |
||
277 | int wq = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::WQUEEN)); |
||
278 | int bk = pos.getKingSq(false); |
||
279 | int br = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::BROOK)); |
||
280 | U64 m = pos.pieceTypeBB(Piece::BPAWN); |
||
281 | int newScore = score; |
||
282 | while (m) { |
||
283 | int bp = BitBoard::extractSquare(m); |
||
284 | int s2 = kqkrpEval(wk, wq, bk, br, bp, pos.isWhiteMove(), score); |
||
285 | newScore = std::min(newScore, s2); |
||
286 | } |
||
287 | if (newScore < score) |
||
288 | return newScore; |
||
289 | } |
||
290 | } |
||
291 | if (pos.pieceTypeBB(Piece::BQUEEN) && (pos.bMtrl() == qV) && |
||
292 | pos.pieceTypeBB(Piece::WROOK) && pos.pieceTypeBB(Piece::WPAWN) && |
||
293 | (pos.wMtrl() - pos.wMtrlPawns() < rV * 2)) { |
||
294 | if (!doEval) return 1; |
||
295 | if (score < 0) { |
||
296 | int bk = pos.getKingSq(false); |
||
297 | int bq = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::BQUEEN)); |
||
298 | int wk = pos.getKingSq(true); |
||
299 | int wr = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::WROOK)); |
||
300 | U64 m = pos.pieceTypeBB(Piece::WPAWN); |
||
301 | int newScore = score; |
||
302 | while (m) { |
||
303 | int wp = BitBoard::extractSquare(m); |
||
304 | int s2 = -kqkrpEval(63-bk, 63-bq, 63-wk, 63-wr, 63-wp, !pos.isWhiteMove(), -score); |
||
305 | newScore = std::max(newScore, s2); |
||
306 | } |
||
307 | if (newScore > score) |
||
308 | return newScore; |
||
309 | } |
||
310 | } |
||
311 | |||
312 | const int nWN = BitBoard::bitCount(pos.pieceTypeBB(Piece::WKNIGHT)); |
||
313 | const int nBN = BitBoard::bitCount(pos.pieceTypeBB(Piece::BKNIGHT)); |
||
314 | const int nWB1 = BitBoard::bitCount(pos.pieceTypeBB(Piece::WBISHOP) & BitBoard::maskLightSq); |
||
315 | const int nWB2 = BitBoard::bitCount(pos.pieceTypeBB(Piece::WBISHOP) & BitBoard::maskDarkSq); |
||
316 | const int nBB1 = BitBoard::bitCount(pos.pieceTypeBB(Piece::BBISHOP) & BitBoard::maskLightSq); |
||
317 | const int nBB2 = BitBoard::bitCount(pos.pieceTypeBB(Piece::BBISHOP) & BitBoard::maskDarkSq); |
||
318 | const int qV = ::qV; |
||
319 | |||
320 | if (pos.materialId() == MI::WB * 2 + MI::BN) { |
||
321 | if (!doEval) return 1; |
||
322 | if (nWB1 == 1) |
||
323 | return 100 + mateEval(pos.getKingSq(true), pos.getKingSq(false)); |
||
324 | } |
||
325 | if (pos.materialId() == MI::BB * 2 + MI::WN) { |
||
326 | if (!doEval) return 1; |
||
327 | if (nBB1 == 1) |
||
328 | return -(100 + mateEval(pos.getKingSq(false), pos.getKingSq(true))); |
||
329 | } |
||
330 | |||
331 | // Bonus for K[BN][BN]KQ |
||
332 | if ((pos.bMtrl() == qV) && pos.pieceTypeBB(Piece::BQUEEN) && ((nWN >= 2) || (nWB1 + nWB2 >= 2))) { |
||
333 | if (!doEval) return 1; |
||
334 | if ((score < 0) && ((nWN >= 2) || ((nWB1 >= 1) && (nWB2 >= 1)))) |
||
335 | return -((pos.bMtrl() - pos.wMtrl()) / 8 + mateEval(pos.getKingSq(false), pos.getKingSq(true))); |
||
336 | } |
||
337 | if ((pos.wMtrl() == qV) && pos.pieceTypeBB(Piece::WQUEEN) && ((nBN >= 2) || (nBB1 + nBB2 >= 2))) { |
||
338 | if (!doEval) return 1; |
||
339 | if ((score > 0) && ((nBN >= 2) || ((nBB1 >= 1) && (nBB2 >= 1)))) |
||
340 | return (pos.wMtrl() - pos.bMtrl()) / 8 + mateEval(pos.getKingSq(true), pos.getKingSq(false)); |
||
341 | } |
||
342 | if ((pos.bMtrl() == qV) && pos.pieceTypeBB(Piece::BQUEEN) && ((nWN >= 1) && (nWB1 + nWB2 >= 1))) { |
||
343 | if (!doEval) return 1; |
||
344 | if (score < 0) |
||
345 | return -((pos.bMtrl() - pos.wMtrl()) / 2 + mateEval(pos.getKingSq(false), pos.getKingSq(true))); |
||
346 | } |
||
347 | if ((pos.wMtrl() == qV) && pos.pieceTypeBB(Piece::WQUEEN) && ((nBN >= 1) && (nBB1 + nBB2 >= 1))) { |
||
348 | if (!doEval) return 1; |
||
349 | if (score > 0) |
||
350 | return (pos.wMtrl() - pos.bMtrl()) / 2 + mateEval(pos.getKingSq(true), pos.getKingSq(false)); |
||
351 | } |
||
352 | |||
353 | // Bonus for KRK |
||
354 | if ((pos.bMtrl() == 0) && pos.pieceTypeBB(Piece::WROOK)) { |
||
355 | if (!doEval) return 1; |
||
356 | return 400 + pos.wMtrl() - pos.bMtrl() + mateEval(pos.getKingSq(true), pos.getKingSq(false)); |
||
357 | } |
||
358 | if ((pos.wMtrl() == 0) && pos.pieceTypeBB(Piece::BROOK)) { |
||
359 | if (!doEval) return 1; |
||
360 | return -(400 + pos.bMtrl() - pos.wMtrl() + mateEval(pos.getKingSq(false), pos.getKingSq(true))); |
||
361 | } |
||
362 | |||
363 | // Bonus for KQK[BN] |
||
364 | const int bV = ::bV; |
||
365 | const int nV = ::nV; |
||
366 | if (pos.pieceTypeBB(Piece::WQUEEN) && (bMtrlPawns == 0) && (pos.bMtrl() <= std::max(bV,nV))) { |
||
367 | if (!doEval) return 1; |
||
368 | return 200 + pos.wMtrl() - pos.bMtrl() + mateEval(pos.getKingSq(true), pos.getKingSq(false)); |
||
369 | } |
||
370 | if (pos.pieceTypeBB(Piece::BQUEEN) && (wMtrlPawns == 0) && (pos.wMtrl() <= std::max(bV,nV))) { |
||
371 | if (!doEval) return 1; |
||
372 | return -(200 + pos.bMtrl() - pos.wMtrl() + mateEval(pos.getKingSq(false), pos.getKingSq(true))); |
||
373 | } |
||
374 | |||
375 | // Bonus for KQK |
||
376 | if ((pos.bMtrl() == 0) && pos.pieceTypeBB(Piece::WQUEEN)) { |
||
377 | if (!doEval) return 1; |
||
378 | return 100 + pos.wMtrl() - pos.bMtrl() + mateEval(pos.getKingSq(true), pos.getKingSq(false)); |
||
379 | } |
||
380 | if ((pos.wMtrl() == 0) && pos.pieceTypeBB(Piece::BQUEEN)) { |
||
381 | if (!doEval) return 1; |
||
382 | return -(100 + pos.bMtrl() - pos.wMtrl() + mateEval(pos.getKingSq(false), pos.getKingSq(true))); |
||
383 | } |
||
384 | |||
385 | if (pos.pieceTypeBB(Piece::WROOK, Piece::WKNIGHT, Piece::WQUEEN) == 0) { |
||
386 | if (!doEval) return 1; |
||
387 | if ((score > 0) && isBishopPawnDraw<true>(pos)) |
||
388 | return 0; |
||
389 | } |
||
390 | if (pos.pieceTypeBB(Piece::BROOK, Piece::BKNIGHT, Piece::BQUEEN) == 0) { |
||
391 | if (!doEval) return 1; |
||
392 | if ((score < 0) && isBishopPawnDraw<false>(pos)) |
||
393 | return 0; |
||
394 | } |
||
395 | |||
396 | // Give bonus/penalty if advantage is/isn't large enough to win |
||
397 | if ((wMtrlPawns == 0) && (wMtrlNoPawns <= bMtrlNoPawns + bV)) { |
||
398 | if (!doEval) return 1; |
||
399 | if (score > 0) { |
||
400 | if (wMtrlNoPawns < rV) |
||
401 | return -pos.bMtrl() / 50; |
||
402 | else |
||
403 | return score / 8; // Too little excess material, probably draw |
||
404 | } |
||
405 | } |
||
406 | if ((bMtrlPawns == 0) && (bMtrlNoPawns <= wMtrlNoPawns + bV)) { |
||
407 | if (!doEval) return 1; |
||
408 | if (score < 0) { |
||
409 | if (bMtrlNoPawns < rV) |
||
410 | return pos.wMtrl() / 50; |
||
411 | else |
||
412 | return score / 8; // Too little excess material, probably draw |
||
413 | } |
||
414 | } |
||
415 | |||
416 | if ((bMtrlPawns == 0) && (wMtrlNoPawns - bMtrlNoPawns > bV)) { |
||
417 | if (!doEval) return 1; |
||
418 | return score + 300; // Enough excess material, should win |
||
419 | } |
||
420 | if ((wMtrlPawns == 0) && (bMtrlNoPawns - wMtrlNoPawns > bV)) { |
||
421 | if (!doEval) return 1; |
||
422 | return score - 300; // Enough excess material, should win |
||
423 | } |
||
424 | |||
425 | // Give bonus for advantage larger than KRKP, to avoid evaluation discontinuity |
||
426 | if ((pos.bMtrl() == pV) && pos.pieceTypeBB(Piece::WROOK) && (pos.wMtrl() > rV)) { |
||
427 | if (!doEval) return 1; |
||
428 | return score + krkpBonus; |
||
429 | } |
||
430 | if ((pos.wMtrl() == pV) && pos.pieceTypeBB(Piece::BROOK) && (pos.bMtrl() > rV)) { |
||
431 | if (!doEval) return 1; |
||
432 | return score - krkpBonus; |
||
433 | } |
||
434 | |||
435 | // Bonus for KRPKN |
||
436 | if (pos.pieceTypeBB(Piece::WROOK) && pos.pieceTypeBB(Piece::WPAWN) && |
||
437 | !pos.pieceTypeBB(Piece::BBISHOP) && (pos.bMtrl() == nV) && (bMtrlPawns == 0)) { |
||
438 | if (!doEval) return 1; |
||
439 | return score + krpknBonus; |
||
440 | } |
||
441 | if (pos.pieceTypeBB(Piece::BROOK) && pos.pieceTypeBB(Piece::BPAWN) && |
||
442 | !pos.pieceTypeBB(Piece::WBISHOP) && (pos.wMtrl() == nV) && (wMtrlPawns == 0)) { |
||
443 | if (!doEval) return 1; |
||
444 | return score - krpknBonus; |
||
445 | } |
||
446 | |||
447 | // Bonus for KRPKB |
||
448 | int krpkbAdjustment = 0; |
||
449 | if (pos.pieceTypeBB(Piece::WROOK) && pos.pieceTypeBB(Piece::WPAWN) && |
||
450 | !pos.pieceTypeBB(Piece::BKNIGHT) && (pos.bMtrl() == bV) && (bMtrlPawns == 0)) { |
||
451 | if (!doEval) return 1; |
||
452 | score += krpkbBonus; |
||
453 | krpkbAdjustment += krpkbBonus; |
||
454 | } |
||
455 | if (pos.pieceTypeBB(Piece::BROOK) && pos.pieceTypeBB(Piece::BPAWN) && |
||
456 | !pos.pieceTypeBB(Piece::WKNIGHT) && (pos.wMtrl() == bV) && (wMtrlPawns == 0)) { |
||
457 | if (!doEval) return 1; |
||
458 | score -= krpkbBonus; |
||
459 | krpkbAdjustment += krpkbBonus; |
||
460 | } |
||
461 | |||
462 | // Penalty for KRPKB when pawn is on a/h file |
||
463 | if ((wMtrlNoPawns == rV) && (wMtrlPawns <= pV) && pos.pieceTypeBB(Piece::BBISHOP)) { |
||
464 | if (!doEval) return 1; |
||
465 | if (score - krpkbAdjustment > 0) { |
||
466 | U64 pMask = pos.pieceTypeBB(Piece::WPAWN); |
||
467 | U64 bMask = pos.pieceTypeBB(Piece::BBISHOP); |
||
468 | if (((pMask & BitBoard::maskFile[0]) && (bMask & BitBoard::maskDarkSq)) || |
||
469 | ((pMask & BitBoard::maskFile[7]) && (bMask & BitBoard::maskLightSq))) { |
||
470 | score = (score - krpkbAdjustment) * krpkbPenalty / 128; |
||
471 | return score; |
||
472 | } |
||
473 | } |
||
474 | } |
||
475 | if ((bMtrlNoPawns == rV) && (bMtrlPawns <= pV) && pos.pieceTypeBB(Piece::WBISHOP)) { |
||
476 | if (!doEval) return 1; |
||
477 | if (score + krpkbAdjustment < 0) { |
||
478 | U64 pMask = pos.pieceTypeBB(Piece::BPAWN); |
||
479 | U64 bMask = pos.pieceTypeBB(Piece::WBISHOP); |
||
480 | if (((pMask & BitBoard::maskFile[0]) && (bMask & BitBoard::maskLightSq)) || |
||
481 | ((pMask & BitBoard::maskFile[7]) && (bMask & BitBoard::maskDarkSq))) { |
||
482 | score = (score + krpkbAdjustment) * krpkbPenalty / 128; |
||
483 | return score; |
||
484 | } |
||
485 | } |
||
486 | } |
||
487 | |||
488 | auto getPawnAsymmetry = [passedPawns, &pos]() { |
||
489 | int f1 = BitBoard::southFill(pos.pieceTypeBB(Piece::WPAWN)) & 0xff; |
||
490 | int f2 = BitBoard::southFill(pos.pieceTypeBB(Piece::BPAWN)) & 0xff; |
||
491 | int asymmetry = BitBoard::bitCount((f1 & ~f2) | (f2 & ~f1)); |
||
492 | U64 passedPawnsW = passedPawns & pos.pieceTypeBB(Piece::WPAWN); |
||
493 | U64 passedPawnsB = passedPawns & pos.pieceTypeBB(Piece::BPAWN); |
||
494 | asymmetry += BitBoard::bitCount(passedPawnsW) + BitBoard::bitCount(passedPawnsB); |
||
495 | return asymmetry; |
||
496 | }; |
||
497 | |||
498 | // Account for draw factor in rook endgames |
||
499 | if ((BitBoard::bitCount(pos.pieceTypeBB(Piece::WROOK)) == 1) && |
||
500 | (BitBoard::bitCount(pos.pieceTypeBB(Piece::BROOK)) == 1) && |
||
501 | (pos.pieceTypeBB(Piece::WQUEEN, Piece::WBISHOP, Piece::WKNIGHT, |
||
502 | Piece::BQUEEN, Piece::BBISHOP, Piece::BKNIGHT) == 0) && |
||
503 | (BitBoard::bitCount(pos.pieceTypeBB(Piece::WPAWN, Piece::BPAWN)) > 1)) { |
||
504 | if (!doEval) return 1; |
||
505 | int asymmetry = getPawnAsymmetry(); |
||
506 | score = score * rookEGDrawFactor[std::min(asymmetry, 6)] / 128; |
||
507 | return score; |
||
508 | } |
||
509 | |||
510 | // Correction for draw factor in RvsB endgames |
||
511 | if ((BitBoard::bitCount(pos.pieceTypeBB(Piece::WROOK)) == 1) && |
||
512 | (BitBoard::bitCount(pos.pieceTypeBB(Piece::BBISHOP)) == 1) && |
||
513 | (pos.pieceTypeBB(Piece::WQUEEN, Piece::WBISHOP, Piece::WKNIGHT, |
||
514 | Piece::BQUEEN, Piece::BROOK, Piece::BKNIGHT) == 0) && |
||
515 | (wMtrlPawns - bMtrlPawns == -pV)) { |
||
516 | if (!doEval) return 1; |
||
517 | int asymmetry = getPawnAsymmetry(); |
||
518 | score = score * RvsBPDrawFactor[std::min(asymmetry, 6)] / 128; |
||
519 | return score; |
||
520 | } |
||
521 | // Correction for draw factor in RvsB endgames |
||
522 | if ((BitBoard::bitCount(pos.pieceTypeBB(Piece::BROOK)) == 1) && |
||
523 | (BitBoard::bitCount(pos.pieceTypeBB(Piece::WBISHOP)) == 1) && |
||
524 | (pos.pieceTypeBB(Piece::BQUEEN, Piece::BBISHOP, Piece::BKNIGHT, |
||
525 | Piece::WQUEEN, Piece::WROOK, Piece::WKNIGHT) == 0) && |
||
526 | (wMtrlPawns - bMtrlPawns == pV)) { |
||
527 | if (!doEval) return 1; |
||
528 | int asymmetry = getPawnAsymmetry(); |
||
529 | score = score * RvsBPDrawFactor[std::min(asymmetry, 6)] / 128; |
||
530 | return score; |
||
531 | } |
||
532 | |||
533 | if (!doEval) return 0; |
||
534 | return score; |
||
535 | } |
||
536 | |||
537 | int |
||
538 | EndGameEval::mateEval(int k1, int k2) { |
||
539 | static const int loseKingTable[64] = { |
||
540 | 0, 4, 8, 12, 12, 8, 4, 0, |
||
541 | 4, 8, 12, 16, 16, 12, 8, 4, |
||
542 | 8, 12, 16, 20, 20, 16, 12, 8, |
||
543 | 12, 16, 20, 24, 24, 20, 16, 12, |
||
544 | 12, 16, 20, 24, 24, 20, 16, 12, |
||
545 | 8, 12, 16, 20, 20, 16, 12, 8, |
||
546 | 4, 8, 12, 16, 16, 12, 8, 4, |
||
547 | 0, 4, 8, 12, 12, 8, 4, 0 |
||
548 | }; |
||
549 | return winKingTable[k1] - loseKingTable[k2]; |
||
550 | } |
||
551 | |||
552 | template <bool whiteBishop> |
||
553 | bool |
||
554 | EndGameEval::isBishopPawnDraw(const Position& pos) { |
||
555 | const Piece::Type bishop = whiteBishop ? Piece::WBISHOP : Piece::BBISHOP; |
||
556 | const bool darkBishop = (pos.pieceTypeBB(bishop) & BitBoard::maskDarkSq) != 0; |
||
557 | const bool lightBishop = (pos.pieceTypeBB(bishop) & BitBoard::maskLightSq) != 0; |
||
558 | if (darkBishop && lightBishop) |
||
559 | return false; // No draw against proper bishop pair |
||
560 | |||
561 | const Piece::Type pawn = whiteBishop ? Piece::WPAWN : Piece::BPAWN; |
||
562 | if (pos.pieceTypeBB(pawn) == 0) |
||
563 | return true; // Only bishops on same color can not win |
||
564 | |||
565 | // Check for rook pawn + wrong color bishop |
||
566 | if (whiteBishop) { |
||
567 | if (((pos.pieceTypeBB(pawn) & BitBoard::maskBToHFiles) == 0) && |
||
568 | !lightBishop && |
||
569 | ((pos.pieceTypeBB(Piece::BKING) & BitBoard::sqMask(A8,B8,A7,B7)) != 0)) { |
||
570 | return true; |
||
571 | } else |
||
572 | if (((pos.pieceTypeBB(pawn) & BitBoard::maskAToGFiles) == 0) && |
||
573 | !darkBishop && |
||
574 | ((pos.pieceTypeBB(Piece::BKING) & BitBoard::sqMask(G8,H8,G7,H7)) != 0)) { |
||
575 | return true; |
||
576 | } |
||
577 | } else { |
||
578 | if (((pos.pieceTypeBB(pawn) & BitBoard::maskBToHFiles) == 0) && |
||
579 | !darkBishop && |
||
580 | ((pos.pieceTypeBB(Piece::WKING) & BitBoard::sqMask(A1,B1,A2,B2)) != 0)) { |
||
581 | return true; |
||
582 | } else |
||
583 | if (((pos.pieceTypeBB(pawn) & BitBoard::maskAToGFiles) == 0) && |
||
584 | !lightBishop && |
||
585 | ((pos.pieceTypeBB(Piece::WKING) & BitBoard::sqMask(G1,H1,G2,H2)) != 0)) { |
||
586 | return true; |
||
587 | } |
||
588 | } |
||
589 | |||
590 | // Check for fortress containing WPb6, BPb7, white bishop on dark square |
||
591 | const Piece::Type king = whiteBishop ? Piece::WKING : Piece::BKING; |
||
592 | const Piece::Type oPawn = whiteBishop ? Piece::BPAWN : Piece::WPAWN; |
||
593 | const Piece::Type oKnight = whiteBishop ? Piece::BKNIGHT : Piece::WKNIGHT; |
||
594 | const int b7 = whiteBishop ? (darkBishop ? B7 : G7) : (lightBishop ? B2 : G2); |
||
595 | const int b6 = whiteBishop ? (darkBishop ? B6 : G6) : (lightBishop ? B3 : G3); |
||
596 | const int c7 = whiteBishop ? (darkBishop ? C7 : F7) : (lightBishop ? C2 : F2); |
||
597 | const int a8 = whiteBishop ? (darkBishop ? A8 : H8) : (lightBishop ? A1 : H1); |
||
598 | const int b8 = whiteBishop ? (darkBishop ? B8 : G8) : (lightBishop ? B1 : G1); |
||
599 | const int c8 = whiteBishop ? (darkBishop ? C8 : F8) : (lightBishop ? C1 : F1); |
||
600 | const int d8 = whiteBishop ? (darkBishop ? D8 : E8) : (lightBishop ? D1 : E1); |
||
601 | const int d7 = whiteBishop ? (darkBishop ? D7 : E7) : (lightBishop ? D2 : E2); |
||
602 | const U64 bFile = (whiteBishop == darkBishop) ? 0x0202020202020202ULL : 0x4040404040404040ULL; |
||
603 | const U64 acFile = (whiteBishop == darkBishop) ? 0x0505050505050505ULL : 0xA0A0A0A0A0A0A0A0ULL; |
||
604 | const U64 corner = whiteBishop ? (darkBishop ? BitBoard::sqMask(A8,B8,A7) : BitBoard::sqMask(G8,H8,H7)) |
||
605 | : (lightBishop ? BitBoard::sqMask(A1,B1,A2) : BitBoard::sqMask(G1,H1,H2)); |
||
606 | |||
607 | if ((pos.getPiece(b7) == oPawn) && (pos.getPiece(b6) == pawn) && |
||
608 | (pos.getPiece(a8) != oKnight) && ((pos.pieceTypeBB(king) & corner) == 0) && |
||
609 | (BitBoard::bitCount(pos.pieceTypeBB(oPawn) & acFile) <= 1)) { |
||
610 | if (pos.getPiece(c7) == pawn) { |
||
611 | if (BitBoard::bitCount(pos.pieceTypeBB(pawn) & ~bFile) == 1) { |
||
612 | int oKingSq = pos.getKingSq(!whiteBishop); |
||
613 | if ((oKingSq == c8) || (oKingSq == d7)) |
||
614 | return true; |
||
615 | } |
||
616 | } else { |
||
617 | int oKingSq = pos.getKingSq(!whiteBishop); |
||
618 | if ((pos.pieceTypeBB(pawn) & ~bFile) == 0) { |
||
619 | if ((oKingSq == a8) || (oKingSq == b8) || (oKingSq == c8) || |
||
620 | (oKingSq == d8) || (oKingSq == d7)) |
||
621 | return true; |
||
622 | } else if (pos.isWhiteMove() != whiteBishop) { // Test if stale-mate |
||
623 | int oMtrl = whiteBishop ? pos.bMtrl() : pos.wMtrl(); |
||
624 | U64 bShift = whiteBishop ? (pos.pieceTypeBB(bishop) << 8) : (pos.pieceTypeBB(bishop) >> 8); |
||
625 | U64 kShift = whiteBishop ? (pos.pieceTypeBB(king) << 8) : (pos.pieceTypeBB(king) >> 8); |
||
626 | const U64 md6_h2 = whiteBishop ? |
||
627 | (darkBishop ? BitBoard::sqMask(D6,E5,F4,G3,H2) : BitBoard::sqMask(E6,D5,C4,B3,A2)) : |
||
628 | (lightBishop ? BitBoard::sqMask(D3,E4,F5,G6,H7) : BitBoard::sqMask(E3,D4,C5,B6,A7)); |
||
629 | if ((oMtrl == pV) || |
||
630 | ((oMtrl == 2*pV) && ((bShift & pos.pieceTypeBB(oPawn)) || |
||
631 | ((kShift & pos.pieceTypeBB(oPawn)) && |
||
632 | ((pos.pieceTypeBB(oPawn) & md6_h2) == 0))))) { |
||
633 | const U64 mc7c8 = whiteBishop ? |
||
634 | (darkBishop ? BitBoard::sqMask(C7,C8) : BitBoard::sqMask(F7,F8)) : |
||
635 | (lightBishop ? BitBoard::sqMask(C2,C1) : BitBoard::sqMask(F2,F1)); |
||
636 | const U64 md6e6e7e8 = whiteBishop ? |
||
637 | (darkBishop ? BitBoard::sqMask(D6,E6,E7,E8) : BitBoard::sqMask(E6,D6,D7,D8)) : |
||
638 | (lightBishop ? BitBoard::sqMask(D3,E3,E2,E1) : BitBoard::sqMask(E3,D3,D2,D1)); |
||
639 | const U64 me7e8 = whiteBishop ? |
||
640 | (darkBishop ? BitBoard::sqMask(E7,E8) : BitBoard::sqMask(D7,D8)) : |
||
641 | (lightBishop ? BitBoard::sqMask(E2,E1) : BitBoard::sqMask(D2,D1)); |
||
642 | if (oKingSq == a8) { |
||
643 | if ((pos.pieceTypeBB(king) & mc7c8) || |
||
644 | (pos.pieceTypeBB(bishop) & (md6_h2 | (1ULL << c7)))) |
||
645 | return true; |
||
646 | } else if (oKingSq == c8) { |
||
647 | if (pos.getPiece(c7) == bishop) { |
||
648 | if (pos.pieceTypeBB(king) & md6e6e7e8) |
||
649 | return true; |
||
650 | } else { |
||
651 | if ((pos.pieceTypeBB(bishop) & md6_h2) && (pos.pieceTypeBB(king) & me7e8)) |
||
652 | return true; |
||
653 | } |
||
654 | } |
||
655 | } |
||
656 | } |
||
657 | } |
||
658 | } |
||
659 | |||
660 | // Check for fortress when all pawns are on the B file and there is no bishop |
||
661 | if (whiteBishop) { |
||
662 | if (pos.pieceTypeBB(Piece::WBISHOP) == 0) { |
||
663 | if ((pos.pieceTypeBB(Piece::WPAWN,Piece::BPAWN) & ~BitBoard::maskFileB) == 0) { |
||
664 | if ((pos.getPiece(B7) == Piece::BPAWN) && |
||
665 | (pos.pieceTypeBB(Piece::BKING) & BitBoard::sqMask(A7,A8,B8))) |
||
666 | return true; |
||
667 | } |
||
668 | if ((pos.pieceTypeBB(Piece::WPAWN,Piece::BPAWN) & ~BitBoard::maskFileG) == 0) { |
||
669 | if ((pos.getPiece(G7) == Piece::BPAWN) && |
||
670 | (pos.pieceTypeBB(Piece::BKING) & BitBoard::sqMask(H7,H8,G8))) |
||
671 | return true; |
||
672 | } |
||
673 | } |
||
674 | } else { |
||
675 | if (pos.pieceTypeBB(Piece::BBISHOP) == 0) { |
||
676 | if ((pos.pieceTypeBB(Piece::WPAWN,Piece::BPAWN) & ~BitBoard::maskFileB) == 0) { |
||
677 | if ((pos.getPiece(B2) == Piece::WPAWN) && |
||
678 | (pos.pieceTypeBB(Piece::WKING) & BitBoard::sqMask(A2,A1,B1))) |
||
679 | return true; |
||
680 | } |
||
681 | if ((pos.pieceTypeBB(Piece::WPAWN,Piece::BPAWN) & ~BitBoard::maskFileG) == 0) { |
||
682 | if ((pos.getPiece(G2) == Piece::WPAWN) && |
||
683 | (pos.pieceTypeBB(Piece::WKING) & BitBoard::sqMask(H2,H1,G1))) |
||
684 | return true; |
||
685 | } |
||
686 | } |
||
687 | } |
||
688 | |||
689 | return false; |
||
690 | } |
||
691 | |||
692 | int |
||
693 | EndGameEval::kqkpEval(int wKing, int wQueen, int bKing, int bPawn, bool whiteMove, int score) { |
||
694 | bool canWin = false; |
||
695 | if (((1ULL << bKing) & 0xFFFF) == 0) { |
||
696 | canWin = true; // King doesn't support pawn |
||
697 | } else if (std::abs(Position::getX(bPawn) - Position::getX(bKing)) > 2) { |
||
698 | canWin = true; // King doesn't support pawn |
||
699 | } else { |
||
700 | switch (bPawn) { |
||
701 | case A2: |
||
702 | canWin = ((1ULL << wKing) & 0x0F1F1F1F1FULL) != 0; |
||
703 | if (canWin && (bKing == A1) && (Position::getX(wQueen) == 1) && !whiteMove) |
||
704 | canWin = false; // Stale-mate |
||
705 | break; |
||
706 | case C2: |
||
707 | canWin = ((1ULL << wKing) & 0x071F1F1FULL) != 0; |
||
708 | break; |
||
709 | case F2: |
||
710 | canWin = ((1ULL << wKing) & 0xE0F8F8F8ULL) != 0; |
||
711 | break; |
||
712 | case H2: |
||
713 | canWin = ((1ULL << wKing) & 0xF0F8F8F8F8ULL) != 0; |
||
714 | if (canWin && (bKing == H1) && (Position::getX(wQueen) == 6) && !whiteMove) |
||
715 | canWin = false; // Stale-mate |
||
716 | break; |
||
717 | default: |
||
718 | canWin = true; |
||
719 | break; |
||
720 | } |
||
721 | } |
||
722 | |||
723 | const int dist = BitBoard::getKingDistance(wKing, bPawn); |
||
724 | score = score - 20 * (dist - 4); |
||
725 | if (!canWin) |
||
726 | score /= 50; |
||
727 | return score; |
||
728 | } |
||
729 | |||
730 | int |
||
731 | EndGameEval::kqkrpEval(int wKing, int wQueen, int bKing, int bRook, int bPawn, bool whiteMove, int score) { |
||
732 | if (!(BitBoard::bPawnAttacks[bPawn] & (1ULL << bRook))) |
||
733 | return score; // Rook not protected by pawn, no fortress |
||
734 | if ((1ULL << bPawn) & (BitBoard::maskFileE | BitBoard::maskFileF | |
||
735 | BitBoard::maskFileG | BitBoard::maskFileH)) { // Mirror X |
||
736 | wKing ^= 7; |
||
737 | wQueen ^= 7; |
||
738 | bKing ^= 7; |
||
739 | bRook ^= 7; |
||
740 | bPawn ^= 7; |
||
741 | } |
||
742 | bool drawish = false; |
||
743 | switch (bPawn) { |
||
744 | case A6: |
||
745 | drawish = ((1ULL << bKing) & BitBoard::sqMask(A8,B8,A7,B7)) && |
||
746 | (Position::getX(wKing) >= 2) && |
||
747 | (Position::getY(wKing) <= 3); |
||
748 | break; |
||
749 | case A2: |
||
750 | drawish = (((1ULL << bKing) & BitBoard::sqMask(A4,B4,A3,B3)) != 0); // Pierre-Marie Baty -- boolean conversion fix |
||
751 | break; |
||
752 | case B7: |
||
753 | drawish = ((1ULL << bKing) & BitBoard::sqMask(A8,B8,C8,A7,C7)) && |
||
754 | (Position::getY(wKing) <= 4); |
||
755 | break; |
||
756 | case B6: |
||
757 | if (bRook == C5) { |
||
758 | drawish = ((1ULL << bKing) & BitBoard::sqMask(A7,B7)) || |
||
759 | (((1ULL << bKing) & BitBoard::sqMask(A8,B8)) && |
||
760 | ((Position::getX(wKing) >= 3) || (Position::getY(wKing) <= 3)) && |
||
761 | (((1ULL << wQueen) & BitBoard::maskRow7) || |
||
762 | (!whiteMove && (wQueen != A6)) || |
||
763 | (whiteMove && !((1ULL << wQueen) & BitBoard::sqMask(A6,A5,A4,A3,A2,A1,B5,B4,B3,B2,B1,C4,D3,E2,F1))))); |
||
764 | } |
||
765 | break; |
||
766 | case B5: |
||
767 | drawish = ((1ULL << bKing) & BitBoard::sqMask(A6,B6,C6,A5)) && |
||
768 | (Position::getY(wKing) <= 2); |
||
769 | break; |
||
770 | case B4: |
||
771 | drawish = ((1ULL << bKing) & BitBoard::sqMask(A6,B6,C6,A5,B5,C5)) && |
||
772 | ((Position::getY(wKing) <= 2) || (Position::getX(wKing) >= 4)); |
||
773 | break; |
||
774 | case B3: |
||
775 | drawish = (((1ULL << bKing) & BitBoard::sqMask(A4,B4,C4,A3)) && |
||
776 | ((1ULL << wKing) & BitBoard::maskRow1Row8)) || |
||
777 | (((1ULL << bKing) & BitBoard::sqMask(A5,B5)) && |
||
778 | ((1ULL << wQueen) & BitBoard::maskRow4) && |
||
779 | ((1ULL << wKing) & BitBoard::maskRow1)); |
||
780 | break; |
||
781 | case B2: |
||
782 | drawish = ((1ULL << bKing) & BitBoard::sqMask(A4,B4,C4,A3,B3,C3,A2,C2)) && |
||
783 | ((1ULL << wKing) & BitBoard::sqMask(A4,B4,C4,A3,B3,C3,A2,C2)) == 0; |
||
784 | break; |
||
785 | case C7: |
||
786 | drawish = ((1ULL << bKing) & BitBoard::sqMask(B8,C8,D8,B7,D7)) && |
||
787 | (Position::getY(wKing) <= 4); |
||
788 | break; |
||
789 | case C3: |
||
790 | drawish = (((1ULL << bKing) & BitBoard::sqMask(B4,C4,D4)) && |
||
791 | ((1ULL << wKing) & BitBoard::maskRow1Row8)) || |
||
792 | (((1ULL << bKing) & BitBoard::sqMask(B5,C5)) && |
||
793 | (((1ULL << wQueen) & BitBoard::maskRow4) || !whiteMove) && |
||
794 | ((1ULL << wKing) & BitBoard::maskRow1)) || |
||
795 | ((((bKing == B3) && (wQueen != B5)) || ((bKing == D3) && (wQueen != D5))) && |
||
796 | ((1ULL << wKing) & BitBoard::maskRow1)); |
||
797 | |||
798 | break; |
||
799 | case C2: |
||
800 | drawish = ((1ULL << bKing) & BitBoard::sqMask(B3,C3,D3,B2,D2)) && |
||
801 | ((Position::getX(wKing) == 0) || (Position::getX(wKing) >= 4)); |
||
802 | break; |
||
803 | case D7: |
||
804 | drawish = ((1ULL << bKing) & BitBoard::sqMask(C8,D8,E8,C7,E7)) && |
||
805 | (Position::getY(wKing) <= 4); |
||
806 | break; |
||
807 | case D3: |
||
808 | drawish = (((1ULL << bKing) & BitBoard::sqMask(C4,D4,E4)) && |
||
809 | ((1ULL << wKing) & BitBoard::maskRow1Row8)) || |
||
810 | (((1ULL << bKing) & BitBoard::sqMask(C5,D5,E5)) && |
||
811 | (((1ULL << wQueen) & BitBoard::maskRow4) || !whiteMove) && |
||
812 | ((1ULL << wKing) & BitBoard::maskRow1)) || |
||
813 | ((((bKing == C3) && (wQueen != C5)) || ((bKing == E3) && (wQueen != E5))) && |
||
814 | ((1ULL << wKing) & BitBoard::maskRow1)); |
||
815 | break; |
||
816 | case D2: |
||
817 | drawish = ((1ULL << bKing) & BitBoard::sqMask(C4,D4,E4,C3,D3,E3,C2,E2)) && |
||
818 | ((1ULL << wKing) & BitBoard::sqMask(C4,D4,E4,C3,D3,E3,C2,E2)) == 0; |
||
819 | break; |
||
820 | default: |
||
821 | drawish = false; |
||
822 | break; |
||
823 | } |
||
824 | return drawish ? score / 16 : score; |
||
825 | } |
||
826 | |||
827 | int |
||
828 | EndGameEval::kpkEval(int wKing, int bKing, int wPawn, bool whiteMove) { |
||
829 | if (Position::getX(wKing) >= 4) { // Mirror X |
||
830 | wKing ^= 7; |
||
831 | bKing ^= 7; |
||
832 | wPawn ^= 7; |
||
833 | } |
||
834 | int index = whiteMove ? 0 : 1; |
||
835 | index = index * 32 + Position::getY(wKing)*4+Position::getX(wKing); |
||
836 | index = index * 64 + bKing; |
||
837 | index = index * 48 + wPawn - 8; |
||
838 | |||
839 | int bytePos = index / 8; |
||
840 | int bitPos = index % 8; |
||
841 | bool draw = (((int)kpkTable[bytePos]) & (1 << bitPos)) == 0; |
||
842 | if (draw) |
||
843 | return 0; |
||
844 | return qV - pV / 4 * (7-Position::getY(wPawn)); |
||
845 | } |
||
846 | |||
847 | bool |
||
848 | EndGameEval::kpkpEval(int wKing, int bKing, int wPawn, int bPawn, int& score) { |
||
849 | const U64 wKingMask = 1ULL << wKing; |
||
850 | const U64 bKingMask = 1ULL << bKing; |
||
851 | if (wPawn == B6 && bPawn == B7) { |
||
852 | if ((bKingMask & BitBoard::sqMask(A8,B8,C8,D8,D7)) && |
||
853 | ((wKingMask & BitBoard::sqMask(A8,B8,A7)) == 0)) { |
||
854 | score = 0; |
||
855 | return true; |
||
856 | } |
||
857 | } else if (wPawn == G6 && bPawn == G7) { |
||
858 | if ((bKingMask & BitBoard::sqMask(E8,F8,G8,H8,E7)) && |
||
859 | ((wKingMask & BitBoard::sqMask(G8,H8,H7)) == 0)) { |
||
860 | score = 0; |
||
861 | return true; |
||
862 | } |
||
863 | } else if (wPawn == B2 && bPawn == B3) { |
||
864 | if ((wKingMask & BitBoard::sqMask(A1,B1,C1,D1,D2)) && |
||
865 | ((bKingMask & BitBoard::sqMask(A1,B1,A2)) == 0)) { |
||
866 | score = 0; |
||
867 | return true; |
||
868 | } |
||
869 | } else if (wPawn == G2 && bPawn == G3) { |
||
870 | if ((wKingMask & BitBoard::sqMask(E1,F1,G1,H1,E2)) && |
||
871 | ((bKingMask & BitBoard::sqMask(G1,H1,H2)) == 0)) { |
||
872 | score = 0; |
||
873 | return true; |
||
874 | } |
||
875 | } |
||
876 | return false; |
||
877 | } |
||
878 | |||
879 | int |
||
880 | EndGameEval::krkpEval(int wKing, int bKing, int bPawn, bool whiteMove, int score) { |
||
881 | if (Position::getX(bKing) >= 4) { // Mirror X |
||
882 | wKing ^= 7; |
||
883 | bKing ^= 7; |
||
884 | bPawn ^= 7; |
||
885 | } |
||
886 | int index = whiteMove ? 0 : 1; |
||
887 | index = index * 32 + Position::getY(bKing)*4+Position::getX(bKing); |
||
888 | index = index * 48 + bPawn - 8; |
||
889 | index = index * 8 + Position::getY(wKing); |
||
890 | U8 mask = krkpTable[index]; |
||
891 | bool canWin = (mask & (1 << Position::getX(wKing))) != 0; |
||
892 | |||
893 | score = score + Position::getY(bPawn) * pV / 4; |
||
894 | if (!canWin) |
||
895 | score /= 50; |
||
896 | else |
||
897 | score += krkpBonus; |
||
898 | return score; |
||
899 | } |
||
900 | |||
901 | int |
||
902 | EndGameEval::krpkrEval(int wKing, int bKing, int wPawn, int wRook, int bRook, bool whiteMove) { |
||
903 | if (Position::getX(wPawn) >= 4) { // Mirror X |
||
904 | wKing ^= 7; |
||
905 | bKing ^= 7; |
||
906 | wPawn ^= 7; |
||
907 | wRook ^= 7; |
||
908 | bRook ^= 7; |
||
909 | } |
||
910 | int index = whiteMove ? 0 : 1; |
||
911 | index = index * 24 + (Position::getY(wPawn)-1)*4+Position::getX(wPawn); |
||
912 | index = index * 64 + wKing; |
||
913 | const U64 kMask = krpkrTable[index]; |
||
914 | const bool canWin = (kMask & (1ULL << bKing)) != 0; |
||
915 | U64 kingNeighbors = BitBoard::kingAttacks[bKing]; |
||
916 | const U64 occupied = (1ULL<<wKing) | (1ULL<<bKing) | (1ULL<<wPawn) | (1ULL<<bRook); |
||
917 | const U64 rAtk = BitBoard::rookAttacks(wRook, occupied); |
||
918 | kingNeighbors &= ~(BitBoard::kingAttacks[wKing] | BitBoard::wPawnAttacks[wPawn] | rAtk); |
||
919 | bool close; |
||
920 | if (canWin) { |
||
921 | close = (kMask & kingNeighbors) != kingNeighbors; |
||
922 | } else { |
||
923 | close = (kMask & kingNeighbors) != 0; |
||
924 | } |
||
925 | int score = pV + Position::getY(wPawn) * pV / 4; |
||
926 | if (canWin) { |
||
927 | if (!close) |
||
928 | score += pV; |
||
929 | } else { |
||
930 | if (close) |
||
931 | score /= 2; |
||
932 | else |
||
933 | score /= 4; |
||
934 | } |
||
935 | return score; |
||
936 | } |
||
937 | |||
938 | int |
||
939 | EndGameEval::krpkrpEval(int wKing, int bKing, int wPawn, int wRook, int bRook, int bPawn, bool whiteMove, int score) { |
||
940 | int hiScore = krpkrEval(wKing, bKing, wPawn, wRook, bRook, whiteMove); |
||
941 | if (score > hiScore * 14 / 16) |
||
942 | return hiScore * 14 / 16; |
||
943 | int loScore = -krpkrEval(63-bKing, 63-wKing, 63-bPawn, 63-bRook, 63-wRook, !whiteMove); |
||
944 | if (score < loScore * 14 / 16) |
||
945 | return loScore * 14 / 16; |
||
946 | return score; |
||
947 | } |
||
948 | |||
949 | int |
||
950 | EndGameEval::kbnkEval(int wKing, int bKing, bool darkBishop) { |
||
951 | int score = 600; |
||
952 | if (darkBishop) { // Mirror X |
||
953 | wKing ^= 7; |
||
954 | bKing ^= 7; |
||
955 | } |
||
956 | static const int bkTable[64] = { 17, 15, 12, 9, 7, 4, 2, 0, |
||
957 | 15, 20, 17, 15, 12, 9, 4, 2, |
||
958 | 12, 17, 22, 20, 17, 15, 9, 4, |
||
959 | 9, 15, 20, 25, 22, 17, 12, 7, |
||
960 | 7, 12, 17, 22, 25, 20, 15, 9, |
||
961 | 4, 9, 15, 17, 20, 22, 17, 12, |
||
962 | 2, 4, 9, 12, 15, 17, 20, 15, |
||
963 | 0, 2, 4, 7, 9, 12, 15, 17 }; |
||
964 | |||
965 | score += winKingTable[wKing] - bkTable[bKing]; |
||
966 | score -= std::min(0, BitBoard::getTaxiDistance(wKing, bKing) - 3); |
||
967 | return score; |
||
968 | } |
||
969 | |||
970 | int |
||
971 | EndGameEval::kbpkbEval(int wKing, int wBish, int wPawn, int bKing, int bBish, int score) { |
||
972 | U64 wPawnMask = 1ULL << wPawn; |
||
973 | U64 pawnPath = BitBoard::northFill(wPawnMask); |
||
974 | U64 bKingMask = 1ULL << bKing; |
||
975 | U64 wBishMask = 1ULL << wBish; |
||
976 | U64 wBishControl = (wBishMask & BitBoard::maskDarkSq) ? BitBoard::maskDarkSq : BitBoard::maskLightSq; |
||
977 | if ((bKingMask & pawnPath) && ((bKingMask & wBishControl) == 0)) |
||
978 | return 0; |
||
979 | |||
980 | U64 bBishMask = 1ULL << bBish; |
||
981 | if (((wBishMask & BitBoard::maskDarkSq) == 0) != ((bBishMask & BitBoard::maskDarkSq) == 0)) { // Different color bishops |
||
982 | if (((bBishMask | BitBoard::bishopAttacks(bBish, bKingMask)) & pawnPath & ~wPawnMask) != 0) |
||
983 | if (!(wPawn == A6 && bBish == B8) && !(wPawn == H6 && bBish == G8)) |
||
984 | return 0; |
||
985 | } |
||
986 | |||
987 | if (bKingMask & BitBoard::wPawnBlockerMask[wPawn]) |
||
988 | return score / 4; |
||
989 | return score; |
||
990 | } |
||
991 | |||
992 | int |
||
993 | EndGameEval::kbpknEval(int wKing, int wBish, int wPawn, int bKing, int bKnight, int score) { |
||
994 | U64 wPawnMask = 1ULL << wPawn; |
||
995 | U64 pawnPath = BitBoard::northFill(wPawnMask); |
||
996 | U64 bKingMask = 1ULL << bKing; |
||
997 | U64 wBishMask = 1ULL << wBish; |
||
998 | U64 wBishControl = (wBishMask & BitBoard::maskDarkSq) ? BitBoard::maskDarkSq : BitBoard::maskLightSq; |
||
999 | |||
1000 | U64 edges = 0xff818181818181ffULL; |
||
1001 | U64 bKnightMask = 1ULL << bKnight; |
||
1002 | if ((bKnightMask & edges & ~wBishControl) != 0) // Knight on edge square where it can be trapped |
||
1003 | return score; |
||
1004 | |||
1005 | if ((bKingMask & pawnPath) && ((bKingMask & wBishControl) == 0)) |
||
1006 | return 0; |
||
1007 | |||
1008 | if (bKingMask & BitBoard::wPawnBlockerMask[wPawn]) |
||
1009 | return score / 4; |
||
1010 | return score; |
||
1011 | } |
||
1012 | |||
1013 | int |
||
1014 | EndGameEval::knpkbEval(int wKing, int wKnight, int wPawn, int bKing, int bBish, int score, bool wtm) { |
||
1015 | U64 wPawnMask = 1ULL << wPawn; |
||
1016 | U64 bBishMask = 1ULL << bBish; |
||
1017 | U64 bBishControl = (bBishMask & BitBoard::maskDarkSq) ? BitBoard::maskDarkSq : BitBoard::maskLightSq; |
||
1018 | |||
1019 | U64 p = wPawnMask; |
||
1020 | if (bBishControl & wPawnMask) { |
||
1021 | U64 bKingMask = 1ULL << bKing; |
||
1022 | U64 wKnightMask = 1ULL << wKnight; |
||
1023 | if (!wtm && (BitBoard::bishopAttacks(bBish, bKingMask | wKnightMask) & wPawnMask)) |
||
1024 | return 0; |
||
1025 | p <<= 8; |
||
1026 | } |
||
1027 | U64 pawnDrawishMask = 0x183c7e7e7e7eULL; |
||
1028 | if (p & pawnDrawishMask) |
||
1029 | return score / 32; |
||
1030 | |||
1031 | return score; |
||
1032 | } |
||
1033 | |||
1034 | int |
||
1035 | EndGameEval::knpkEval(int wKing, int wKnight, int wPawn, int bKing, int score, bool wtm) { |
||
1036 | if (Position::getX(wPawn) >= 4) { // Mirror X |
||
1037 | wKing ^= 7; |
||
1038 | wKnight ^= 7; |
||
1039 | wPawn ^= 7; |
||
1040 | bKing ^= 7; |
||
1041 | } |
||
1042 | if (wPawn == A7) { |
||
1043 | if (bKing == A8 || bKing == B7) // Fortress |
||
1044 | return 0; |
||
1045 | if (wKing == A8 && (bKing == C7 || bKing == C8)) { |
||
1046 | bool knightDark = Position::darkSquare(Position::getX(wKnight), Position::getY(wKnight)); |
||
1047 | bool kingDark = Position::darkSquare(Position::getX(bKing), Position::getY(bKing)); |
||
1048 | if (wtm == (knightDark == kingDark)) // King trapped |
||
1049 | return 0; |
||
1050 | } |
||
1051 | } |
||
1052 | return score; |
||
1053 | } |