1 #include "chess.h"
2 #include "data.h"
3 #include "epdglue.h"
4 #if defined(SYZYGY)
5 # include "tbprobe.h"
6 #endif
7 /* last modified 07/11/16 */
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 */
RootMoveList(int wtm)18 void RootMoveList(int wtm) {
19 TREE *const tree = block[0];
20 ROOT_MOVE rtemp;
21 unsigned mvp, *lastm, rmoves[256];
22 int value, done;
23 #if defined(SYZYGY)
24 int tb_result, tb_root = -9;
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 */
43 #if defined(SYZYGY)
44 EGTB_draw = 0;
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 }
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;
74 for (mvp = 0; mvp < n_root_moves; mvp++)
75 root_moves[mvp].move = rmoves[mvp];
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 */
90 abort_search = 0;
91 for (mvp = 0; mvp < n_root_moves; mvp++) {
92 value = -4000000;
93 #if defined(TRACE)
94 if (trace_level >= 1) {
95 tree->curmv[1] = root_moves[mvp].move;
96 Trace(tree, 1, 0, wtm, -MATE, MATE, "RootMoves()", serial, HASH,
97 mvp + 1);
98 }
99 #endif
100 MakeMove(tree, 1, wtm, root_moves[mvp].move);
101 tree->nodes_searched++;
102 if (!Check(wtm))
103 do {
104 tree->curmv[1] = root_moves[mvp].move;
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 }
119 }
120 #endif
121 value = -Quiesce(tree, 2, Flip(wtm), -MATE, MATE, 0);
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 */
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])))
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 */
145 if (Promote(root_moves[mvp].move) &&
146 (Promote(root_moves[mvp].move) != queen))
147 value -= 50;
148 } while (0);
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);
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 */
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);
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--)
186 if (root_moves[n_root_moves - 1].path.pathv > -3000000)
187 break;
188 if (root_moves[0].path.pathv > 1000000)
189 root_moves[0].path.pathv -= 2000000;
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 */
198 if (display_options & 128) {
199 Print(128, "%d moves at root\n", n_root_moves);
200 Print(128, " score move/pv\n");
201 for (mvp = 0; mvp < n_root_moves; mvp++)
202 Print(128, "%10s %s\n", DisplayEvaluation(root_moves[mvp].path.pathv,
203 wtm), DisplayPath(tree, wtm, &root_moves[mvp].path));
204 }
205 /*
206 ************************************************************
207 * *
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. *
212 * *
213 ************************************************************
214 */
215 for (mvp = 1; mvp < n_root_moves; mvp++)
216 root_moves[mvp].path.pathv = -MATE;
217 return;
218 }
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 */
RootMoveEGTB(int wtm)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 }
387