1 #include "chess.h"
2 #include "data.h"
3 /* last modified 01/06/16 */
4 /*
5 *******************************************************************************
6 * *
7 * MakeMove() is responsible for updating the position database whenever a *
8 * piece is moved. It performs the following operations: (1) update the *
9 * board structure itself by moving the piece and removing any captured *
10 * piece. (2) update the hash keys. (3) update material counts. (4) then *
11 * update castling status. (5) and finally update number of moves since *
12 * last reversible move. *
13 * *
14 * There are some special-cases handled here, such as en passant captures *
15 * where the enemy pawn is not on the <target> square, castling which moves *
16 * both the king and rook, and then rook moves/captures which give up the *
17 * castling right to that side when the rook is moved. *
18 * *
19 * note: side = 1 if white is to move, 0 otherwise. enemy is the opposite *
20 * and is 1 if it is not white to move, 0 otherwise. *
21 * *
22 *******************************************************************************
23 */
MakeMove(TREE * RESTRICT tree,int ply,int side,int move)24 void MakeMove(TREE * RESTRICT tree, int ply, int side, int move) {
25 uint64_t bit_move;
26 int piece, from, to, captured, promote, enemy = Flip(side), cpiece;
27 #if defined(DEBUG)
28 int i;
29 #endif
30
31 /*
32 ************************************************************
33 * *
34 * First, some basic information is updated for all moves *
35 * before we do the piece-specific stuff. We need to save *
36 * the current position and both hash signatures, and add *
37 * the current position to the repetition-list for the *
38 * side on move, before the move is actually made on the *
39 * board. We also update the 50 move rule counter which *
40 * will be reset if a capture or pawn move is made here. *
41 * *
42 * If the en passant flag was set the previous ply, we *
43 * have already used it to generate moves at this ply and *
44 * we need to clear it before continuing. If it is set, *
45 * we also need to update the hash signature since the EP *
46 * opportunity no longer exists after making any move at *
47 * this ply (one ply deeper than when a pawn was advanced *
48 * two squares). *
49 * *
50 ************************************************************
51 */
52 #if defined(DEBUG)
53 ValidatePosition(tree, ply, move, "MakeMove(1)");
54 #endif
55 tree->status[ply + 1] = tree->status[ply];
56 tree->save_hash_key[ply] = HashKey;
57 tree->save_pawn_hash_key[ply] = PawnHashKey;
58 if (EnPassant(ply + 1)) {
59 HashEP(EnPassant(ply + 1));
60 EnPassant(ply + 1) = 0;
61 }
62 Reversible(ply + 1)++;
63 /*
64 ************************************************************
65 * *
66 * Now do the things that are common to all pieces, such *
67 * as updating the bitboards and hash signature. *
68 * *
69 ************************************************************
70 */
71 piece = Piece(move);
72 from = From(move);
73 to = To(move);
74 captured = Captured(move);
75 promote = Promote(move);
76 bit_move = SetMask(from) | SetMask(to);
77 cpiece = PcOnSq(to);
78 ClearSet(bit_move, Pieces(side, piece));
79 ClearSet(bit_move, Occupied(side));
80 Hash(side, piece, from);
81 Hash(side, piece, to);
82 PcOnSq(from) = 0;
83 PcOnSq(to) = pieces[side][piece];
84 /*
85 ************************************************************
86 * *
87 * Now do the piece-specific things by jumping to the *
88 * appropriate routine. *
89 * *
90 ************************************************************
91 */
92 switch (piece) {
93 case pawn:
94 HashP(side, from);
95 HashP(side, to);
96 Reversible(ply + 1) = 0;
97 if (captured == 1 && !cpiece) {
98 Clear(to + epsq[side], Pawns(enemy));
99 Clear(to + epsq[side], Occupied(enemy));
100 Hash(enemy, pawn, to + epsq[side]);
101 HashP(enemy, to + epsq[side]);
102 PcOnSq(to + epsq[side]) = 0;
103 Material -= PieceValues(enemy, pawn);
104 TotalPieces(enemy, pawn)--;
105 TotalAllPieces--;
106 captured = 0;
107 }
108 if (promote) {
109 TotalPieces(side, pawn)--;
110 Material -= PieceValues(side, pawn);
111 Clear(to, Pawns(side));
112 Hash(side, pawn, to);
113 HashP(side, to);
114 Hash(side, promote, to);
115 PcOnSq(to) = pieces[side][promote];
116 TotalPieces(side, occupied) += p_vals[promote];
117 TotalPieces(side, promote)++;
118 Material += PieceValues(side, promote);
119 Set(to, Pieces(side, promote));
120 } else if ((Abs(to - from) == 16) && (mask_eptest[to] & Pawns(enemy))) {
121 EnPassant(ply + 1) = to + epsq[side];
122 HashEP(to + epsq[side]);
123 }
124 break;
125 case knight:
126 case bishop:
127 case queen:
128 break;
129 case rook:
130 if (Castle(ply + 1, side) > 0) {
131 if ((from == rook_A[side]) && (Castle(ply + 1, side) & 2)) {
132 Castle(ply + 1, side) &= 1;
133 HashCastle(1, side);
134 } else if ((from == rook_H[side]) && (Castle(ply + 1, side) & 1)) {
135 Castle(ply + 1, side) &= 2;
136 HashCastle(0, side);
137 }
138 }
139 break;
140 case king:
141 KingSQ(side) = to;
142 if (Castle(ply + 1, side) > 0) {
143 if (Castle(ply + 1, side) & 2)
144 HashCastle(1, side);
145 if (Castle(ply + 1, side) & 1)
146 HashCastle(0, side);
147 if (Abs(to - from) == 2) {
148 Castle(ply + 1, side) = -1;
149 piece = rook;
150 if (to == rook_G[side]) {
151 from = rook_H[side];
152 to = rook_F[side];
153 } else {
154 from = rook_A[side];
155 to = rook_D[side];
156 }
157 bit_move = SetMask(from) | SetMask(to);
158 ClearSet(bit_move, Rooks(side));
159 ClearSet(bit_move, Occupied(side));
160 Hash(side, rook, from);
161 Hash(side, rook, to);
162 PcOnSq(from) = 0;
163 PcOnSq(to) = pieces[side][rook];
164 } else
165 Castle(ply + 1, side) = 0;
166 }
167 break;
168 }
169 /*
170 ************************************************************
171 * *
172 * If this is a capture move, we also have to update the *
173 * information that must change when a piece is removed *
174 * from the board. *
175 * *
176 ************************************************************
177 */
178 if (captured) {
179 Reversible(ply + 1) = 0;
180 TotalAllPieces--;
181 if (promote)
182 piece = promote;
183 Hash(enemy, captured, to);
184 Clear(to, Pieces(enemy, captured));
185 Clear(to, Occupied(enemy));
186 Material -= PieceValues(enemy, captured);
187 TotalPieces(enemy, captured)--;
188 if (captured != pawn)
189 TotalPieces(enemy, occupied) -= p_vals[captured];
190 switch (captured) {
191 case pawn:
192 HashP(enemy, to);
193 break;
194 case knight:
195 case bishop:
196 case queen:
197 break;
198 case rook:
199 if (Castle(ply + 1, enemy) > 0) {
200 if ((to == rook_A[enemy]) && (Castle(ply + 1, enemy) & 2)) {
201 Castle(ply + 1, enemy) &= 1;
202 HashCastle(1, enemy);
203 } else if ((to == rook_H[enemy]) && (Castle(ply + 1, enemy) & 1)) {
204 Castle(ply + 1, enemy) &= 2;
205 HashCastle(0, enemy);
206 }
207 }
208 break;
209 case king:
210 #if defined(DEBUG)
211 Print(2048, "captured a king (Make)\n");
212 for (i = 1; i <= ply; i++)
213 Print(2048,
214 "ply=%2d, phase=%d, piece=%2d,from=%2d,to=%2d,captured=%2d\n",
215 i, tree->phase[i], Piece(tree->curmv[i]), From(tree->curmv[i]),
216 To(tree->curmv[i]), Captured(tree->curmv[i]));
217 Print(2048, "ply=%2d, piece=%2d,from=%2d,to=%2d,captured=%2d\n", i,
218 piece, from, to, captured);
219 if (log_file)
220 DisplayChessBoard(log_file, tree->position);
221 #endif
222 break;
223 }
224 }
225 #if defined(DEBUG)
226 ValidatePosition(tree, ply + 1, move, "MakeMove(2)");
227 #endif
228 return;
229 }
230
231 /* last modified 01/06/16 */
232 /*
233 *******************************************************************************
234 * *
235 * MakeMoveRoot() is used to make a move at the root of the game tree, *
236 * before any searching is done. It uses MakeMove() to execute the move, *
237 * but then copies the resulting position back to position[0], the actual *
238 * board position. It handles the special-case of the draw-by-repetition *
239 * rule by clearing the repetition list when a non-reversible move is made, *
240 * since no repetitions are possible once such a move is played. *
241 * *
242 *******************************************************************************
243 */
MakeMoveRoot(TREE * RESTRICT tree,int side,int move)244 void MakeMoveRoot(TREE * RESTRICT tree, int side, int move) {
245 int player;
246
247 /*
248 ************************************************************
249 * *
250 * First, make the move and then reset the repetition *
251 * index if the 50 move rule counter was reset to zero. *
252 * *
253 ************************************************************
254 */
255 MakeMove(tree, 0, side, move);
256 if (Reversible(1) == 0)
257 rep_index = -1;
258 tree->rep_list[++rep_index] = HashKey;
259 /*
260 ************************************************************
261 * *
262 * One odd action is to note if the castle status is *
263 * currently negative, which indicates that that side *
264 * castled during the previous search. We simply set the *
265 * castle status for that side to zero and we are done. *
266 * *
267 * We then copy this back to ply=0 status (which is the *
268 * permanent game-board ply). *
269 * *
270 ************************************************************
271 */
272 for (player = black; player <= white; player++)
273 Castle(1, player) = Max(0, Castle(1, player));
274 tree->status[0] = tree->status[1];
275 }
276