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