Update copyright year to 2015.
[cboard.git] / libchess / move.c
blob0237c4c7c38d48f37e7671803d5a8321a6a5ff32
1 /* vim:tw=78:ts=8:sw=4:set ft=c: */
2 /*
3 Copyright (C) 2002-2015 Ben Kibbey <bjk@luxsci.net>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 #ifdef HAVE_CONFIG_H
20 #include <config.h>
21 #endif
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <ctype.h>
28 #include "chess.h"
29 #include "common.h"
31 #ifdef DEBUG
32 #include "debug.h"
33 #endif
35 enum {
36 CHECK = 1,
37 CHECK_SELF,
38 CHECK_MATE
41 enum {
42 KINGSIDE = 1, QUEENSIDE
45 static int finalize_move(GAME g, BOARD b, int promo, int sfile, int srank,
46 int file, int rank);
47 static int find_source_square(GAME, BOARD, int, int *, int *, int, int);
48 static int check_self(GAME g, BOARD b, int file, int rank);
49 static int validate_pawn(GAME g, BOARD b, int sfile, int srank, int file,
50 int rank);
51 static int find_source_square(GAME, BOARD, int, int *, int *, int, int);
53 static int val_piece_side(char turn, int c)
55 if ((isupper(c) && turn == WHITE) ||
56 (islower(c) && turn == BLACK))
57 return 1;
59 return 0;
62 static int count_piece(GAME g, BOARD b, int piece, int sfile,
63 int srank, int file, int rank,
64 int *count)
66 int p, pi;
68 if (!VALIDRANK(rank) || !VALIDFILE(file))
69 return 0;
71 p = b[RANKTOBOARD(rank)][FILETOBOARD(file)].icon;
72 pi = pgn_piece_to_int(p);
74 if (pi != OPEN_SQUARE) {
75 if (pi == piece && val_piece_side(g->turn, p)) {
76 if (sfile && file == sfile) {
77 if (!srank || (srank && rank == srank))
78 (*count)++;
80 else if (srank && rank == srank) {
81 if (!sfile)
82 (*count)++;
84 else if (!sfile && !srank)
85 (*count)++;
88 return 1;
91 return 0;
95 * Get the source row and column for a given piece.
97 * The following two functions find 'piece' from the given square 'col' and
98 * 'row' and store the resulting column or row in 'c' and 'r'. The return
99 * value is the number of 'piece' found (on the current g->side) or zero.
100 * Search for 'piece' stops when a non-empty square is found.
102 static int count_by_diag(GAME g, BOARD b, int piece,
103 int sfile, int srank, int file,
104 int rank)
106 int count = 0;
107 int ul = 0, ur = 0, dl = 0, dr = 0;
108 int i;
109 int f, r;
111 for (i = 1; VALIDFILE(i); i++) {
112 r = rank + i;
113 f = file - i;
115 if (!ul && VALIDRANK(r) && VALIDFILE(f))
116 ul = count_piece(g, b, piece, sfile, srank, f, r, &count);
118 r = rank + i;
119 f = file + i;
121 if (!ur && VALIDRANK(r) && VALIDFILE(f))
122 ur = count_piece(g, b, piece, sfile, srank, f, r, &count);
124 r = rank - i;
125 f = file - i;
127 if (!dl && VALIDRANK(r) && VALIDFILE(f))
128 dl = count_piece(g, b, piece, sfile, srank, f, r, &count);
130 r = rank - i;
131 f = file + i;
133 if (!dr && VALIDRANK(r) && VALIDFILE(f))
134 dr = count_piece(g, b, piece, sfile, srank, f, r, &count);
137 return count;
140 static int count_knight(GAME g, BOARD b, int piece, int sfile, int srank,
141 int file, int rank)
143 int count = 0;
145 count_piece(g, b, piece, sfile, srank, file - 1, rank + 2, &count);
146 count_piece(g, b, piece, sfile, srank, file + 1, rank + 2, &count);
147 count_piece(g, b, piece, sfile, srank, file + 2, rank + 1, &count);
148 count_piece(g, b, piece, sfile, srank, file - 2, rank + 1, &count);
149 count_piece(g, b, piece, sfile, srank, file + 1, rank - 2, &count);
150 count_piece(g, b, piece, sfile, srank, file - 1, rank - 2, &count);
151 count_piece(g, b, piece, sfile, srank, file + 2, rank - 1, &count);
152 count_piece(g, b, piece, sfile, srank, file - 2, rank - 1, &count);
153 return count;
156 static int count_by_rank(GAME g, BOARD b, int piece,
157 int sfile, int srank, int file,
158 int rank)
160 int i;
161 int count = 0;
162 int u = 0, d = 0;
164 for (i = 1; VALIDRANK(i); i++) {
165 if (!u && VALIDRANK((rank + i)))
166 u = count_piece(g, b, piece, sfile, srank, file, rank + i, &count);
168 if (!d && VALIDRANK((rank - i)))
169 d = count_piece(g, b, piece, sfile, srank, file, rank - i, &count);
171 if (d && u)
172 break;
175 return count;
178 static int count_by_file(GAME g, BOARD b, int piece,
179 int sfile, int srank, int file,
180 int rank)
182 int i;
183 int count = 0;
184 int l = 0, r = 0;
186 for (i = 1; VALIDFILE(i); i++) {
187 if (!r && VALIDFILE((file + i)))
188 r = count_piece(g, b, piece, sfile, srank, file + i, rank, &count);
190 if (!l && VALIDFILE((file - i)))
191 l = count_piece(g, b, piece, sfile, srank, file - i, rank, &count);
193 if (r && l)
194 break;
197 return count;
200 static int count_by_rank_file(GAME g, BOARD b, int piece,
201 int sfile, int srank, int file,
202 int rank)
204 int count;
206 count = count_by_rank(g, b, piece, sfile, srank, file, rank);
207 return count + count_by_file(g, b, piece, sfile, srank, file, rank);
210 static int opponent_can_attack(GAME g, BOARD b, int file,
211 int rank)
213 int f, r;
214 int p, pi;
215 int kf = g->kfile, kr = g->krank;
217 pgn_switch_turn(g);
218 g->kfile = g->okfile, g->krank = g->okrank;
220 for (r = 1; VALIDRANK(r); r++) {
221 for (f = 1; VALIDFILE(f); f++) {
222 p = b[RANKTOBOARD(r)][FILETOBOARD(f)].icon;
223 pi = pgn_piece_to_int(p);
225 if (pi == OPEN_SQUARE || !val_piece_side(g->turn, p))
226 continue;
228 if (find_source_square(g, b, pi, &f, &r, file, rank) != 0) {
229 g->kfile = kf, g->krank = kr;
230 pgn_switch_turn(g);
231 return 1;
236 g->kfile = kf, g->krank = kr;
237 pgn_switch_turn(g);
238 return 0;
241 static int validate_castle_move(GAME g, BOARD b, int side, int sfile,
242 int srank, int file, int rank)
244 int n;
246 if (side == KINGSIDE) {
247 if ((g->turn == WHITE && !TEST_FLAG(g->flags, GF_WK_CASTLE)) ||
248 (g->turn == BLACK && !TEST_FLAG(g->flags, GF_BK_CASTLE)))
249 return E_PGN_INVALID;
251 else {
252 if ((g->turn == WHITE && !TEST_FLAG(g->flags, GF_WQ_CASTLE)) ||
253 (g->turn == BLACK && !TEST_FLAG(g->flags, GF_BQ_CASTLE)))
254 return E_PGN_INVALID;
257 if (file > FILETOINT('e')) {
258 if (b[RANKTOBOARD(srank)][FILETOBOARD((sfile + 1))].icon
259 != pgn_int_to_piece(g->turn, OPEN_SQUARE) ||
260 b[RANKTOBOARD(srank)][FILETOBOARD((sfile + 2))].icon
261 != pgn_int_to_piece(g->turn, OPEN_SQUARE))
262 return E_PGN_INVALID;
265 if (pgn_config.strict_castling > 0) {
266 if (opponent_can_attack(g, b, sfile + 1, srank))
267 return E_PGN_INVALID;
269 if (opponent_can_attack(g, b, sfile + 2, srank))
270 return E_PGN_INVALID;
273 else {
274 if (b[RANKTOBOARD(srank)][FILETOBOARD((sfile - 1))].icon !=
275 pgn_int_to_piece(g->turn, OPEN_SQUARE) ||
276 b[RANKTOBOARD(srank)][FILETOBOARD((sfile - 2))].icon !=
277 pgn_int_to_piece(g->turn, OPEN_SQUARE) ||
278 b[RANKTOBOARD(srank)][FILETOBOARD((sfile - 3))].icon !=
279 pgn_int_to_piece(g->turn, OPEN_SQUARE))
280 return E_PGN_INVALID;
282 if (pgn_config.strict_castling > 0) {
283 if (opponent_can_attack(g, b, sfile - 1, srank))
284 return E_PGN_INVALID;
286 if (opponent_can_attack(g, b, sfile - 2, srank))
287 return E_PGN_INVALID;
289 if (opponent_can_attack(g, b, sfile - 3, srank))
290 return E_PGN_INVALID;
294 n = g->check_testing;
295 g->check_testing = 1;
297 if (check_self(g, b, g->kfile, g->krank) == CHECK_SELF) {
298 g->check_testing = n;
299 return E_PGN_INVALID;
302 g->check_testing = n;
303 g->castle = side;
304 return E_PGN_OK;
307 static int validate_piece(GAME g, BOARD b, int p, int sfile,
308 int srank, int file, int rank)
310 int f, r;
311 int i, dist;
313 switch (p) {
314 case PAWN:
315 if (sfile == file) {
316 /* Find the first pawn in the current column. */
317 i = (g->turn == WHITE) ? -1 : 1;
319 if (!srank) {
320 for (r = rank + i, dist = 0; VALIDFILE(r); r += i, dist++) {
321 p = b[RANKTOBOARD(r)][FILETOBOARD(file)].icon;
323 if (pgn_piece_to_int(p) != OPEN_SQUARE)
324 break;
327 if (pgn_piece_to_int(p) != PAWN || !val_piece_side(g->turn, p) || dist > 2)
328 return E_PGN_INVALID;
330 srank = r;
332 else {
333 dist = abs(srank - rank);
334 p = b[RANKTOBOARD(srank)][FILETOBOARD(sfile)].icon;
336 if (pgn_piece_to_int(p) != PAWN || !val_piece_side(g->turn, p) || dist > 2)
337 return E_PGN_INVALID;
340 if (g->turn == WHITE) {
341 if ((srank == 2 && dist > 2) || (srank > 2 && dist > 1))
342 return E_PGN_INVALID;
344 else {
345 if ((srank == 7 && dist > 2) || (srank < 7 && dist > 1))
346 return E_PGN_INVALID;
349 p = b[RANKTOBOARD(rank)][FILETOBOARD(file)].icon;
351 if (pgn_piece_to_int(p) != OPEN_SQUARE)
352 return E_PGN_INVALID;
354 else if (sfile != file) {
355 if (abs(sfile - file) != 1)
356 return E_PGN_INVALID;
358 srank = (g->turn == WHITE) ? rank - 1 : rank + 1;
359 p = b[RANKTOBOARD(srank)][FILETOBOARD(sfile)].icon;
361 if (!val_piece_side(g->turn, p))
362 return E_PGN_INVALID;
364 if (pgn_piece_to_int(p) != PAWN || abs(srank - rank) != 1)
365 return E_PGN_INVALID;
367 p = b[RANKTOBOARD(rank)][FILETOBOARD(file)].icon;
369 /* En Passant. */
370 if (pgn_piece_to_int(p) == OPEN_SQUARE) {
371 /* Previous move was not 2 squares and a pawn. */
372 if (!TEST_FLAG(g->flags, GF_ENPASSANT))
373 return E_PGN_INVALID;
375 if (!b[RANKTOBOARD(rank)][FILETOBOARD(file)].enpassant)
376 return E_PGN_INVALID;
378 r = (g->turn == WHITE) ? 6 : 3;
380 if (rank != r)
381 return E_PGN_INVALID;
383 r = (g->turn == WHITE) ? rank - 1 : rank + 1;
384 p = b[RANKTOBOARD(r)][FILETOBOARD(file)].icon;
386 if (pgn_piece_to_int(p) != PAWN)
387 return E_PGN_INVALID;
390 if (val_piece_side(g->turn, p))
391 return E_PGN_INVALID;
394 p = b[RANKTOBOARD(srank)][FILETOBOARD(sfile)].icon;
396 if (!val_piece_side(g->turn, p))
397 return E_PGN_INVALID;
399 break;
400 case KING:
401 f = abs(sfile - file);
402 r = abs(srank - rank);
404 if (r > 1 || f > 2)
405 return E_PGN_INVALID;
407 if (f == 2) {
408 if (sfile != FILETOINT('e'))
409 return E_PGN_INVALID;
410 else {
411 if (validate_castle_move(g, b, (file > FILETOINT('e')) ?
412 KINGSIDE : QUEENSIDE, sfile, srank, file, rank)
413 != E_PGN_OK)
414 return E_PGN_INVALID;
417 break;
418 default:
419 break;
422 return E_PGN_OK;
426 * Returns the number of pieces of type 'p' that can move to the destination
427 * square located at 'file' and 'rank'. The source square 'sfile' and 'srank'
428 * (if available in the move text) should be determined before calling this
429 * function or set to 0 if unknown. Returns 0 if the move is impossible for
430 * the piece 'p'.
432 static int find_ambiguous(GAME g, BOARD b, int p, int sfile,
433 int srank, int file, int rank)
435 int count = 0;
437 switch (p) {
438 case PAWN:
439 count = validate_pawn(g, b, sfile, srank, file, rank);
440 break;
441 case ROOK:
442 count = count_by_rank_file(g, b, p, sfile, srank, file, rank);
443 break;
444 case KNIGHT:
445 count = count_knight(g, b, p, sfile, srank, file, rank);
446 break;
447 case BISHOP:
448 count = count_by_diag(g, b, p, sfile, srank, file, rank);
449 break;
450 case QUEEN:
451 count = count_by_rank_file(g, b, p, sfile, srank, file, rank);
452 count += count_by_diag(g, b, p, sfile, srank, file, rank);
453 break;
454 case KING:
455 count = count_by_rank_file(g, b, p, sfile, srank, file, rank);
456 count += count_by_diag(g, b, p, sfile, srank, file, rank);
457 break;
458 default:
459 break;
462 return count;
465 static void find_king_squares(GAME g, BOARD b, int *file,
466 int *rank, int *ofile, int *orank)
468 int f, r;
470 for (r = 1; VALIDRANK(r); r++) {
471 for (f = 1; VALIDFILE(f); f++) {
472 int p = b[RANKTOBOARD(r)][FILETOBOARD(f)].icon;
473 int pi = pgn_piece_to_int(p);
475 if (pi == OPEN_SQUARE || pi != KING)
476 continue;
478 if (val_piece_side(g->turn, p))
479 *file = f, *rank = r;
480 else
481 *ofile = f, *orank = r;
485 #ifdef DEBUG
486 PGN_DUMP("%s:%d: king location: %c%c %c%c(opponent)\n", __FILE__,
487 __LINE__, INTTOFILE(*file), INTTORANK(*rank),
488 INTTOFILE(*ofile), INTTORANK(*orank));
489 #endif
492 static int parse_castle_move(GAME g, BOARD b, int side, int *sfile,
493 int *srank, int *file, int *rank)
495 *srank = *rank = (g->turn == WHITE) ? 1 : 8;
496 *sfile = FILETOINT('e');
498 if (side == KINGSIDE)
499 *file = FILETOINT('g');
500 else
501 *file = FILETOINT('c');
503 return validate_castle_move(g, b, side, *sfile, *srank, *file, *rank);
507 * Almost exactly like pgn_find_valid_moves() but returns immediately after a
508 * valid move is found.
510 static int check_mate_thingy(GAME g, BOARD b, int file, int rank)
512 int r, f;
513 int p = pgn_piece_to_int(b[RANKTOBOARD(rank)][FILETOBOARD(file)].icon);
515 for (r = 1; VALIDRANK(r); r++) {
516 for (f = 1; VALIDFILE(f); f++) {
517 if (val_piece_side(g->turn, b[RANKTOBOARD(r)][FILETOBOARD(f)].icon))
518 continue;
520 if (find_source_square(g, b, p, &file, &rank, f, r) != 0)
521 return 1;
525 return 0;
528 static int checkmate_test(GAME g, BOARD b)
530 int f, r;
531 int n = CHECK_MATE;
532 int kf = g->kfile, kr = g->krank, okf = g->okfile, okr = g->okrank;
534 #ifdef DEBUG
535 PGN_DUMP("%s:%d: BEGIN checkmate test\n", __FILE__, __LINE__);
536 #endif
538 * The king squares need to be switched also for find_source_squares()
539 * which calls check_self().
541 pgn_switch_turn(g);
542 g->kfile = g->okfile, g->krank = g->okrank;
544 for (r = 1; VALIDRANK(r); r++) {
545 for (f = 1; VALIDFILE(f); f++) {
546 int p;
547 int sfile = f, srank = r;
549 p = b[RANKTOBOARD(r)][FILETOBOARD(f)].icon;
551 if (p == OPEN_SQUARE || !val_piece_side(g->turn, p))
552 continue;
554 if (check_mate_thingy(g, b, sfile, srank)) {
555 n = CHECK;
556 goto done;
561 done:
562 pgn_switch_turn(g);
563 g->kfile = kf, g->krank = kr, g->okfile = okf, g->okrank = okr;
564 return n;
567 static int check_opponent(GAME g, BOARD b, int file, int rank)
569 int f, r;
570 int p, pi;
572 #ifdef DEBUG
573 PGN_DUMP("%s:%d: BEGIN opponent check test\n", __FILE__, __LINE__);
574 #endif
576 for (r = 1; VALIDRANK(r); r++) {
577 for (f = 1; VALIDFILE(f); f++) {
578 p = b[RANKTOBOARD(r)][FILETOBOARD(f)].icon;
579 pi = pgn_piece_to_int(p);
581 if (pi == OPEN_SQUARE || !val_piece_side(g->turn, p))
582 continue;
584 if (find_source_square(g, b, pi, &f, &r, file, rank) != 0)
585 return CHECK;
589 return 0;
592 static int check_self(GAME g, BOARD b, int file, int rank)
594 int f, r;
595 int p, pi;
597 #ifdef DEBUG
598 PGN_DUMP("%s:%d: BEGIN self check test\n", __FILE__, __LINE__);
599 #endif
600 pgn_switch_turn(g);
602 for (r = 1; VALIDRANK(r); r++) {
603 for (f = 1; VALIDFILE(f); f++) {
604 p = b[RANKTOBOARD(r)][FILETOBOARD(f)].icon;
605 pi = pgn_piece_to_int(p);
607 if (pi == OPEN_SQUARE || !val_piece_side(g->turn, p))
608 continue;
610 if (find_source_square(g, b, pi, &f, &r, file, rank) != 0) {
611 pgn_switch_turn(g);
612 return CHECK_SELF;
617 pgn_switch_turn(g);
618 return g->check;
621 static int check_test(GAME g, BOARD b)
623 #ifdef DEBUG
624 PGN_DUMP("%s:%d: BEGIN check test\n", __FILE__, __LINE__);
625 #endif
626 g->check = check_opponent(g, b, g->okfile, g->okrank);
628 if (g->check)
629 return checkmate_test(g, b);
631 g->check_testing = 0;
632 return g->check;
635 static int validate_pawn(GAME g, BOARD b, int sfile,
636 int srank, int file, int rank)
638 int n = abs(srank - rank);
639 int p;
641 if (abs(sfile - file) > 1)
642 return 0;
644 if (g->turn == WHITE) {
645 if ((srank == 2 && n > 2) || (srank > 2 && n > 1))
646 return 0;
648 else {
649 if ((srank == 7 && n > 2) || (srank < 7 && n > 1))
650 return 0;
653 if (n > 1 && abs(sfile - file) != 0)
654 return 0;
656 p = b[RANKTOBOARD(srank)][FILETOBOARD(sfile)].icon;
658 if (!val_piece_side(g->turn, p) || pgn_piece_to_int(p) != PAWN)
659 return 0;
661 if (srank == rank || (g->turn == WHITE && rank < srank) ||
662 (g->turn == BLACK && rank > srank))
663 return 0;
665 if (sfile == file) {
666 p = b[RANKTOBOARD(rank)][FILETOBOARD(file)].icon;
668 if (pgn_piece_to_int(p) != OPEN_SQUARE)
669 return 0;
671 p = (g->turn == WHITE) ? rank - 1 : rank + 1;
672 p = b[RANKTOBOARD(p)][FILETOBOARD(file)].icon;
674 if (n > 1 && pgn_piece_to_int(p) != OPEN_SQUARE)
675 return 0;
677 return 1;
680 p = b[RANKTOBOARD(rank)][FILETOBOARD(file)].icon;
682 if (pgn_piece_to_int(p) != OPEN_SQUARE) {
683 if (val_piece_side(g->turn, p))
684 return 0;
686 return 1;
689 /* En Passant. */
690 p = (g->turn == WHITE) ? rank - 1 : rank + 1;
691 p = b[RANKTOBOARD(p)][FILETOBOARD(file)].icon;
693 if (pgn_piece_to_int(p) == OPEN_SQUARE || val_piece_side(g->turn, p))
694 return 0;
696 /* Previous move was not 2 squares and a pawn. */
697 if (!TEST_FLAG(g->flags, GF_ENPASSANT) ||
698 b[RANKTOBOARD(rank)][FILETOBOARD(file)].enpassant == 0)
699 return 0;
702 // GF_ENPASSANT should take care of this
703 if (!b[RANKTOBOARD(rank)][FILETOBOARD(file)].enpassant)
704 return 0;
707 if ((g->turn == WHITE && rank != 6) || (g->turn == BLACK && rank != 3))
708 return 0;
710 #if 0
711 // GF_ENPASSANT should take care of this
712 n = (g->turn == WHITE) ? rank - 1 : rank + 1;
713 p = b[RANKTOBOARD(n)][FILETOBOARD(file)].icon;
715 if (pgn_piece_to_int(p) != PAWN)
716 return 0;
718 if (val_piece_side(g->turn, p))
719 return 0;
720 #endif
722 return 1;
725 static int check_self_test(GAME g, BOARD b, int p, int sfile, int srank,
726 int file, int rank)
728 BOARD tmpb;
729 struct game_s newg;
730 int oldv = g->validate;
731 int nkfile, nkrank, nokfile, nokrank;
732 int go;
734 g->validate = 0;
735 g->check_testing = 1;
736 memcpy(tmpb, b, sizeof(BOARD));
737 memcpy(&newg, g, sizeof(struct game_s));
739 if (finalize_move(&newg, tmpb, 0, sfile, srank, file, rank)
740 != E_PGN_OK) {
741 g->check_testing = 0;
742 g->validate = oldv;
743 return 0;
746 if (p == KING)
747 find_king_squares(&newg, tmpb, &nkfile, &nkrank, &nokfile, &nokrank);
748 else
749 nkfile = g->kfile, nkrank = g->krank;
751 go = TEST_FLAG(newg.flags, GF_GAMEOVER);
752 memcpy(&newg, g, sizeof(struct game_s));
754 if (check_self(&newg, tmpb, nkfile, nkrank) == CHECK_SELF) {
755 g->check_testing = 0;
756 g->validate = oldv;
757 return 0;
760 if (!g->validate && !g->validate_find) {
761 g->flags = newg.flags;
763 if (go)
764 SET_FLAG(g->flags, GF_GAMEOVER);
767 g->validate = oldv;
768 g->check_testing = 0;
769 return 1;
772 static int find_source_square(GAME g, BOARD b, int piece, int *sfile,
773 int *srank, int file, int rank)
775 int p = 0;
776 int r, f, i;
777 int dist = 0;
778 int count = 0;
780 #ifdef DEBUG
781 PGN_DUMP("%s:%d: finding source square: piece=%c source=%c%c dest=%c%c\n",
782 __FILE__, __LINE__, pgn_int_to_piece(g->turn, piece),
783 (*sfile) ? INTTOFILE(*sfile) : '0',
784 (*srank) ? INTTORANK(*srank) : '0', INTTOFILE(file),
785 INTTORANK(rank));
786 #endif
788 if (piece == PAWN) {
789 if (!*srank && *sfile == file) {
790 /* Find the first pawn in 'file'. */
791 i = (g->turn == WHITE) ? -1 : 1;
793 for (r = rank + i, dist = 0; VALIDFILE(r); r += i, dist++) {
794 p = pgn_piece_to_int(b[RANKTOBOARD(r)][FILETOBOARD(file)].icon);
796 if (p != OPEN_SQUARE)
797 break;
800 *srank = r;
803 if (!*srank)
804 *srank = (g->turn == WHITE) ? rank - 1 : rank + 1;
806 if (!validate_pawn(g, b, *sfile, *srank, file, rank))
807 return 0;
808 else
809 count = 1;
811 if (!g->check_testing) {
812 if (check_self_test(g, b, piece, *sfile, *srank, file, rank) == 0)
813 return 0;
816 else {
817 if (*sfile && *srank) {
818 count = find_ambiguous(g, b, piece, *sfile, *srank, file, rank);
820 if (count != 1)
821 return count;
823 if (!g->check_testing) {
824 if (check_self_test(g, b, piece, *sfile, *srank, file, rank)
825 == 0)
826 return 0;
829 else {
830 int ff = *sfile, rr = *srank;
832 for (r = 1; VALIDRANK(r); r++) {
833 for (f = 1; VALIDFILE(f); f++) {
834 int n;
836 if ((*sfile && f != ff) || (*srank && r != rr))
837 continue;
839 n = find_ambiguous(g, b, piece, f, r, file, rank);
841 if (n) {
842 if (!g->check_testing &&
843 check_self_test(g, b, piece, f, r, file, rank) == 0)
844 continue;
846 count += n;
847 ff = f;
848 rr = r;
853 if (count == 1) {
854 *sfile = ff;
855 *srank = rr;
859 if (validate_piece(g, b, piece, *sfile, *srank, file, rank) != E_PGN_OK)
860 return 0;
863 return count;
866 static int finalize_move(GAME g, BOARD b, int promo, int sfile, int srank,
867 int file, int rank)
869 int p, pi;
871 #ifdef DEBUG
872 if (!g->validate && !g->check_testing)
873 PGN_DUMP("%s:%d: BEGIN finalizing\n", __FILE__, __LINE__);
874 #endif
876 p = b[RANKTOBOARD(rank)][FILETOBOARD(file)].icon;
877 pi = pgn_piece_to_int(p);
879 if (pi != OPEN_SQUARE && val_piece_side(g->turn, p))
880 return E_PGN_INVALID;
882 if (!g->validate) {
883 #ifdef DEBUG
884 if (!g->check_testing)
885 PGN_DUMP("%s:%d: updating board and game flags\n", __FILE__,
886 __LINE__);
887 #endif
888 pgn_reset_enpassant(b);
889 p = b[RANKTOBOARD(srank)][FILETOBOARD(sfile)].icon;
890 pi = pgn_piece_to_int(p);
892 if (pi == PAWN) {
893 p = b[RANKTOBOARD(rank)][FILETOBOARD(file)].icon;
895 if (sfile != file && pgn_piece_to_int(p) == OPEN_SQUARE &&
896 TEST_FLAG(g->flags, GF_ENPASSANT)) {
897 p = (g->turn == WHITE) ? rank - 1 : rank + 1;
898 b[RANKTOBOARD(p)][FILETOBOARD(file)].icon =
899 pgn_int_to_piece(g->turn, OPEN_SQUARE);
902 if (abs(srank - rank) > 1) {
903 SET_FLAG(g->flags, GF_ENPASSANT);
904 b[RANKTOBOARD(((g->turn == WHITE) ? rank - 1 : rank + 1))][FILETOBOARD(file)].enpassant = 1;
907 else if (pi == ROOK) {
908 if (g->turn == WHITE) {
909 if (sfile == FILETOINT('h') && srank == 1)
910 CLEAR_FLAG(g->flags, GF_WK_CASTLE);
911 else if (sfile == FILETOINT('a') && srank == 1)
912 CLEAR_FLAG(g->flags, GF_WQ_CASTLE);
914 else {
915 if (sfile == FILETOINT('h') && srank == 8)
916 CLEAR_FLAG(g->flags, GF_BK_CASTLE);
917 else if (sfile == FILETOINT('a') && srank == 8)
918 CLEAR_FLAG(g->flags, GF_BQ_CASTLE);
922 if (pi != PAWN)
923 CLEAR_FLAG(g->flags, GF_ENPASSANT);
925 if (pi == KING && !g->castle) {
926 if (g->turn == WHITE)
927 CLEAR_FLAG(g->flags, GF_WK_CASTLE|GF_WQ_CASTLE);
928 else
929 CLEAR_FLAG(g->flags, GF_BK_CASTLE|GF_BQ_CASTLE);
932 if (g->castle) {
933 p = b[RANKTOBOARD(srank)][FILETOBOARD(sfile)].icon;
934 b[RANKTOBOARD(srank)][FILETOBOARD(sfile)].icon =
935 pgn_int_to_piece(g->turn, OPEN_SQUARE);
936 b[RANKTOBOARD(srank)][FILETOBOARD(
937 (file > FILETOINT('e') ? 8 : 1))].icon =
938 pgn_int_to_piece(g->turn, OPEN_SQUARE);
939 b[RANKTOBOARD(rank)][FILETOBOARD(file)].icon = p;
941 if (file > FILETOINT('e'))
942 b[RANKTOBOARD(rank)][FILETOBOARD((file - 1))].icon =
943 pgn_int_to_piece(g->turn, ROOK);
944 else
945 b[RANKTOBOARD(rank)][FILETOBOARD((file + 1))].icon =
946 pgn_int_to_piece(g->turn, ROOK);
948 if (g->turn == WHITE)
949 CLEAR_FLAG(g->flags, (file > FILETOINT('e')) ? GF_WK_CASTLE :
950 GF_WQ_CASTLE);
951 else
952 CLEAR_FLAG(g->flags, (file > FILETOINT('e')) ? GF_BK_CASTLE :
953 GF_BQ_CASTLE);
955 else {
956 if (promo)
957 p = pgn_int_to_piece(g->turn, promo);
958 else
959 p = b[RANKTOBOARD(srank)][FILETOBOARD(sfile)].icon;
961 b[RANKTOBOARD(srank)][FILETOBOARD(sfile)].icon =
962 pgn_int_to_piece(g->turn, OPEN_SQUARE);
963 b[RANKTOBOARD(rank)][FILETOBOARD(file)].icon = p;
967 g->castle = 0;
969 if (!g->check_testing) {
970 if (pgn_piece_to_int(p) == KING)
971 find_king_squares(g, b, &g->kfile, &g->krank, &g->okfile, &g->okrank);
973 switch (check_test(g, b)) {
974 case CHECK:
975 g->check = CHECK;
976 break;
977 case CHECK_MATE:
978 g->check = CHECK_MATE;
980 if (!g->validate) {
981 pgn_tag_add(&g->tag, "Result",
982 (g->turn == WHITE) ? "1-0" : "0-1");
983 SET_FLAG(g->flags, GF_GAMEOVER);
985 break;
986 default:
987 break;
990 // FIXME RAV
991 if (!g->validate && (g->pgn_fen_tag > 0 && !g->done_fen_tag) &&
992 !pgn_history_total(g->hp) && srank >= 7)
993 SET_FLAG(g->flags, GF_BLACK_OPENING);
996 #ifdef DEBUG
997 if (!g->validate && !g->check_testing)
998 PGN_DUMP("%s:%d: END finalizing\n", __FILE__, __LINE__);
999 #endif
1001 if (!g->validate) {
1002 p = pgn_piece_to_int(p);
1004 if (p == PAWN || promo || g->capture)
1005 g->ply = 0;
1006 else
1007 g->ply++;
1009 if (g->ply >= 50) {
1010 if (g->tag[6]->value[0] == '*') {
1011 pgn_tag_add(&g->tag, "Result", "1/2-1/2");
1012 SET_FLAG(g->flags, GF_GAMEOVER);
1017 return E_PGN_OK;
1020 static void black_opening(GAME g, BOARD b, int rank)
1022 if (!g->ravlevel && !g->hindex && pgn_tag_find(g->tag, "FEN") == -1) {
1023 if (rank > 4) {
1024 g->turn = BLACK;
1025 find_king_squares(g, b, &g->kfile, &g->krank, &g->okfile, &g->okrank);
1027 if (!g->validate)
1028 SET_FLAG(g->flags, GF_BLACK_OPENING);
1030 else {
1031 g->turn = WHITE;
1033 if (!g->validate)
1034 CLEAR_FLAG(g->flags, GF_BLACK_OPENING);
1038 #ifdef DEBUG
1039 if (TEST_FLAG(g->flags, GF_BLACK_OPENING))
1040 PGN_DUMP("%s:%d: black opening\n", __FILE__, __LINE__);
1041 #endif
1044 static char *format_santofrfr(int promo, int sfile, int srank, int file,
1045 int rank)
1047 char *frfr = malloc(6);
1049 memset(frfr, 0, 6);
1050 snprintf(frfr, 6, "%c%c%c%c", INTTOFILE(sfile),
1051 INTTORANK(srank), INTTOFILE(file), INTTORANK(rank));
1053 if (promo)
1054 frfr[4] = pgn_int_to_piece(BLACK, promo);
1056 return frfr;
1060 * Converts a2a3 formatted moves to SAN format. The promotion piece should be
1061 * appended (a7a8q).
1063 static int frfrtosan(GAME g, BOARD b, char **m, char **dst)
1065 char buf[MAX_SAN_MOVE_LEN+1] = {0}, *bp = buf;
1066 int icon, p, dp, promo = 0;
1067 int sfile, srank, file, rank;
1068 int n;
1069 int fc, rc;
1070 int ff = 0, rr = 0;
1072 #ifdef DEBUG
1073 PGN_DUMP("%s:%d: converting to SAN format\n", __FILE__, __LINE__);
1074 #endif
1076 strcpy (buf, *m);
1077 sfile = FILETOINT(bp[0]);
1078 srank = RANKTOINT(bp[1]);
1079 file = FILETOINT(bp[2]);
1080 rank = RANKTOINT(bp[3]);
1082 black_opening(g, b, rank);
1084 if (bp[4]) {
1085 if ((promo = pgn_piece_to_int(bp[4])) == -1 || promo == OPEN_SQUARE)
1086 return E_PGN_PARSE;
1087 #ifdef DEBUG
1088 PGN_DUMP("%s:%d: promotion to %c\n", __FILE__, __LINE__,
1089 pgn_int_to_piece(g->turn, promo));
1090 #endif
1093 icon = b[RANKTOBOARD(srank)][FILETOBOARD(sfile)].icon;
1095 if ((p = pgn_piece_to_int(icon)) == -1 || p == OPEN_SQUARE)
1096 return E_PGN_PARSE;
1098 if (p != PAWN && promo)
1099 return E_PGN_INVALID;
1101 if (p == PAWN) {
1102 if (find_source_square(g, b, p, &sfile, &srank, file, rank) != 1)
1103 return E_PGN_INVALID;
1105 goto capture;
1108 if (validate_piece(g, b, p, sfile, srank, file, rank) != E_PGN_OK)
1109 return E_PGN_INVALID;
1111 if (p == KING && abs(sfile - file) > 1) {
1112 strcpy(buf, (file > FILETOINT('e')) ? "O-O" : "O-O-O");
1114 if (finalize_move(g, b, promo, sfile, srank, file, rank) != E_PGN_OK)
1115 return E_PGN_INVALID;
1117 if (g->check)
1118 strcat(buf, (g->check == CHECK) ? "+" : "#");
1120 /* The move buffer size may be shorter than frfr format. Since
1121 * pgn_parse() uses a buffer allocated on the stack, this is not
1122 * needed because its' size is always MAX_SAN_MOVE_LEN+1.
1124 if (!parsing_file && strlen (*m) < strlen (buf)) {
1125 free (*m);
1126 *m = malloc (strlen (buf)+1);
1127 strcpy (*m, buf);
1129 else
1130 strcpy (*m, buf);
1132 *dst = format_santofrfr(promo, sfile, srank, file, rank);
1133 return E_PGN_OK;
1136 bp = buf;
1137 *bp++ = toupper(icon);
1138 fc = rc = 0;
1139 n = find_source_square(g, b, p, &fc, &rc, file, rank);
1141 if (!n)
1142 return E_PGN_INVALID;
1143 else if (n > 1) {
1144 fc = find_source_square(g, b, p, &sfile, &rr, file, rank);
1145 rc = find_source_square(g, b, p, &ff, &srank, file, rank);
1147 if (fc == 1)
1148 *bp++ = INTTOFILE(sfile);
1149 else if (!fc && rc)
1150 *bp++ = INTTORANK(srank);
1151 else if (fc && rc) {
1152 if (rc == 1)
1153 *bp++ = INTTORANK(srank);
1154 else {
1155 *bp++ = INTTOFILE(sfile);
1156 *bp++ = INTTORANK(srank);
1159 else
1160 return E_PGN_PARSE; // not reached.
1163 capture:
1164 icon = b[RANKTOBOARD(rank)][FILETOBOARD(file)].icon;
1166 if ((dp = pgn_piece_to_int(icon)) == -1)
1167 return E_PGN_PARSE;
1170 * [Pf][fr]x
1172 if (dp != OPEN_SQUARE || (dp == OPEN_SQUARE && p == PAWN && sfile != file)) {
1173 if (p == PAWN)
1174 *bp++ = INTTOFILE(sfile);
1176 *bp++ = 'x';
1180 * [Pf][fr][x]fr
1183 *bp++ = INTTOFILE(file);
1184 *bp++ = INTTORANK(rank);
1186 if (p == PAWN && !promo && (rank == 8 || rank == 1))
1187 promo = pgn_piece_to_int('q');
1190 * [Pf][fr][x]fr[=P]
1192 if (promo) {
1193 if (p != PAWN || (g->turn == WHITE && (srank != 7 || rank != 8)) ||
1194 (g->turn == BLACK && (srank != 2 || rank != 1)))
1195 return E_PGN_INVALID;
1197 *bp++ = '=';
1198 *bp++ = pgn_int_to_piece(WHITE, promo);
1201 *bp = 0;
1203 if (find_source_square(g, b, p, &sfile, &srank, file, rank) != 1)
1204 return E_PGN_INVALID;
1206 if (finalize_move(g, b, promo, sfile, srank, file, rank) != E_PGN_OK)
1207 return E_PGN_INVALID;
1209 if (g->check)
1210 *bp++ = (g->check == CHECK) ? '+' : '#';
1212 *bp = 0;
1214 /* The move buffer size may be shorter than frfr format. Since
1215 * pgn_parse() uses a buffer allocated on the stack, this is not
1216 * needed because its' size is always MAX_SAN_MOVE_LEN+1.
1218 if (strlen (*m) < strlen (buf)) {
1219 free (*m);
1220 *m = malloc (strlen (buf)+1);
1221 strcpy (*m, buf);
1223 else
1224 strcpy (*m, buf);
1226 g->check = 0;
1227 *dst = format_santofrfr(promo, sfile, srank, file, rank);
1228 #ifdef DEBUG
1229 PGN_DUMP("%s:%d: END validating %s\n", __FILE__, __LINE__, *m);
1230 #endif
1231 return E_PGN_OK;
1234 static int do_santofrfr(GAME g, BOARD b, char **san, int *promo, int *sfile,
1235 int *srank, int *file, int *rank)
1237 char *p;
1238 int piece;
1239 int i = 0;
1240 char *m = *san;
1242 g->capture = 0;
1244 again:
1245 if (strlen(m) < 2)
1246 return E_PGN_PARSE;
1248 p = (m) + strlen(m);
1250 while (!isdigit(*--p) && *p != 'O') {
1251 if (*p == '=') {
1252 *promo = pgn_piece_to_int(i);
1253 i = 0;
1254 break;
1257 i = *p;
1258 *p = '\0';
1261 /* Alternate promotion text (e8Q). Convert to SAN. */
1262 if (i && pgn_piece_to_int(i) != E_PGN_ERR) {
1263 p = (m) + strlen(m);
1264 *p++ = '=';
1265 *p++ = toupper(i);
1266 *p = '\0';
1267 goto again;
1270 p = m;
1272 /* Skip 'P' (pawn). */
1273 if (pgn_piece_to_int(*p) == PAWN)
1274 p++;
1276 /* Pawn. */
1277 if (VALIDCOL(*p)) {
1278 for (i = 0; *p; i++) {
1279 if (VALIDCOL(*p)) {
1280 if (i > 0)
1281 *file = FILETOINT(*p++);
1282 else
1283 *file = *sfile = FILETOINT(*p++);
1285 else if (VALIDROW(*p)) {
1286 if (1 > 1)
1287 *rank = RANKTOINT(*p++);
1288 else
1289 *rank = RANKTOINT(*p++);
1291 else if (*p == 'x') {
1292 *file = FILETOINT(*++p);
1293 *rank = RANKTOINT(*++p);
1294 g->capture++;
1296 else if (*p == '=') {
1297 if (*promo == -1 || *promo == KING || *promo == PAWN)
1298 return E_PGN_PARSE;
1300 *p++ = '=';
1301 *p++ = toupper(pgn_int_to_piece(g->turn, *promo));
1302 *p = '\0';
1303 break;
1305 else {
1306 #ifdef DEBUG
1307 PGN_DUMP("Pawn (move: '%s'): %c\n", m, *p++);
1308 #else
1309 p++;
1310 #endif
1314 black_opening(g, b, *rank);
1316 if (find_source_square(g, b, PAWN, sfile, srank, *file, *rank) != 1)
1317 return E_PGN_INVALID;
1319 if (!*promo && (*rank == 8 || *rank == 1)) {
1320 *promo = pgn_piece_to_int('q');
1321 *p++ = '=';
1322 *p++ = pgn_int_to_piece(WHITE, *promo);
1323 *p = 0;
1326 /* Not a pawn. */
1327 else {
1328 p = m;
1331 * P[fr][x]fr
1333 * The first character is the piece but only if not a pawn.
1335 if ((piece = pgn_piece_to_int(*p++)) == -1)
1336 return E_PGN_PARSE;
1339 * [fr]fr
1341 if (strlen(m) > 3) {
1343 * rfr
1345 if (isdigit(*p))
1346 *srank = RANKTOINT(*p++);
1348 * f[r]fr
1350 else if (VALIDCOL(*p)) {
1351 *sfile = FILETOINT(*p++);
1354 * frfr
1356 if (isdigit(*p))
1357 *srank = RANKTOINT(*p++);
1361 * xfr
1363 if (*p == 'x') {
1364 g->capture++;
1365 p++;
1370 * fr
1372 * The destination square.
1374 *file = FILETOINT(*p++);
1375 *rank = RANKTOINT(*p++);
1377 if (*p == '=')
1378 *promo = *++p;
1380 black_opening(g, b, *rank);
1382 if ((i = find_source_square(g, b, piece, sfile, srank, *file, *rank))
1383 != 1 && !g->check)
1384 return (i == 0) ? E_PGN_INVALID : E_PGN_AMBIGUOUS;
1386 #if 0
1388 * The move is a valid one. Find the source file and rank so we
1389 * can later update the board positions.
1391 if (find_source_square(*g, b, piece, sfile, srank, *file, *rank)
1392 != 1 && !check)
1393 return E_PGN_INVALID;
1394 #endif
1397 *p = 0;
1398 *san = m;
1399 return E_PGN_OK;
1403 * Valididate move 'mp' against the game state 'g' and game board 'b' and
1404 * update board 'b'. 'mp' is updated to SAN format for moves which aren't
1405 * (frfr or e8Q for example). Returns E_PGN_PARSE if there was a move text
1406 * parsing error, E_PGN_INVALID if the move is invalid or E_PGN_OK if
1407 * successful.
1409 pgn_error_t pgn_parse_move(GAME g, BOARD b, char **mp, char **dst)
1411 int srank = 0, sfile = 0, rank, file;
1412 char *m = *mp, *p;
1413 int i;
1414 int promo = -1;
1415 size_t len = m ? strlen (m) : 0;
1418 * This may be an empty move with only an annotation. Kinda strange.
1420 if (!m || !*m)
1421 return E_PGN_OK;
1423 #ifdef DEBUG
1424 PGN_DUMP("%s:%d: BEGIN validating '%s' (%s)...\n", __FILE__, __LINE__, m,
1425 (g->turn == WHITE) ? "white" : "black");
1426 #endif
1428 g->check_testing = g->castle = 0;
1429 srank = rank = file = sfile = promo = 0;
1430 find_king_squares(g, b, &g->kfile, &g->krank, &g->okfile, &g->okrank);
1432 if (m[len-1] == '+')
1433 m[--len] = 0;
1435 if (VALIDCOL(*m) && VALIDROW(*(m + 1)) && VALIDCOL(*(m + 2))
1436 && VALIDROW(*(m + 3)))
1437 return frfrtosan(g, b, mp, dst);
1438 else if (*m == 'O') {
1439 if (strcmp(m, "O-O") == 0)
1440 i = KINGSIDE;
1441 else if (strcmp(m, "O-O-O") == 0)
1442 i = QUEENSIDE;
1443 else
1444 return E_PGN_PARSE;
1446 if (parse_castle_move(g, b, i, &sfile, &srank, &file, &rank) !=
1447 E_PGN_OK)
1448 return E_PGN_INVALID;
1450 /* The move buffer size may be shorter than frfr format. Since
1451 * pgn_parse() uses a buffer allocated on the stack, this is not
1452 * needed because its' size is always MAX_SAN_MOVE_LEN+1.
1454 if (len < 4 && !parsing_file) {
1455 m = malloc (5*sizeof(char));
1456 free (*mp);
1457 *mp = m;
1460 m = *mp;
1461 *m++ = INTTOFILE(sfile);
1462 *m++ = INTTORANK(srank);
1463 *m++ = INTTOFILE(file);
1464 *m++ = INTTORANK(rank);
1465 *m = 0;
1466 return frfrtosan(g, b, mp, dst);
1469 if ((i = do_santofrfr(g, b, &m, &promo, &sfile, &srank, &file, &rank))
1470 != E_PGN_OK)
1471 return i;
1473 p = m + strlen(m);
1475 if (finalize_move(g, b, promo, sfile, srank, file, rank) != E_PGN_OK)
1476 return E_PGN_INVALID;
1478 if (g->check)
1479 *p++ = (g->check == CHECK) ? '+' : '#';
1481 *p = g->check = 0;
1482 *dst = format_santofrfr(promo, sfile, srank, file, rank);
1484 #ifdef DEBUG
1485 PGN_DUMP("%s:%d: END validating %s\n", __FILE__, __LINE__, m);
1486 #endif
1487 return E_PGN_OK;
1491 * Like pgn_parse_move() but don't modify game flags in 'g' or board 'b'.
1493 pgn_error_t pgn_validate_move(GAME g, BOARD b, char **m, char **dst)
1495 int ret;
1496 int side = g->side, turn = g->turn;
1497 unsigned short flags = g->flags;
1499 #ifdef DEBUG
1500 PGN_DUMP("%s:%d: BEGIN validate only\n", __FILE__, __LINE__);
1501 #endif
1502 g->validate = 1;
1503 ret = pgn_parse_move(g, b, m, dst);
1504 g->validate = 0;
1505 #ifdef DEBUG
1506 PGN_DUMP("%s:%d: END validate only\n", __FILE__, __LINE__);
1507 #endif
1508 g->side = side;
1509 g->turn = turn;
1510 g->flags = flags;
1511 return ret;
1515 * Sets valid moves from game 'g' using board 'b'. The valid moves are for the
1516 * piece on the board 'b' at 'rank' and 'file'. Returns nothing.
1518 void pgn_find_valid_moves(GAME g, BOARD b, int file, int rank)
1520 int p = pgn_piece_to_int(b[RANKTOBOARD(rank)][FILETOBOARD(file)].icon);
1521 int r, f;
1523 #ifdef DEBUG
1524 PGN_DUMP("%s:%d: BEGIN valid destination squares for %c%c\n", __FILE__,
1525 __LINE__, INTTOFILE(file), INTTORANK(rank));
1526 #endif
1528 g->validate_find = 1;
1529 find_king_squares(g, b, &g->kfile, &g->krank, &g->okfile, &g->okrank);
1531 for (r = 1; VALIDRANK(r); r++) {
1532 for (f = 1; VALIDFILE(f); f++) {
1533 if (val_piece_side(g->turn, b[RANKTOBOARD(r)][FILETOBOARD(f)].icon))
1534 continue;
1536 if (find_source_square(g, b, p, &file, &rank, f, r) != 0) {
1537 b[RANKTOBOARD(r)][FILETOBOARD(f)].valid = 1;
1538 #ifdef DEBUG
1539 PGN_DUMP("%s:%d: %c%c is valid\n", __FILE__, __LINE__,
1540 INTTOFILE(f), INTTORANK(r));
1541 #endif
1546 #ifdef DEBUG
1547 PGN_DUMP("%s:%d: END valid destination squares for %c%c\n", __FILE__,
1548 __LINE__, INTTOFILE(file), INTTORANK(rank));
1549 #endif
1550 g->check_testing = 0;
1551 g->validate_find = 0;