1 #if defined(DEBUG)
2 #  include "chess.h"
3 #  include "data.h"
4 /* last modified 02/26/14 */
5 /*
6  *******************************************************************************
7  *                                                                             *
8  *   ValidatePosition() is a debugging tool that is enabled by using the       *
9  *   -DDEBUG compilation flag.  This procedure tests the various data          *
10  *   structures used in Crafty related to the chess board and incrementally    *
11  *   updated values like hash signatures and so forth.  It simply looks for    *
12  *   consistency between the various bitboards, and recomputes the hash        *
13  *   signatures to determine if they are correct.  If anything fails to pass   *
14  *   the validation test, we print out a dump of the moves made in this path   *
15  *   through the tree, and then exit since things are corrupted.               *
16  *                                                                             *
17  *   This greatly slows the program down, because ValidatePosition() is called *
18  *   after each Make()/Unmake() (these are the functions that modify the       *
19  *   primary data structures).  In general, this will not be used by users     *
20  *   unless they are modifying the source code themselves.                     *
21  *                                                                             *
22  *******************************************************************************
23  */
ValidatePosition(TREE * RESTRICT tree,int ply,int move,char * caller)24 void ValidatePosition(TREE * RESTRICT tree, int ply, int move, char *caller) {
25   uint64_t temp, temp1, temp_occ;
26   uint64_t temp_occx;
27   int i, square, error = 0;
28   int side, piece, temp_score;
29 
30 /*
31  ************************************************************
32  *                                                          *
33  *  First, test occupied[side] which should match the OR    *
34  *  result of all pieces[side].                             *
35  *                                                          *
36  ************************************************************
37  */
38   for (side = black; side <= white; side++) {
39     temp_occ =
40         Pawns(side) | Knights(side) | Bishops(side) | Rooks(side) |
41         Queens(side)
42         | Kings(side);
43     if (Occupied(side) ^ temp_occ) {
44       if (!error)
45         Print(2048, "\n");
46       Print(2048, "ERROR %s occupied squares is bad!\n",
47           (side) ? "white" : "black");
48       Display2BitBoards(temp_occ, Occupied(white));
49       error = 1;
50     }
51   }
52 /*
53  ************************************************************
54  *                                                          *
55  *  Now we do some sanity tests on the actual chess board   *
56  *  information.  The first test is to make sure that no    *
57  *  bitmap square is set in more than one bitmap, which     *
58  *  would imply two different pieces on the same square.    *
59  *                                                          *
60  ************************************************************
61  */
62   temp_occ =
63       Pawns(white) ^ Knights(white) ^ Bishops(white) ^ Rooks(white) ^
64       Queens(white) ^ Pawns(black) ^ Knights(black) ^ Bishops(black) ^
65       Rooks(black) ^ Queens(black) ^ Kings(white) ^ Kings(black);
66   temp_occx =
67       Pawns(white) | Knights(white) | Bishops(white) | Rooks(white) |
68       Queens(white) | Pawns(black) | Knights(black) | Bishops(black) |
69       Rooks(black) | Queens(black) | Kings(white) | Kings(black);
70   if (temp_occ ^ temp_occx) {
71     if (!error)
72       Print(2048, "\n");
73     Print(2048, "ERROR two pieces on same square\n");
74     error = 1;
75   }
76 /*
77  ************************************************************
78  *                                                          *
79  *  Add up all the pieces (material values) to see if this  *
80  *  matches the incrementally updated value.                *
81  *                                                          *
82  ************************************************************
83  */
84   temp_score = 0;
85   for (side = black; side <= white; side++)
86     for (piece = pawn; piece < king; piece++)
87       temp_score += PopCnt(Pieces(side, piece)) * PieceValues(side, piece);
88   if (temp_score != Material) {
89     if (!error)
90       Print(2048, "\n");
91     Print(2048, "ERROR  material evaluation is wrong, good=%d, bad=%d\n",
92         temp_score, Material);
93     error = 1;
94   }
95 /*
96  ************************************************************
97  *                                                          *
98  *  Next, check the incrementally updated piece counts for  *
99  *  both sides.  ditto for pawn counts.                     *
100  *                                                          *
101  ************************************************************
102  */
103   for (side = black; side <= white; side++) {
104     temp_score = 0;
105     for (piece = knight; piece < king; piece++)
106       temp_score += PopCnt(Pieces(side, piece)) * p_vals[piece];
107     if (temp_score != TotalPieces(side, occupied)) {
108       if (!error)
109         Print(2048, "\n");
110       Print(2048, "ERROR  %s pieces is wrong, good=%d, bad=%d\n",
111           (side) ? "white" : "black", temp_score, TotalPieces(side,
112               occupied));
113       error = 1;
114     }
115   }
116   for (side = black; side <= white; side++) {
117     temp_score = PopCnt(Pawns(side));
118     if (temp_score != TotalPieces(side, pawn)) {
119       if (!error)
120         Print(2048, "\n");
121       Print(2048, "ERROR  %s pawns is wrong, good=%d, bad=%d\n",
122           (side) ? "white" : "black", temp_score, TotalPieces(side, pawn));
123       error = 1;
124     }
125   }
126   i = PopCnt(OccupiedSquares);
127   if (i != TotalAllPieces) {
128     if (!error)
129       Print(2048, "\n");
130     Print(2048, "ERROR!  TotalAllPieces is wrong, correct=%d  bad=%d\n", i,
131         TotalAllPieces);
132     error = 1;
133   }
134 /*
135  ************************************************************
136  *                                                          *
137  *  Now we cycle through each different chessboard bitmap   *
138  *  and verify that each piece in a bitmap matches the same *
139  *  piece type in the board[64] array.                      *
140  *                                                          *
141  ************************************************************
142  */
143   for (side = black; side <= white; side++)
144     for (piece = pawn; piece <= king; piece++) {
145       temp = Pieces(side, piece);
146       while (temp) {
147         square = LSB(temp);
148         if (PcOnSq(square) != pieces[side][piece]) {
149           if (!error)
150             Print(2048, "\n");
151           Print(2048, "ERROR!  board[%d]=%d, should be %d\n", square,
152               PcOnSq(square), pieces[side][piece]);
153           error = 1;
154         }
155         temp &= temp - 1;
156       }
157     }
158 /*
159  ************************************************************
160  *                                                          *
161  *  And then we look at the board[64] array and make sure   *
162  *  that any non-zero piece matches the proper bitmap for   *
163  *  that particular piece type.                             *
164  *                                                          *
165  ************************************************************
166  */
167   for (i = 0; i < 64; i++) {
168     if (!PcOnSq(i))
169       continue;
170     side = (PcOnSq(i) > 0) ? 1 : 0;
171     if (SetMask(i) & Pieces(side, Abs(PcOnSq(i))))
172       continue;
173     if (!error)
174       Print(2048, "\n");
175     Print(2048, "ERROR!  bitboards/board[%d] don't agree!\n", i);
176     error = 1;
177     break;
178   }
179 /*
180  ************************************************************
181  *                                                          *
182  *  The last chess board test is to make sure that any      *
183  *  square that is empty according to board[64] is also     *
184  *  empty according to the occupied squares bitmap.         *
185  *                                                          *
186  ************************************************************
187  */
188   temp = ~(temp_occ | temp_occx);
189   while (temp) {
190     square = LSB(temp);
191     if (PcOnSq(square)) {
192       if (!error)
193         Print(2048, "\n");
194       Print(2048, "ERROR!  board[%d]=%d, should be 0\n", square,
195           PcOnSq(square));
196       error = 1;
197     }
198     temp &= temp - 1;
199   }
200 /*
201  ************************************************************
202  *                                                          *
203  *  Finally, we re-compute the pawn hash signature and the  *
204  *  normal hash signature and verify that they match the    *
205  *  incrementally updated values.                           *
206  *                                                          *
207  ************************************************************
208  */
209   temp = 0;
210   temp1 = 0;
211   for (i = 0; i < 64; i++) {
212     side = (PcOnSq(i) > 0) ? 1 : 0;
213     temp ^= randoms[side][Abs(PcOnSq(i))][i];
214     if (Abs(PcOnSq(i)) == pawn)
215       temp1 ^= randoms[side][Abs(PcOnSq(i))][i];
216   }
217   if (EnPassant(ply))
218     temp ^= enpassant_random[EnPassant(ply)];
219   for (side = black; side <= white; side++) {
220     if (Castle(ply, side) < 0 || !(Castle(ply, side) & 1))
221       temp ^= castle_random[0][side];
222     if (Castle(ply, side) < 0 || !(Castle(ply, side) & 2))
223       temp ^= castle_random[1][side];
224   }
225   if (temp ^ HashKey) {
226     if (!error)
227       Print(2048, "\n");
228     Print(2048, "ERROR!  hash_key is bad.\n");
229     error = 1;
230   }
231   if (temp1 ^ PawnHashKey) {
232     if (!error)
233       Print(2048, "\n");
234     Print(2048, "ERROR!  pawn_hash_key is bad.\n");
235     error = 1;
236   }
237 /*
238  ************************************************************
239  *                                                          *
240  *  If any inconsistencies/errors were found, we are going  *
241  *  to dump as much debugging information as possible to    *
242  *  help pinpoint the source of the problem.                *
243  *                                                          *
244  ************************************************************
245  */
246   if (error) {
247     Lock(lock_smp);
248     Unlock(lock_smp);
249     Print(2048, "ply=%d\n", tree->ply);
250     Print(2048, "phase[%d]=%d  current move:\n", ply, tree->phase[ply]);
251     DisplayChessMove("move=", move);
252     DisplayChessBoard(stdout, tree->position);
253     Print(2048, "called from %s, ply=%d\n", caller, ply);
254     Print(2048, "node=%" PRIu64 "\n", tree->nodes_searched);
255     Print(2048, "active path:\n");
256     for (i = 1; i <= ply; i++) {
257       Print(2048, "ply=%d  ", i);
258       DisplayChessMove("move=", tree->curmv[i]);
259     }
260     CraftyExit(1);
261   }
262 }
263 #endif
264