search: fixed a bug in NULL move pruning
[owl.git] / move.c
blob38e5b772c5f4420cad0234c592a61478f5e978db
1 /*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
16 #include "common.h"
17 #include "attacks.h"
18 #include "board.h"
19 #include "engine.h"
20 #include "move.h"
22 const char pieces[] = {' ', 'p', 'n', 'b', 'r', 'q', 'k'};
24 /* move in coordinate notation */
25 char *
26 coord_move(int move, char *buf)
28 assert(move);
30 if (GET_PROMOTE(move))
31 snprintf(buf, 6, "%s%s%c", char_board[GET_FROM(move)], \
32 char_board[GET_TO(move)], pieces[GET_PROMOTE(move)]);
33 else
34 snprintf(buf, 5, "%s%s", char_board[GET_FROM(move)], \
35 char_board[GET_TO(move)]);
37 return buf;
40 /* PV in coordinate notation */
41 char *
42 get_pv_string_coord(char *buf)
44 int i;
45 char move_buf[256];
47 buf[0] = '\0';
48 for (i = 0; i < pv_length[0]; i++) {
49 strcat(buf, coord_move(pv[0][i], move_buf));
50 strcat(buf, " ");
52 strcat(buf, "\n");
54 return buf;
57 /* PV in SAN notation */
58 char *
59 get_pv_string_san(char *buf)
61 int i;
62 char move_buf[256];
63 char move_buf2[256];
65 if (pv_length[0]) {
66 if (brd.wtm)
67 sprintf(buf, "%d. ... ", (game_history.count + 1) / 2 + \
68 (game_history.count ? 0 : 1));
69 else
70 sprintf(buf, "%d. ", (game_history.count + 1) / 2 + 1);
72 for (i = 0; i < pv_length[0]; i++) {
73 if (i && !brd.wtm) {
74 sprintf(move_buf, "%d. ", (game_history.count + 1) / 2 + 1);
75 strcat(buf, move_buf);
77 sprintf(move_buf, "%s ", san_move(pv[0][i], move_buf2));
78 strcat(buf, move_buf);
80 make_move(pv[0][i], FALSE);
82 strcat(buf, "\n");
84 for (i = 0; i < pv_length[0]; i++)
85 takeback();
86 } else
87 assert(0 == 1); /* no PV! */
89 return buf;
92 /* make move and update board info */
93 int
94 make_move(int move, int tb)
96 int ep_square;
97 int to, from, type;
98 int cap_piece, piece_from, piece_to;
99 int go_castle;
100 int xwtm;
102 assert(board_is_legal(&brd));
103 assert(move_is_legal(move));
105 from = GET_FROM(move); /* 'from' coordinate */
106 to = GET_TO(move); /* 'to' coordinate */
107 type = GET_TYPE(move); /* type flags */
108 go_castle = FALSE;
109 xwtm = 1 ^ brd.wtm;
111 if (type & TYPE_CASTLE) {
112 /* check if castle move is legal here */
113 if (to == G1) {
114 if (IS_ATTACKED(E1, BLACK) || IS_ATTACKED(F1, BLACK) || \
115 IS_ATTACKED(G1, BLACK))
116 return FALSE;
117 } else if (to == C1) {
118 if (IS_ATTACKED(E1, BLACK) || IS_ATTACKED(D1, BLACK) || \
119 IS_ATTACKED(C1, BLACK))
120 return FALSE;
121 } else if (to == G8) {
122 if (IS_ATTACKED(E8, WHITE) || IS_ATTACKED(F8, WHITE) || \
123 IS_ATTACKED(G8, WHITE))
124 return FALSE;
125 } else if (to == C8) {
126 if (IS_ATTACKED(E8, WHITE) || IS_ATTACKED(D8, WHITE) || \
127 IS_ATTACKED(C8, WHITE))
128 return FALSE;
130 go_castle = TRUE;
133 cap_piece = brd.cap_piece = square64[to];
134 brd.move = move;
136 /* copy parameters for takeback */
137 memcpy(&game_history.board[game_history.count++], &brd, \
138 sizeof(struct board_t));
140 piece_from = square64[from];
142 if (brd.ep != -1)
143 brd.hash_key ^= hash_ep[_FILE(brd.ep)]; /* clear ep hash */
145 if (cap_piece)
146 /* remove captured piece */
147 remove_piece(to, cap_piece, xwtm);
149 /* handle promote move */
150 piece_to = (type & TYPE_PROMOTE) ? GET_PROMOTE(move) : piece_from;
152 remove_piece(from, piece_from, brd.wtm);
153 add_piece(to, piece_to, brd.wtm);
155 if (type & TYPE_ENPASSANT) {
156 /* remove enpassant victim */
157 ep_square = to - (brd.wtm? -1 : 1) * 8;
158 remove_piece(ep_square, PAWN, xwtm);
161 brd.ep = -1; /* remove ep status by default */
162 if (type & TYPE_PAWN_TWO)
163 /* polyglot-compatibility hack! if there is no pawn on enpassant
164 target square skip ep */
165 if ((BIT(to - 1) & PAWNS(xwtm)) || (BIT(to + 1) & PAWNS(xwtm))) {
166 brd.ep = (to + from) / 2;
167 brd.hash_key ^= hash_ep[_FILE(brd.ep)];
170 if (go_castle) {
171 if (to == G1) {
172 add_piece(F1, ROOK, brd.wtm);
173 remove_piece(H1, ROOK, brd.wtm);
174 castled[WHITE] = TRUE;
175 } else if (to == C1) {
176 add_piece(D1, ROOK, brd.wtm);
177 remove_piece(A1, ROOK, brd.wtm);
178 castled[WHITE] = TRUE;
179 } else if (to == G8) {
180 add_piece(F8, ROOK, brd.wtm);
181 remove_piece(H8, ROOK, brd.wtm);
182 castled[BLACK] = TRUE;
183 } else if (to == C8) {
184 add_piece(D8, ROOK, brd.wtm);
185 remove_piece(A8, ROOK, brd.wtm);
186 castled[BLACK] = TRUE;
190 if (brd.castle_mask) {
191 if (from == E1) {
192 if (brd.castle_mask & WHITE_CASTLE_KINGSIDE)
193 brd.hash_key ^= hash_wck;
194 if (brd.castle_mask & WHITE_CASTLE_QUEENSIDE)
195 brd.hash_key ^= hash_wcq;
196 brd.castle_mask &= ~(WHITE_CASTLE_KINGSIDE | WHITE_CASTLE_QUEENSIDE);
197 } else if (from == E8) {
198 if (brd.castle_mask & BLACK_CASTLE_KINGSIDE)
199 brd.hash_key ^= hash_bck;
200 if (brd.castle_mask & BLACK_CASTLE_QUEENSIDE)
201 brd.hash_key ^= hash_bcq;
202 brd.castle_mask &= ~(BLACK_CASTLE_KINGSIDE | BLACK_CASTLE_QUEENSIDE);
203 } else {
204 if (from == H1 || to == H1) {
205 if (brd.castle_mask & WHITE_CASTLE_KINGSIDE)
206 brd.hash_key ^= hash_wck;
207 brd.castle_mask &= ~WHITE_CASTLE_KINGSIDE;
209 if (from == A1 || to == A1) {
210 if (brd.castle_mask & WHITE_CASTLE_QUEENSIDE)
211 brd.hash_key ^= hash_wcq;
212 brd.castle_mask &= ~WHITE_CASTLE_QUEENSIDE;
214 if (from == H8 || to == H8) {
215 if (brd.castle_mask & BLACK_CASTLE_KINGSIDE)
216 brd.hash_key ^= hash_bck;
217 brd.castle_mask &= ~BLACK_CASTLE_KINGSIDE;
219 if (from == A8 || to == A8) {
220 if (brd.castle_mask & BLACK_CASTLE_QUEENSIDE)
221 brd.hash_key ^= hash_bcq;
222 brd.castle_mask &= ~BLACK_CASTLE_QUEENSIDE;
226 /* if the move is not a capture or pawn push, increase fifty_rule */
227 brd.fifty_rule = (!type || type == TYPE_CASTLE)? brd.fifty_rule + 1 : 0;
229 brd.wtm = xwtm;
230 brd.hash_key ^= hash_side;
232 if (IS_CHECKED(1 ^ brd.wtm)) {
233 takeback();
234 return FALSE;
235 } else {
236 if (tb)
237 takeback();
238 counters.searched_nodes++;
239 return TRUE;
243 /* used for sanity check */
244 int
245 move_is_legal(int move)
247 int from = GET_FROM(move);
248 int to = GET_TO(move);
250 if (to == from)
251 return FALSE;
253 int piece_from = square64[from];
254 int piece_to = square64[to];
256 if (!piece_from)
257 return FALSE;
259 if ((BIT(from) & piece_boards[brd.wtm][piece_from]) == 0)
260 return FALSE;
262 if (piece_to) {
263 if (BIT(to) & side_boards[brd.wtm])
264 return FALSE;
265 if (piece_to == KING)
266 return FALSE;
269 return TRUE;
272 /* return internal binary move representation from coordinate */
273 int
274 parse_move_coord(char *move)
276 char buf[256];
277 int i;
278 struct moves_t moves;
280 gen_legal_moves(&moves);
282 /* generate pseudolegal moves and find target move */
283 for (i = 0; i < moves.count; i++) {
284 if (!strcmp(move, coord_move(moves.move[i], buf)))
285 return moves.move[i];
287 return 0;
290 /* return internal binary move representation from SAN */
291 int
292 parse_move_san(char *move)
294 int i;
295 char buf[MAX_STRING];
296 struct moves_t moves;
298 gen_legal_moves(&moves);
300 /* generate pseudolegal moves and find target move */
301 for (i = 0; i < moves.count; i++) {
302 if (!strncmp(move, san_move(moves.move[i], buf), 6))
303 return moves.move[i];
306 return 0;
309 void
310 print_move_coord(int move)
312 char buf[256];
313 printf("%s\n", coord_move(move, buf));
316 void
317 print_moves_coord(struct moves_t *moves)
319 int i;
320 char buf[256];
322 for (i = 0; i < moves->count; i++)
323 printf("%s ", coord_move(moves->move[i], buf));
324 printf("\n");
327 void
328 print_moves_san(struct moves_t *moves)
330 int i;
331 char buf[256];
333 for (i = 0; i < moves->count; i++) {
334 if (make_move(moves->move[i], TRUE))
335 printf("%s ", san_move(moves->move[i], buf));
337 printf("\n");
340 /* undo move */
341 void
342 takeback(void)
344 int c;
345 int cap_piece;
346 int move;
347 int from, to, type;
348 int to_piece;
349 int xwtm;
351 assert(board_is_legal(&brd));
353 c = --game_history.count;
354 cap_piece = game_history.board[c].cap_piece;
355 move = game_history.board[c].move;
357 brd.castle_mask = game_history.board[c].castle_mask;
358 brd.ep = game_history.board[c].ep;
360 /* do the following if move is not 'NULL move' */
361 if (move) {
362 from = GET_FROM(move);
363 to = GET_TO(move);
364 type = GET_TYPE(move);
365 to_piece = square64[to];
366 xwtm = 1 ^ brd.wtm;
368 remove_piece(to, to_piece, xwtm);
370 if (cap_piece) {
371 /* restore captured piece */
372 add_piece(to, cap_piece, brd.wtm);
373 add_piece(from, (type & TYPE_PROMOTE)? PAWN : to_piece, xwtm);
374 } else if (type & TYPE_ENPASSANT) {
375 /* restore captured by enpassant pawn */
376 add_piece(brd.wtm? to - 8 : to + 8, PAWN, brd.wtm);
377 add_piece(from, PAWN, xwtm);
378 } else if (type & TYPE_PROMOTE)
379 add_piece(from, PAWN, xwtm);
380 else
381 add_piece(from, to_piece, xwtm);
383 /* undo castle */
384 if (type & TYPE_CASTLE) {
385 if (to == G1) {
386 add_piece(H1, ROOK, xwtm);
387 remove_piece(F1, ROOK, xwtm);
388 castled[WHITE] = FALSE;
389 } else if (to == C1) {
390 add_piece(A1, ROOK, xwtm);
391 remove_piece(D1, ROOK, xwtm);
392 castled[WHITE] = FALSE;
393 } else if (to == G8) {
394 add_piece(H8, ROOK, xwtm);
395 remove_piece(F8, ROOK, xwtm);
396 castled[BLACK] = FALSE;
397 } else if (to == C8) {
398 add_piece(A8, ROOK, xwtm);
399 remove_piece(D8, ROOK, xwtm);
400 castled[BLACK] = FALSE;
404 brd.wtm = 1 ^ brd.wtm;
405 brd.hash_key = game_history.board[c].hash_key;
406 brd.hash_pawn_key = game_history.board[c].hash_pawn_key;
407 brd.fifty_rule = game_history.board[c].fifty_rule;