#if defined(DEBUG)
# include "chess.h"
# include "data.h"
/* last modified 02/26/14 */
/*
*******************************************************************************
* *
* ValidatePosition() is a debugging tool that is enabled by using the *
* -DDEBUG compilation flag. This procedure tests the various data *
* structures used in Crafty related to the chess board and incrementally *
* updated values like hash signatures and so forth. It simply looks for *
* consistency between the various bitboards, and recomputes the hash *
* signatures to determine if they are correct. If anything fails to pass *
* the validation test, we print out a dump of the moves made in this path *
* through the tree, and then exit since things are corrupted. *
* *
* This greatly slows the program down, because ValidatePosition() is called *
* after each Make()/Unmake() (these are the functions that modify the *
* primary data structures). In general, this will not be used by users *
* unless they are modifying the source code themselves. *
* *
*******************************************************************************
*/
void ValidatePosition(TREE * RESTRICT tree, int ply, int move, char *caller) {
uint64_t temp, temp1, temp_occ;
uint64_t temp_occx;
int i, square, error = 0;
int side, piece, temp_score;
/*
************************************************************
* *
* First, test occupied[side] which should match the OR *
* result of all pieces[side]. *
* *
************************************************************
*/
for (side = black; side <= white; side++) {
temp_occ =
Pawns(side) | Knights(side) | Bishops(side) | Rooks(side) |
Queens(side)
| Kings(side);
if (Occupied(side) ^ temp_occ) {
if (!error)
Print(2048, "\n");
Print(2048, "ERROR %s occupied squares is bad!\n",
(side) ? "white" : "black");
Display2BitBoards(temp_occ, Occupied(white));
error = 1;
}
}
/*
************************************************************
* *
* Now we do some sanity tests on the actual chess board *
* information. The first test is to make sure that no *
* bitmap square is set in more than one bitmap, which *
* would imply two different pieces on the same square. *
* *
************************************************************
*/
temp_occ =
Pawns(white) ^ Knights(white) ^ Bishops(white) ^ Rooks(white) ^
Queens(white) ^ Pawns(black) ^ Knights(black) ^ Bishops(black) ^
Rooks(black) ^ Queens(black) ^ Kings(white) ^ Kings(black);
temp_occx =
Pawns(white) | Knights(white) | Bishops(white) | Rooks(white) |
Queens(white) | Pawns(black) | Knights(black) | Bishops(black) |
Rooks(black) | Queens(black) | Kings(white) | Kings(black);
if (temp_occ ^ temp_occx) {
if (!error)
Print(2048, "\n");
Print(2048, "ERROR two pieces on same square\n");
error = 1;
}
/*
************************************************************
* *
* Add up all the pieces (material values) to see if this *
* matches the incrementally updated value. *
* *
************************************************************
*/
temp_score = 0;
for (side = black; side <= white; side++)
for (piece = pawn; piece < king; piece++)
temp_score += PopCnt(Pieces(side, piece)) * PieceValues(side, piece);
if (temp_score != Material) {
if (!error)
Print(2048, "\n");
Print(2048, "ERROR material evaluation is wrong, good=%d, bad=%d\n",
temp_score, Material);
error = 1;
}
/*
************************************************************
* *
* Next, check the incrementally updated piece counts for *
* both sides. ditto for pawn counts. *
* *
************************************************************
*/
for (side = black; side <= white; side++) {
temp_score = 0;
for (piece = knight; piece < king; piece++)
temp_score += PopCnt(Pieces(side, piece)) * p_vals[piece];
if (temp_score != TotalPieces(side, occupied)) {
if (!error)
Print(2048, "\n");
Print(2048, "ERROR %s pieces is wrong, good=%d, bad=%d\n",
(side) ? "white" : "black", temp_score, TotalPieces(side,
occupied));
error = 1;
}
}
for (side = black; side <= white; side++) {
temp_score = PopCnt(Pawns(side));
if (temp_score != TotalPieces(side, pawn)) {
if (!error)
Print(2048, "\n");
Print(2048, "ERROR %s pawns is wrong, good=%d, bad=%d\n",
(side) ? "white" : "black", temp_score, TotalPieces(side, pawn));
error = 1;
}
}
i = PopCnt(OccupiedSquares);
if (i != TotalAllPieces) {
if (!error)
Print(2048, "\n");
Print(2048, "ERROR! TotalAllPieces is wrong, correct=%d bad=%d\n", i,
TotalAllPieces);
error = 1;
}
/*
************************************************************
* *
* Now we cycle through each different chessboard bitmap *
* and verify that each piece in a bitmap matches the same *
* piece type in the board[64] array. *
* *
************************************************************
*/
for (side = black; side <= white; side++)
for (piece = pawn; piece <= king; piece++) {
temp = Pieces(side, piece);
while (temp) {
square = LSB(temp);
if (PcOnSq(square) != pieces[side][piece]) {
if (!error)
Print(2048, "\n");
Print(2048, "ERROR! board[%d]=%d, should be %d\n", square,
PcOnSq(square), pieces[side][piece]);
error = 1;
}
temp &= temp - 1;
}
}
/*
************************************************************
* *
* And then we look at the board[64] array and make sure *
* that any non-zero piece matches the proper bitmap for *
* that particular piece type. *
* *
************************************************************
*/
for (i = 0; i < 64; i++) {
if (!PcOnSq(i))
continue;
side = (PcOnSq(i) > 0) ? 1 : 0;
if (SetMask(i) & Pieces(side, Abs(PcOnSq(i))))
continue;
if (!error)
Print(2048, "\n");
Print(2048, "ERROR! bitboards/board[%d] don't agree!\n", i);
error = 1;
break;
}
/*
************************************************************
* *
* The last chess board test is to make sure that any *
* square that is empty according to board[64] is also *
* empty according to the occupied squares bitmap. *
* *
************************************************************
*/
temp = ~(temp_occ | temp_occx);
while (temp) {
square = LSB(temp);
if (PcOnSq(square)) {
if (!error)
Print(2048, "\n");
Print(2048, "ERROR! board[%d]=%d, should be 0\n", square,
PcOnSq(square));
error = 1;
}
temp &= temp - 1;
}
/*
************************************************************
* *
* Finally, we re-compute the pawn hash signature and the *
* normal hash signature and verify that they match the *
* incrementally updated values. *
* *
************************************************************
*/
temp = 0;
temp1 = 0;
for (i = 0; i < 64; i++) {
side = (PcOnSq(i) > 0) ? 1 : 0;
temp ^= randoms[side][Abs(PcOnSq(i))][i];
if (Abs(PcOnSq(i)) == pawn)
temp1 ^= randoms[side][Abs(PcOnSq(i))][i];
}
if (EnPassant(ply))
temp ^= enpassant_random[EnPassant(ply)];
for (side = black; side <= white; side++) {
if (Castle(ply, side) < 0 || !(Castle(ply, side) & 1))
temp ^= castle_random[0][side];
if (Castle(ply, side) < 0 || !(Castle(ply, side) & 2))
temp ^= castle_random[1][side];
}
if (temp ^ HashKey) {
if (!error)
Print(2048, "\n");
Print(2048, "ERROR! hash_key is bad.\n");
error = 1;
}
if (temp1 ^ PawnHashKey) {
if (!error)
Print(2048, "\n");
Print(2048, "ERROR! pawn_hash_key is bad.\n");
error = 1;
}
/*
************************************************************
* *
* If any inconsistencies/errors were found, we are going *
* to dump as much debugging information as possible to *
* help pinpoint the source of the problem. *
* *
************************************************************
*/
if (error) {
Lock(lock_smp);
Unlock(lock_smp);
Print(2048, "ply=%d\n", tree->ply);
Print(2048, "phase[%d]=%d current move:\n", ply, tree->phase[ply]);
DisplayChessMove("move=", move);
DisplayChessBoard(stdout, tree->position);
Print(2048, "called from %s, ply=%d\n", caller, ply);
Print(2048, "node=%" PRIu64 "\n", tree->nodes_searched);
Print(2048, "active path:\n");
for (i = 1; i <= ply; i++) {
Print(2048, "ply=%d ", i);
DisplayChessMove("move=", tree->curmv[i]);
}
CraftyExit(1);
}
}
#endif