#include "chess.h"
#include "data.h"
#include "epdglue.h"
#if defined(SYZYGY)
# include "tbprobe.h"
#endif
/* last modified 07/11/16 */
/*
*******************************************************************************
* *
* RootMoveList() is used to set up the ply one move list. It is a more *
* accurate ordering of the move list than that done for plies deeper than *
* one. Briefly, Quiesce() is used to obtain the positional score plus the *
* expected gain/loss for pieces that can be captured. *
* *
*******************************************************************************
*/
void RootMoveList(int wtm) {
TREE *const tree = block[0];
ROOT_MOVE rtemp;
int mvp; unsigned *lastm, rmoves[256]; // Pierre-Marie Baty -- fixed type
int value, done;
#if defined(SYZYGY)
int tb_result, tb_root = -9;
#endif
/*
************************************************************
* *
* If the position at the root is a draw, based on EGTB *
* results, we are going to behave differently. We will *
* extract the root moves that are draws, and toss the *
* losers out. Then, we will do a normal search on the *
* moves that draw to try and chose the drawing move that *
* gives our opponent the best chance to make an error. *
* This search will be done sans EGTB probes since we al- *
* ready know this is a draw at the root. We simply find *
* the best move (based on search/eval) that preserves the *
* draw. *
* *
************************************************************
*/
#if defined(SYZYGY)
EGTB_draw = 0;
if (swindle_mode) {
if (EGTBlimit && TotalAllPieces <= EGTBlimit &&
Castle(1, white) + Castle(1, black) == 0) {
tb_result =
tb_probe_root(Occupied(white), Occupied(black),
Kings(white) | Kings(black), Queens(white) | Queens(black),
Rooks(white) | Rooks(black), Bishops(white) | Bishops(black),
Knights(white) | Knights(black), Pawns(white) | Pawns(black),
Reversible(1), 0, EnPassant(1), wtm, NULL);
if (tb_result != TB_RESULT_FAILED) {
tb_root = TB_GET_WDL(tb_result);
if ((tb_root == TB_DRAW && MaterialSTM(wtm) > 0) ||
(tb_root == TB_CURSED_WIN))
EGTB_draw = 1;
}
}
}
#endif
/*
************************************************************
* *
* First, use GenerateMoves() to generate the set of legal *
* moves from the root position. *
* *
************************************************************
*/
lastm = GenerateCaptures(tree, 1, wtm, rmoves);
lastm = GenerateNoncaptures(tree, 1, wtm, lastm);
n_root_moves = lastm - rmoves;
for (mvp = 0; mvp < n_root_moves; mvp++)
root_moves[mvp].move = rmoves[mvp];
/*
************************************************************
* *
* Now make each move and use Quiesce() to analyze the *
* potential captures and return a minimax score. *
* *
* Special case: if this is an egtb draw at the root, *
* then this is where we cull the non-drawing moves by *
* doing an EGTB probe for each move. Any moves that lose *
* are left with a very bad sorting score to move them to *
* the end of the root move list. *
* *
************************************************************
*/
abort_search = 0;
for (mvp = 0; mvp < n_root_moves; mvp++) {
value = -4000000;
#if defined(TRACE)
if (trace_level >= 1) {
tree->curmv[1] = root_moves[mvp].move;
Trace(tree, 1, 0, wtm, -MATE, MATE, "RootMoves()", serial, HASH,
mvp + 1);
}
#endif
MakeMove(tree, 1, wtm, root_moves[mvp].move);
tree->nodes_searched++;
if (!Check(wtm))
do {
tree->curmv[1] = root_moves[mvp].move;
#if defined(SYZYGY)
if (EGTB_draw && TotalAllPieces <= EGTBlimit &&
Castle(2, white) + Castle(2, black) == 0) {
tb_result =
tb_probe_root(Occupied(white), Occupied(black),
Kings(white) | Kings(black), Queens(white) | Queens(black),
Rooks(white) | Rooks(black), Bishops(white) | Bishops(black),
Knights(white) | Knights(black), Pawns(white) | Pawns(black),
Reversible(2), 0, EnPassant(2), Flip(wtm), NULL);
if (tb_result != TB_RESULT_FAILED) {
tb_result = 4 - TB_GET_WDL(tb_result);
if (tb_result < tb_root)
break;
}
}
#endif
value = -Quiesce(tree, 2, Flip(wtm), -MATE, MATE, 0);
/*
************************************************************
* *
* Add in a bonus if this move is part of the previous *
* principal variation. It was good in the search, we *
* should try it first now. *
* *
************************************************************
*/
if ((Piece(root_moves[mvp].move) == Piece(last_pv.path[1]))
&& (From(root_moves[mvp].move) == From(last_pv.path[1]))
&& (To(root_moves[mvp].move) == To(last_pv.path[1]))
&& (Captured(root_moves[mvp].move) == Captured(last_pv.path[1]))
&& (Promote(root_moves[mvp].move) == Promote(last_pv.path[1])))
value += 2000000;
/*
************************************************************
* *
* Fudge the score for promotions so that promotion to a *
* queen is tried first. *
* *
************************************************************
*/
if (Promote(root_moves[mvp].move) &&
(Promote(root_moves[mvp].move) != queen))
value -= 50;
} while (0);
root_moves[mvp].path = tree->pv[1];
root_moves[mvp].path.pathv = value;
root_moves[mvp].status = 0;
root_moves[mvp].bm_age = 0;
UnmakeMove(tree, 1, wtm, root_moves[mvp].move);
}
/*
************************************************************
* *
* Sort the moves into order based on the scores returned *
* by Quiesce() which includes evaluation + captures. *
* *
************************************************************
*/
do {
done = 1;
for (mvp = 0; mvp < n_root_moves - 1; mvp++) {
if (root_moves[mvp].path.pathv < root_moves[mvp + 1].path.pathv) {
rtemp = root_moves[mvp];
root_moves[mvp] = root_moves[mvp + 1];
root_moves[mvp + 1] = rtemp;
done = 0;
}
}
} while (!done);
/*
************************************************************
* *
* Trim the move list to eliminate those moves that hang *
* the king and are illegal. This also culls any non- *
* drawing moves when we are in the swindle-mode situation *
* and want to do a normal search but only on moves that *
* preserve the draw. *
* *
************************************************************
*/
for (; n_root_moves; n_root_moves--)
if (root_moves[n_root_moves - 1].path.pathv > -3000000)
break;
if (root_moves[0].path.pathv > 1000000)
root_moves[0].path.pathv -= 2000000;
/*
************************************************************
* *
* Debugging output to dump root move list and the stuff *
* used to sort them, for testing and debugging. *
* *
************************************************************
*/
if (display_options & 128) {
Print(128, "%d moves at root\n", n_root_moves);
Print(128, " score move/pv\n");
for (mvp = 0; mvp < n_root_moves; mvp++)
Print(128, "%10s %s\n", DisplayEvaluation(root_moves[mvp].path.pathv,
wtm), DisplayPath(tree, wtm, &root_moves[mvp].path));
}
/*
************************************************************
* *
* Finally, before we return the list of moves, we need to *
* set the values to an impossible negative value so that *
* as we sort the root move list after fail highs and lows *
* the un-searched moves won't pop to the top of the list. *
* *
************************************************************
*/
for (mvp = 1; mvp < n_root_moves; mvp++)
root_moves[mvp].path.pathv = -MATE;
return;
}
/* last modified 07/11/16 */
/*
*******************************************************************************
* *
* RootMoveEGTB() is used to handle the case where we are using syzygy end- *
* game tablebases and the root position is found in them. We need to use *
* the DTZ tables to play the best move we can find since the game outcome *
* is known for each possible move at this point. We return it in a manner *
* similar to Book(). *
* *
* Note: This depends on RootMoveList() being called FIRST since it is the *
* responsible party to note that we are drawn at the root according to EGTB *
* and if appropriate, it will let RootMoveEGTB() know this to activate *
* "swindle mode" and play on with a search rather than an instant move. *
* *
*******************************************************************************
*/
int RootMoveEGTB(int wtm) {
#if defined(SYZYGY)
TREE *const tree = block[0];
int tb_result, result;
/*
************************************************************
* *
* first, we need to find the best TB move. Simply, this *
* is the move that gives us the best result, even though *
* it might be speculative in the case of choosing a *
* "cursed win" which is still technically a draw if the *
* opponent makes no errors. *
* *
************************************************************
*/
EGTB_use = EGTBlimit;
if (EGTB_use <= 0)
return 0;
if (EGTB_draw && !puzzling && swindle_mode)
EGTB_use = 0;
if (EGTBlimit && !EGTB_use)
Print(32, "Drawn at root, trying for swindle.\n");
if (EGTB_use && TotalAllPieces <= EGTBlimit && !Castle(0, white) &&
!Castle(0, black)) {
tree->egtb_probes++;
tb_result =
tb_probe_root(Occupied(white), Occupied(black),
Kings(white) | Kings(black), Queens(white) | Queens(black),
Rooks(white) | Rooks(black), Bishops(white) | Bishops(black),
Knights(white) | Knights(black), Pawns(white) | Pawns(black),
Reversible(1), 0, EnPassant(1), wtm, NULL);
if (tb_result != TB_RESULT_FAILED) {
int value, piece, captured;
unsigned cmove, omove;
if (n_root_moves > 0) {
tree->egtb_hits++;
result = TB_GET_WDL(tb_result);
switch (result) {
case TB_LOSS:
value = -TBWIN;
break;
case TB_WIN:
value = TBWIN;
break;
case TB_BLESSED_LOSS:
value = -3;
break;
case TB_DRAW:
value = 0;
break;
case TB_CURSED_WIN:
value = 3;
break;
default:
value = TB_GET_DTZ(tb_result);;
break;
}
if (result != TB_LOSS && result != TB_WIN) {
if (MaterialSTM(wtm) > 0)
value += 1;
else if (MaterialSTM(wtm) < 0)
value -= 1;
}
piece
= abs(PcOnSq
(TB_GET_FROM
(tb_result
)));
captured
= abs(PcOnSq
(TB_GET_TO
(tb_result
)));
cmove =
TB_GET_FROM(tb_result) | (TB_GET_TO(tb_result) << 6) | (piece <<
12) | (captured << 15);
if (TB_GET_PROMOTES(tb_result))
cmove |= (6 - TB_GET_PROMOTES(tb_result)) << 18;
end_time = ReadClock();
tree->pv[0].path[1] = cmove;
tree->pv[0].pathl = 2;
tree->pv[0].pathh = 4;
tree->pv[0].pathd = 0;
tree->pv[0].pathv = value;
MakeMove(tree, 1, wtm, cmove);
result = Mated(tree, 2, Flip(wtm));
UnmakeMove(tree, 1, wtm, cmove);
if (result == 1)
tree->pv[0].pathv = MATE - 2;
else if (result == 2)
tree->pv[0].pathv = DrawScore(wtm);
/*
************************************************************
* *
* If we are not mated and did not mate on the move, we *
* flip the side on move and find the best TB move so that *
* we can show the expected reply in the PV. *
* *
************************************************************
*/
else {
MakeMove(tree, 1, wtm, cmove);
tree->egtb_probes++;
tb_result =
tb_probe_root(Occupied(white), Occupied(black),
Kings(white) | Kings(black), Queens(white) | Queens(black),
Rooks(white) | Rooks(black), Bishops(white) | Bishops(black),
Knights(white) | Knights(black), Pawns(white) | Pawns(black),
Reversible(2), 0, EnPassant(2), Flip(wtm), NULL);
if (tb_result != TB_RESULT_FAILED) {
tree->egtb_hits++;
piece
= abs(PcOnSq
(TB_GET_FROM
(tb_result
)));
captured
= abs(PcOnSq
(TB_GET_TO
(tb_result
)));
omove =
TB_GET_FROM(tb_result) | (TB_GET_TO(tb_result) << 6) | (piece
<< 12) | (captured << 15);
if (TB_GET_PROMOTES(tb_result))
omove |= (6 - TB_GET_PROMOTES(tb_result)) << 18;
end_time = ReadClock();
tree->pv[0].path[2] = omove;
tree->pv[0].pathl = 3;
}
UnmakeMove(tree, 1, wtm, cmove);
}
}
/*
************************************************************
* *
* We now know the best move to play, and possibly the *
* opponent's best response. Display this info and then *
* we wait for the next move to pop in. *
* *
************************************************************
*/
Print(2, " depth time score variation\n");
if (n_root_moves == 0) {
program_end_time = ReadClock();
tree->pv[0].pathl = 0;
tree->pv[0].pathd = 0;
if (Check(wtm))
value = -(MATE - 1);
else
value = DrawScore(wtm);
Print(2, " Mated (no moves)\n");
tree->nodes_searched = 1;
if (!puzzling)
last_root_value = value;
return 1;
}
DisplayPV(tree, 5, wtm, end_time - start_time, &tree->pv[0], 1);
return 1;
}
}
#endif
return 0;
}