Rev 108 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed
| Rev | Author | Line No. | Line |
|---|---|---|---|
| 33 | pmbaty | 1 | #include "chess.h" |
| 2 | #include "data.h" |
||
| 3 | #include "epdglue.h" |
||
| 154 | pmbaty | 4 | #if defined(SYZYGY) |
| 5 | # include "tbprobe.h" |
||
| 6 | #endif |
||
| 7 | /* last modified 07/11/16 */ |
||
| 33 | pmbaty | 8 | /* |
| 9 | ******************************************************************************* |
||
| 10 | * * |
||
| 11 | * RootMoveList() is used to set up the ply one move list. It is a more * |
||
| 12 | * accurate ordering of the move list than that done for plies deeper than * |
||
| 13 | * one. Briefly, Quiesce() is used to obtain the positional score plus the * |
||
| 14 | * expected gain/loss for pieces that can be captured. * |
||
| 15 | * * |
||
| 16 | ******************************************************************************* |
||
| 17 | */ |
||
| 18 | void RootMoveList(int wtm) { |
||
| 19 | TREE *const tree = block[0]; |
||
| 154 | pmbaty | 20 | ROOT_MOVE rtemp; |
| 108 | pmbaty | 21 | unsigned mvp, *lastm, rmoves[256]; |
| 154 | pmbaty | 22 | int value, done; |
| 23 | #if defined(SYZYGY) |
||
| 24 | int tb_result, tb_root = -9; |
||
| 33 | pmbaty | 25 | #endif |
| 26 | |||
| 27 | /* |
||
| 28 | ************************************************************ |
||
| 29 | * * |
||
| 30 | * If the position at the root is a draw, based on EGTB * |
||
| 31 | * results, we are going to behave differently. We will * |
||
| 32 | * extract the root moves that are draws, and toss the * |
||
| 33 | * losers out. Then, we will do a normal search on the * |
||
| 34 | * moves that draw to try and chose the drawing move that * |
||
| 35 | * gives our opponent the best chance to make an error. * |
||
| 36 | * This search will be done sans EGTB probes since we al- * |
||
| 37 | * ready know this is a draw at the root. We simply find * |
||
| 38 | * the best move (based on search/eval) that preserves the * |
||
| 39 | * draw. * |
||
| 40 | * * |
||
| 41 | ************************************************************ |
||
| 42 | */ |
||
| 154 | pmbaty | 43 | #if defined(SYZYGY) |
| 33 | pmbaty | 44 | EGTB_draw = 0; |
| 154 | pmbaty | 45 | if (swindle_mode) { |
| 46 | if (EGTBlimit && TotalAllPieces <= EGTBlimit && |
||
| 47 | Castle(1, white) + Castle(1, black) == 0) { |
||
| 48 | tb_result = |
||
| 49 | tb_probe_root(Occupied(white), Occupied(black), |
||
| 50 | Kings(white) | Kings(black), Queens(white) | Queens(black), |
||
| 51 | Rooks(white) | Rooks(black), Bishops(white) | Bishops(black), |
||
| 52 | Knights(white) | Knights(black), Pawns(white) | Pawns(black), |
||
| 53 | Reversible(1), 0, EnPassant(1), wtm, NULL); |
||
| 54 | if (tb_result != TB_RESULT_FAILED) { |
||
| 55 | tb_root = TB_GET_WDL(tb_result); |
||
| 56 | if ((tb_root == TB_DRAW && MaterialSTM(wtm) > 0) || |
||
| 57 | (tb_root == TB_CURSED_WIN)) |
||
| 58 | EGTB_draw = 1; |
||
| 59 | } |
||
| 60 | } |
||
| 33 | pmbaty | 61 | } |
| 62 | #endif |
||
| 63 | /* |
||
| 64 | ************************************************************ |
||
| 65 | * * |
||
| 66 | * First, use GenerateMoves() to generate the set of legal * |
||
| 67 | * moves from the root position. * |
||
| 68 | * * |
||
| 69 | ************************************************************ |
||
| 70 | */ |
||
| 71 | lastm = GenerateCaptures(tree, 1, wtm, rmoves); |
||
| 72 | lastm = GenerateNoncaptures(tree, 1, wtm, lastm); |
||
| 73 | n_root_moves = lastm - rmoves; |
||
| 154 | pmbaty | 74 | for (mvp = 0; mvp < n_root_moves; mvp++) |
| 108 | pmbaty | 75 | root_moves[mvp].move = rmoves[mvp]; |
| 33 | pmbaty | 76 | /* |
| 77 | ************************************************************ |
||
| 78 | * * |
||
| 79 | * Now make each move and use Quiesce() to analyze the * |
||
| 80 | * potential captures and return a minimax score. * |
||
| 81 | * * |
||
| 82 | * Special case: if this is an egtb draw at the root, * |
||
| 83 | * then this is where we cull the non-drawing moves by * |
||
| 84 | * doing an EGTB probe for each move. Any moves that lose * |
||
| 85 | * are left with a very bad sorting score to move them to * |
||
| 86 | * the end of the root move list. * |
||
| 87 | * * |
||
| 88 | ************************************************************ |
||
| 89 | */ |
||
| 108 | pmbaty | 90 | abort_search = 0; |
| 154 | pmbaty | 91 | for (mvp = 0; mvp < n_root_moves; mvp++) { |
| 33 | pmbaty | 92 | value = -4000000; |
| 93 | #if defined(TRACE) |
||
| 94 | if (trace_level >= 1) { |
||
| 108 | pmbaty | 95 | tree->curmv[1] = root_moves[mvp].move; |
| 96 | Trace(tree, 1, 0, wtm, -MATE, MATE, "RootMoves()", serial, HASH, |
||
| 97 | mvp + 1); |
||
| 33 | pmbaty | 98 | } |
| 99 | #endif |
||
| 108 | pmbaty | 100 | MakeMove(tree, 1, wtm, root_moves[mvp].move); |
| 33 | pmbaty | 101 | tree->nodes_searched++; |
| 102 | if (!Check(wtm)) |
||
| 103 | do { |
||
| 108 | pmbaty | 104 | tree->curmv[1] = root_moves[mvp].move; |
| 154 | pmbaty | 105 | #if defined(SYZYGY) |
| 106 | if (EGTB_draw && TotalAllPieces <= EGTBlimit && |
||
| 107 | Castle(2, white) + Castle(2, black) == 0) { |
||
| 108 | tb_result = |
||
| 109 | tb_probe_root(Occupied(white), Occupied(black), |
||
| 110 | Kings(white) | Kings(black), Queens(white) | Queens(black), |
||
| 111 | Rooks(white) | Rooks(black), Bishops(white) | Bishops(black), |
||
| 112 | Knights(white) | Knights(black), Pawns(white) | Pawns(black), |
||
| 113 | Reversible(2), 0, EnPassant(2), Flip(wtm), NULL); |
||
| 114 | if (tb_result != TB_RESULT_FAILED) { |
||
| 115 | tb_result = 4 - TB_GET_WDL(tb_result); |
||
| 116 | if (tb_result < tb_root) |
||
| 117 | break; |
||
| 118 | } |
||
| 33 | pmbaty | 119 | } |
| 120 | #endif |
||
| 108 | pmbaty | 121 | value = -Quiesce(tree, 2, Flip(wtm), -MATE, MATE, 0); |
| 33 | pmbaty | 122 | /* |
| 123 | ************************************************************ |
||
| 124 | * * |
||
| 125 | * Add in a bonus if this move is part of the previous * |
||
| 126 | * principal variation. It was good in the search, we * |
||
| 127 | * should try it first now. * |
||
| 128 | * * |
||
| 129 | ************************************************************ |
||
| 130 | */ |
||
| 108 | pmbaty | 131 | if ((Piece(root_moves[mvp].move) == Piece(last_pv.path[1])) |
| 132 | && (From(root_moves[mvp].move) == From(last_pv.path[1])) |
||
| 133 | && (To(root_moves[mvp].move) == To(last_pv.path[1])) |
||
| 134 | && (Captured(root_moves[mvp].move) == Captured(last_pv.path[1])) |
||
| 135 | && (Promote(root_moves[mvp].move) == Promote(last_pv.path[1]))) |
||
| 33 | pmbaty | 136 | value += 2000000; |
| 137 | /* |
||
| 138 | ************************************************************ |
||
| 139 | * * |
||
| 140 | * Fudge the score for promotions so that promotion to a * |
||
| 141 | * queen is tried first. * |
||
| 142 | * * |
||
| 143 | ************************************************************ |
||
| 144 | */ |
||
| 108 | pmbaty | 145 | if (Promote(root_moves[mvp].move) && |
| 146 | (Promote(root_moves[mvp].move) != queen)) |
||
| 33 | pmbaty | 147 | value -= 50; |
| 148 | } while (0); |
||
| 108 | pmbaty | 149 | root_moves[mvp].path = tree->pv[1]; |
| 150 | root_moves[mvp].path.pathv = value; |
||
| 151 | root_moves[mvp].status = 0; |
||
| 152 | root_moves[mvp].bm_age = 0; |
||
| 153 | UnmakeMove(tree, 1, wtm, root_moves[mvp].move); |
||
| 33 | pmbaty | 154 | } |
| 155 | /* |
||
| 156 | ************************************************************ |
||
| 157 | * * |
||
| 158 | * Sort the moves into order based on the scores returned * |
||
| 159 | * by Quiesce() which includes evaluation + captures. * |
||
| 160 | * * |
||
| 161 | ************************************************************ |
||
| 162 | */ |
||
| 154 | pmbaty | 163 | do { |
| 164 | done = 1; |
||
| 165 | for (mvp = 0; mvp < n_root_moves - 1; mvp++) { |
||
| 166 | if (root_moves[mvp].path.pathv < root_moves[mvp + 1].path.pathv) { |
||
| 167 | rtemp = root_moves[mvp]; |
||
| 168 | root_moves[mvp] = root_moves[mvp + 1]; |
||
| 169 | root_moves[mvp + 1] = rtemp; |
||
| 170 | done = 0; |
||
| 171 | } |
||
| 172 | } |
||
| 173 | } while (!done); |
||
| 33 | pmbaty | 174 | /* |
| 175 | ************************************************************ |
||
| 176 | * * |
||
| 177 | * Trim the move list to eliminate those moves that hang * |
||
| 178 | * the king and are illegal. This also culls any non- * |
||
| 179 | * drawing moves when we are in the swindle-mode situation * |
||
| 180 | * and want to do a normal search but only on moves that * |
||
| 181 | * preserve the draw. * |
||
| 182 | * * |
||
| 183 | ************************************************************ |
||
| 184 | */ |
||
| 185 | for (; n_root_moves; n_root_moves--) |
||
| 108 | pmbaty | 186 | if (root_moves[n_root_moves - 1].path.pathv > -3000000) |
| 33 | pmbaty | 187 | break; |
| 108 | pmbaty | 188 | if (root_moves[0].path.pathv > 1000000) |
| 189 | root_moves[0].path.pathv -= 2000000; |
||
| 33 | pmbaty | 190 | /* |
| 191 | ************************************************************ |
||
| 192 | * * |
||
| 193 | * Debugging output to dump root move list and the stuff * |
||
| 194 | * used to sort them, for testing and debugging. * |
||
| 195 | * * |
||
| 196 | ************************************************************ |
||
| 197 | */ |
||
| 108 | pmbaty | 198 | if (display_options & 128) { |
| 199 | Print(128, "%d moves at root\n", n_root_moves); |
||
| 200 | Print(128, " score move/pv\n"); |
||
| 154 | pmbaty | 201 | for (mvp = 0; mvp < n_root_moves; mvp++) |
| 108 | pmbaty | 202 | Print(128, "%10s %s\n", DisplayEvaluation(root_moves[mvp].path.pathv, |
| 203 | wtm), DisplayPath(tree, wtm, &root_moves[mvp].path)); |
||
| 33 | pmbaty | 204 | } |
| 205 | /* |
||
| 206 | ************************************************************ |
||
| 207 | * * |
||
| 108 | pmbaty | 208 | * Finally, before we return the list of moves, we need to * |
| 209 | * set the values to an impossible negative value so that * |
||
| 210 | * as we sort the root move list after fail highs and lows * |
||
| 211 | * the un-searched moves won't pop to the top of the list. * |
||
| 33 | pmbaty | 212 | * * |
| 213 | ************************************************************ |
||
| 214 | */ |
||
| 154 | pmbaty | 215 | for (mvp = 1; mvp < n_root_moves; mvp++) |
| 216 | root_moves[mvp].path.pathv = -MATE; |
||
| 33 | pmbaty | 217 | return; |
| 218 | } |
||
| 154 | pmbaty | 219 | |
| 220 | /* last modified 07/11/16 */ |
||
| 221 | /* |
||
| 222 | ******************************************************************************* |
||
| 223 | * * |
||
| 224 | * RootMoveEGTB() is used to handle the case where we are using syzygy end- * |
||
| 225 | * game tablebases and the root position is found in them. We need to use * |
||
| 226 | * the DTZ tables to play the best move we can find since the game outcome * |
||
| 227 | * is known for each possible move at this point. We return it in a manner * |
||
| 228 | * similar to Book(). * |
||
| 229 | * * |
||
| 230 | * Note: This depends on RootMoveList() being called FIRST since it is the * |
||
| 231 | * responsible party to note that we are drawn at the root according to EGTB * |
||
| 232 | * and if appropriate, it will let RootMoveEGTB() know this to activate * |
||
| 233 | * "swindle mode" and play on with a search rather than an instant move. * |
||
| 234 | * * |
||
| 235 | ******************************************************************************* |
||
| 236 | */ |
||
| 237 | int RootMoveEGTB(int wtm) { |
||
| 238 | #if defined(SYZYGY) |
||
| 239 | TREE *const tree = block[0]; |
||
| 240 | int tb_result, result; |
||
| 241 | |||
| 242 | /* |
||
| 243 | ************************************************************ |
||
| 244 | * * |
||
| 245 | * first, we need to find the best TB move. Simply, this * |
||
| 246 | * is the move that gives us the best result, even though * |
||
| 247 | * it might be speculative in the case of choosing a * |
||
| 248 | * "cursed win" which is still technically a draw if the * |
||
| 249 | * opponent makes no errors. * |
||
| 250 | * * |
||
| 251 | ************************************************************ |
||
| 252 | */ |
||
| 253 | EGTB_use = EGTBlimit; |
||
| 254 | if (EGTB_use <= 0) |
||
| 255 | return 0; |
||
| 256 | if (EGTB_draw && !puzzling && swindle_mode) |
||
| 257 | EGTB_use = 0; |
||
| 258 | if (EGTBlimit && !EGTB_use) |
||
| 259 | Print(32, "Drawn at root, trying for swindle.\n"); |
||
| 260 | if (EGTB_use && TotalAllPieces <= EGTBlimit && !Castle(0, white) && |
||
| 261 | !Castle(0, black)) { |
||
| 262 | tree->egtb_probes++; |
||
| 263 | tb_result = |
||
| 264 | tb_probe_root(Occupied(white), Occupied(black), |
||
| 265 | Kings(white) | Kings(black), Queens(white) | Queens(black), |
||
| 266 | Rooks(white) | Rooks(black), Bishops(white) | Bishops(black), |
||
| 267 | Knights(white) | Knights(black), Pawns(white) | Pawns(black), |
||
| 268 | Reversible(1), 0, EnPassant(1), wtm, NULL); |
||
| 269 | if (tb_result != TB_RESULT_FAILED) { |
||
| 270 | int value, piece, captured; |
||
| 271 | unsigned cmove, omove; |
||
| 272 | |||
| 273 | if (n_root_moves > 0) { |
||
| 274 | tree->egtb_hits++; |
||
| 275 | result = TB_GET_WDL(tb_result); |
||
| 276 | switch (result) { |
||
| 277 | case TB_LOSS: |
||
| 278 | value = -TBWIN; |
||
| 279 | break; |
||
| 280 | case TB_WIN: |
||
| 281 | value = TBWIN; |
||
| 282 | break; |
||
| 283 | case TB_BLESSED_LOSS: |
||
| 284 | value = -3; |
||
| 285 | break; |
||
| 286 | case TB_DRAW: |
||
| 287 | value = 0; |
||
| 288 | break; |
||
| 289 | case TB_CURSED_WIN: |
||
| 290 | value = 3; |
||
| 291 | break; |
||
| 292 | default: |
||
| 293 | value = TB_GET_DTZ(tb_result);; |
||
| 294 | break; |
||
| 295 | } |
||
| 296 | if (result != TB_LOSS && result != TB_WIN) { |
||
| 297 | if (MaterialSTM(wtm) > 0) |
||
| 298 | value += 1; |
||
| 299 | else if (MaterialSTM(wtm) < 0) |
||
| 300 | value -= 1; |
||
| 301 | } |
||
| 302 | piece = abs(PcOnSq(TB_GET_FROM(tb_result))); |
||
| 303 | captured = abs(PcOnSq(TB_GET_TO(tb_result))); |
||
| 304 | cmove = |
||
| 305 | TB_GET_FROM(tb_result) | (TB_GET_TO(tb_result) << 6) | (piece << |
||
| 306 | 12) | (captured << 15); |
||
| 307 | if (TB_GET_PROMOTES(tb_result)) |
||
| 308 | cmove |= (6 - TB_GET_PROMOTES(tb_result)) << 18; |
||
| 309 | end_time = ReadClock(); |
||
| 310 | tree->pv[0].path[1] = cmove; |
||
| 311 | tree->pv[0].pathl = 2; |
||
| 312 | tree->pv[0].pathh = 4; |
||
| 313 | tree->pv[0].pathd = 0; |
||
| 314 | tree->pv[0].pathv = value; |
||
| 315 | MakeMove(tree, 1, wtm, cmove); |
||
| 316 | result = Mated(tree, 2, Flip(wtm)); |
||
| 317 | UnmakeMove(tree, 1, wtm, cmove); |
||
| 318 | if (result == 1) |
||
| 319 | tree->pv[0].pathv = MATE - 2; |
||
| 320 | else if (result == 2) |
||
| 321 | tree->pv[0].pathv = DrawScore(wtm); |
||
| 322 | /* |
||
| 323 | ************************************************************ |
||
| 324 | * * |
||
| 325 | * If we are not mated and did not mate on the move, we * |
||
| 326 | * flip the side on move and find the best TB move so that * |
||
| 327 | * we can show the expected reply in the PV. * |
||
| 328 | * * |
||
| 329 | ************************************************************ |
||
| 330 | */ |
||
| 331 | else { |
||
| 332 | MakeMove(tree, 1, wtm, cmove); |
||
| 333 | tree->egtb_probes++; |
||
| 334 | tb_result = |
||
| 335 | tb_probe_root(Occupied(white), Occupied(black), |
||
| 336 | Kings(white) | Kings(black), Queens(white) | Queens(black), |
||
| 337 | Rooks(white) | Rooks(black), Bishops(white) | Bishops(black), |
||
| 338 | Knights(white) | Knights(black), Pawns(white) | Pawns(black), |
||
| 339 | Reversible(2), 0, EnPassant(2), Flip(wtm), NULL); |
||
| 340 | if (tb_result != TB_RESULT_FAILED) { |
||
| 341 | tree->egtb_hits++; |
||
| 342 | piece = abs(PcOnSq(TB_GET_FROM(tb_result))); |
||
| 343 | captured = abs(PcOnSq(TB_GET_TO(tb_result))); |
||
| 344 | omove = |
||
| 345 | TB_GET_FROM(tb_result) | (TB_GET_TO(tb_result) << 6) | (piece |
||
| 346 | << 12) | (captured << 15); |
||
| 347 | if (TB_GET_PROMOTES(tb_result)) |
||
| 348 | omove |= (6 - TB_GET_PROMOTES(tb_result)) << 18; |
||
| 349 | end_time = ReadClock(); |
||
| 350 | tree->pv[0].path[2] = omove; |
||
| 351 | tree->pv[0].pathl = 3; |
||
| 352 | } |
||
| 353 | UnmakeMove(tree, 1, wtm, cmove); |
||
| 354 | } |
||
| 355 | } |
||
| 356 | /* |
||
| 357 | ************************************************************ |
||
| 358 | * * |
||
| 359 | * We now know the best move to play, and possibly the * |
||
| 360 | * opponent's best response. Display this info and then * |
||
| 361 | * we wait for the next move to pop in. * |
||
| 362 | * * |
||
| 363 | ************************************************************ |
||
| 364 | */ |
||
| 365 | Print(2, " depth time score variation\n"); |
||
| 366 | if (n_root_moves == 0) { |
||
| 367 | program_end_time = ReadClock(); |
||
| 368 | tree->pv[0].pathl = 0; |
||
| 369 | tree->pv[0].pathd = 0; |
||
| 370 | if (Check(wtm)) |
||
| 371 | value = -(MATE - 1); |
||
| 372 | else |
||
| 373 | value = DrawScore(wtm); |
||
| 374 | Print(2, " Mated (no moves)\n"); |
||
| 375 | tree->nodes_searched = 1; |
||
| 376 | if (!puzzling) |
||
| 377 | last_root_value = value; |
||
| 378 | return 1; |
||
| 379 | } |
||
| 380 | DisplayPV(tree, 5, wtm, end_time - start_time, &tree->pv[0], 1); |
||
| 381 | return 1; |
||
| 382 | } |
||
| 383 | } |
||
| 384 | #endif |
||
| 385 | return 0; |
||
| 386 | } |