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 | } |