1 /* vim:tw=78:ts=8:sw=4:set ft=c: */
3 Copyright (C) 2002-2006 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
40 static int val_piece_side(register char turn
, register int c
)
42 if ((isupper(c
) && turn
== WHITE
) ||
43 (islower(c
) && turn
== BLACK
))
49 static int count_piece(GAME g
, BOARD b
, register int piece
, register int sfile
,
50 register int srank
, register int file
, register int rank
,
53 register int p
= b
[RANKTOBOARD(rank
)][FILETOBOARD(file
)].icon
;
54 register int pi
= pgn_piece_to_int(p
);
56 if (!VALIDRANK(rank
) || !VALIDFILE(file
))
59 if (pi
!= OPEN_SQUARE
) {
60 if (pi
== piece
&& val_piece_side(g
.turn
, p
)) {
61 if (sfile
&& file
== sfile
) {
62 if (!srank
|| (srank
&& rank
== srank
))
65 else if (srank
&& rank
== srank
) {
69 else if (!sfile
&& !srank
)
80 * Get the source row and column for a given piece.
82 * The following two functions find 'piece' from the given square 'col' and
83 * 'row' and store the resulting column or row in 'c' and 'r'. The return
84 * value is the number of 'piece' found (on the current g.side) or zero.
85 * Search for 'piece' stops when a non-empty square is found.
87 static int count_by_diag(GAME g
, BOARD b
, register int piece
,
88 register int sfile
, register int srank
, register int file
,
92 register int ul
= 0, ur
= 0, dl
= 0, dr
= 0;
96 for (i
= 1; VALIDFILE(i
); i
++) {
100 if (!ul
&& VALIDRANK(r
) && VALIDFILE(f
))
101 ul
= count_piece(g
, b
, piece
, sfile
, srank
, f
, r
, &count
);
106 if (!ur
&& VALIDRANK(r
) && VALIDFILE(f
))
107 ur
= count_piece(g
, b
, piece
, sfile
, srank
, f
, r
, &count
);
112 if (!dl
&& VALIDRANK(r
) && VALIDFILE(f
))
113 dl
= count_piece(g
, b
, piece
, sfile
, srank
, f
, r
, &count
);
118 if (!dr
&& VALIDRANK(r
) && VALIDFILE(f
))
119 dr
= count_piece(g
, b
, piece
, sfile
, srank
, f
, r
, &count
);
125 static int count_knight(GAME g
, BOARD b
, int piece
, int sfile
, int srank
,
130 count_piece(g
, b
, piece
, sfile
, srank
, file
- 1, rank
+ 2, &count
);
131 count_piece(g
, b
, piece
, sfile
, srank
, file
+ 1, rank
+ 2, &count
);
132 count_piece(g
, b
, piece
, sfile
, srank
, file
+ 2, rank
+ 1, &count
);
133 count_piece(g
, b
, piece
, sfile
, srank
, file
- 2, rank
+ 1, &count
);
134 count_piece(g
, b
, piece
, sfile
, srank
, file
+ 1, rank
- 2, &count
);
135 count_piece(g
, b
, piece
, sfile
, srank
, file
- 1, rank
- 2, &count
);
136 count_piece(g
, b
, piece
, sfile
, srank
, file
+ 2, rank
- 1, &count
);
137 count_piece(g
, b
, piece
, sfile
, srank
, file
- 2, rank
- 1, &count
);
141 static int count_by_rank(GAME g
, BOARD b
, register int piece
,
142 register int sfile
, register int srank
, register int file
,
147 register int u
= 0, d
= 0;
149 for (i
= 1; VALIDRANK(i
); i
++) {
150 if (!u
&& VALIDRANK((rank
+ i
)))
151 u
= count_piece(g
, b
, piece
, sfile
, srank
, file
, rank
+ i
, &count
);
153 if (!d
&& VALIDRANK((rank
- i
)))
154 d
= count_piece(g
, b
, piece
, sfile
, srank
, file
, rank
- i
, &count
);
163 static int count_by_file(GAME g
, BOARD b
, register int piece
,
164 register int sfile
, register int srank
, register int file
,
169 register int l
= 0, r
= 0;
171 for (i
= 1; VALIDFILE(i
); i
++) {
172 if (!r
&& VALIDFILE((file
+ i
)))
173 r
= count_piece(g
, b
, piece
, sfile
, srank
, file
+ i
, rank
, &count
);
175 if (!l
&& VALIDFILE((file
- i
)))
176 l
= count_piece(g
, b
, piece
, sfile
, srank
, file
- i
, rank
, &count
);
185 static int count_by_rank_file(GAME g
, BOARD b
, register int piece
,
186 register int sfile
, register int srank
, register int file
,
191 count
= count_by_rank(g
, b
, piece
, sfile
, srank
, file
, rank
);
192 return count
+ count_by_file(g
, b
, piece
, sfile
, srank
, file
, rank
);
195 static int find_source_square(GAME
, BOARD
, int, int *, int *, int, int);
196 static int opponent_can_attack(GAME g
, BOARD b
, register int file
,
201 int kf
= kfile
, kr
= krank
;
204 kfile
= okfile
, krank
= okrank
;
206 for (r
= 1; VALIDRANK(r
); r
++) {
207 for (f
= 1; VALIDFILE(f
); f
++) {
208 p
= b
[RANKTOBOARD(r
)][FILETOBOARD(f
)].icon
;
209 pi
= pgn_piece_to_int(p
);
211 if (pi
== OPEN_SQUARE
|| !val_piece_side(g
.turn
, p
))
214 if (find_source_square(g
, b
, pi
, &f
, &r
, file
, rank
) != 0) {
215 kfile
= kf
, krank
= kr
;
222 kfile
= kf
, krank
= kr
;
227 static int check_self(GAME g
, BOARD b
, int file
, int rank
);
228 static int validate_castle_move(GAME g
, BOARD b
, int side
, int sfile
,
229 int srank
, int file
, int rank
)
233 if (side
== KINGSIDE
) {
234 if ((g
.turn
== WHITE
&& !TEST_FLAG(g
.flags
, GF_WK_CASTLE
)) ||
235 (g
.turn
== BLACK
&& !TEST_FLAG(g
.flags
, GF_BK_CASTLE
)))
236 return E_PGN_INVALID
;
239 if ((g
.turn
== WHITE
&& !TEST_FLAG(g
.flags
, GF_WQ_CASTLE
)) ||
240 (g
.turn
== BLACK
&& !TEST_FLAG(g
.flags
, GF_BQ_CASTLE
)))
241 return E_PGN_INVALID
;
244 if (file
> FILETOINT('e')) {
245 if (b
[RANKTOBOARD(srank
)][FILETOBOARD((sfile
+ 1))].icon
246 != pgn_int_to_piece(g
.turn
, OPEN_SQUARE
) ||
247 b
[RANKTOBOARD(srank
)][FILETOBOARD((sfile
+ 2))].icon
248 != pgn_int_to_piece(g
.turn
, OPEN_SQUARE
))
249 return E_PGN_INVALID
;
251 if (opponent_can_attack(g
, b
, sfile
+ 1, srank
))
252 return E_PGN_INVALID
;
254 if (opponent_can_attack(g
, b
, sfile
+ 2, srank
))
255 return E_PGN_INVALID
;
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 b
[RANKTOBOARD(srank
)][FILETOBOARD((sfile
- 3))].icon
!=
263 pgn_int_to_piece(g
.turn
, OPEN_SQUARE
))
264 return E_PGN_INVALID
;
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
;
272 if (opponent_can_attack(g
, b
, sfile
- 3, srank
))
273 return E_PGN_INVALID
;
279 if (check_self(g
, b
, kfile
, krank
) == CHECK_SELF
) {
281 return E_PGN_INVALID
;
289 static int validate_piece(GAME g
, BOARD b
, register int p
, register int sfile
,
290 register int srank
, register int file
, register int rank
)
293 register int i
, dist
;
298 /* Find the first pawn in the current column. */
299 i
= (g
.turn
== WHITE
) ? -1 : 1;
302 for (r
= rank
+ i
, dist
= 0; VALIDFILE(r
); r
+= i
, dist
++) {
303 p
= b
[RANKTOBOARD(r
)][FILETOBOARD(file
)].icon
;
305 if (pgn_piece_to_int(p
) != OPEN_SQUARE
)
309 if (pgn_piece_to_int(p
) != PAWN
|| !val_piece_side(g
.turn
, p
) || dist
> 2)
310 return E_PGN_INVALID
;
315 dist
= abs(srank
- rank
);
316 p
= b
[RANKTOBOARD(srank
)][FILETOBOARD(sfile
)].icon
;
318 if (pgn_piece_to_int(p
) != PAWN
|| !val_piece_side(g
.turn
, p
) || dist
> 2)
319 return E_PGN_INVALID
;
322 if (g
.turn
== WHITE
) {
323 if ((srank
== 2 && dist
> 2) || (srank
> 2 && dist
> 1))
324 return E_PGN_INVALID
;
327 if ((srank
== 7 && dist
> 2) || (srank
< 7 && dist
> 1))
328 return E_PGN_INVALID
;
331 p
= b
[RANKTOBOARD(rank
)][FILETOBOARD(file
)].icon
;
333 if (pgn_piece_to_int(p
) != OPEN_SQUARE
)
334 return E_PGN_INVALID
;
336 else if (sfile
!= file
) {
337 if (abs(sfile
- file
) != 1)
338 return E_PGN_INVALID
;
340 srank
= (g
.turn
== WHITE
) ? rank
- 1 : rank
+ 1;
341 p
= b
[RANKTOBOARD(srank
)][FILETOBOARD(sfile
)].icon
;
343 if (!val_piece_side(g
.turn
, p
))
344 return E_PGN_INVALID
;
346 if (pgn_piece_to_int(p
) != PAWN
|| abs(srank
- rank
) != 1)
347 return E_PGN_INVALID
;
349 p
= b
[RANKTOBOARD(rank
)][FILETOBOARD(file
)].icon
;
352 if (pgn_piece_to_int(p
) == OPEN_SQUARE
) {
353 /* Previous move was not 2 squares and a pawn. */
354 if (!TEST_FLAG(g
.flags
, GF_ENPASSANT
))
355 return E_PGN_INVALID
;
357 if (!b
[RANKTOBOARD(rank
)][FILETOBOARD(file
)].enpassant
)
358 return E_PGN_INVALID
;
360 r
= (g
.turn
== WHITE
) ? 6 : 3;
363 return E_PGN_INVALID
;
365 r
= (g
.turn
== WHITE
) ? rank
- 1 : rank
+ 1;
366 p
= b
[RANKTOBOARD(r
)][FILETOBOARD(file
)].icon
;
368 if (pgn_piece_to_int(p
) != PAWN
)
369 return E_PGN_INVALID
;
372 if (val_piece_side(g
.turn
, p
))
373 return E_PGN_INVALID
;
376 p
= b
[RANKTOBOARD(srank
)][FILETOBOARD(sfile
)].icon
;
378 if (!val_piece_side(g
.turn
, p
))
379 return E_PGN_INVALID
;
383 f
= abs(sfile
- file
);
384 r
= abs(srank
- rank
);
387 return E_PGN_INVALID
;
390 if (sfile
!= FILETOINT('e'))
391 return E_PGN_INVALID
;
393 if (validate_castle_move(g
, b
, (file
> FILETOINT('e')) ?
394 KINGSIDE
: QUEENSIDE
, sfile
, srank
, file
, rank
)
396 return E_PGN_INVALID
;
408 * Returns the number of pieces of type 'p' that can move to the destination
409 * square located at 'file' and 'rank'. The source square 'sfile' and 'srank'
410 * (if available in the move text) should be determined before calling this
411 * function or set to 0 if unknown. Returns 0 if the move is impossible for
414 static int validate_pawn(GAME g
, BOARD b
, int sfile
, int srank
, int file
,
416 static int find_ambiguous(GAME g
, BOARD b
, register int p
, register int sfile
,
417 register int srank
, register int file
, register int rank
)
423 count
= validate_pawn(g
, b
, sfile
, srank
, file
, rank
);
426 count
= count_by_rank_file(g
, b
, p
, sfile
, srank
, file
, rank
);
429 count
= count_knight(g
, b
, p
, sfile
, srank
, file
, rank
);
432 count
= count_by_diag(g
, b
, p
, sfile
, srank
, file
, rank
);
435 count
= count_by_rank_file(g
, b
, p
, sfile
, srank
, file
, rank
);
436 count
+= count_by_diag(g
, b
, p
, sfile
, srank
, file
, rank
);
439 count
= count_by_rank_file(g
, b
, p
, sfile
, srank
, file
, rank
);
440 count
+= count_by_diag(g
, b
, p
, sfile
, srank
, file
, rank
);
449 static void find_king_squares(GAME g
, BOARD b
, register int *file
,
450 register int *rank
, register int *ofile
, register int *orank
)
454 for (r
= 1; VALIDRANK(r
); r
++) {
455 for (f
= 1; VALIDFILE(f
); f
++) {
456 register int p
= b
[RANKTOBOARD(r
)][FILETOBOARD(f
)].icon
;
457 register int pi
= pgn_piece_to_int(p
);
459 if (pi
== OPEN_SQUARE
|| pi
!= KING
)
462 if (val_piece_side(g
.turn
, p
))
463 *file
= f
, *rank
= r
;
465 *ofile
= f
, *orank
= r
;
470 static int parse_castle_move(GAME g
, BOARD b
, int side
, int *sfile
,
471 int *srank
, int *file
, int *rank
)
473 *srank
= *rank
= (g
.turn
== WHITE
) ? 1 : 8;
474 *sfile
= FILETOINT('e');
476 if (side
== KINGSIDE
)
477 *file
= FILETOINT('g');
479 *file
= FILETOINT('c');
481 return validate_castle_move(g
, b
, side
, *sfile
, *srank
, *file
, *rank
);
485 * Almost exactly like pgn_find_valid_moves() but returns immediately after a
486 * valid move is found.
488 static int check_mate_thingy(GAME g
, BOARD b
, int file
, int rank
)
491 register int p
= pgn_piece_to_int(b
[RANKTOBOARD(rank
)][FILETOBOARD(file
)].icon
);
493 for (r
= 1; VALIDRANK(r
); r
++) {
494 for (f
= 1; VALIDFILE(f
); f
++) {
495 if (val_piece_side(g
.turn
, b
[RANKTOBOARD(r
)][FILETOBOARD(f
)].icon
))
498 if (find_source_square(g
, b
, p
, &file
, &rank
, f
, r
) != 0)
506 static int checkmate_test(GAME g
, BOARD b
)
510 register int kf
= kfile
, kr
= krank
, okf
= okfile
, okr
= okrank
;
513 * The king squares need to be switched also for find_source_squares()
514 * which calls check_self().
517 kfile
= okfile
, krank
= okrank
;
519 for (r
= 1; VALIDRANK(r
); r
++) {
520 for (f
= 1; VALIDFILE(f
); f
++) {
522 int sfile
= f
, srank
= r
;
524 p
= b
[RANKTOBOARD(r
)][FILETOBOARD(f
)].icon
;
525 pi
= pgn_piece_to_int(p
);
527 if (p
== OPEN_SQUARE
|| !val_piece_side(g
.turn
, p
))
530 if (check_mate_thingy(g
, b
, sfile
, srank
)) {
539 kfile
= kf
, krank
= kr
, okfile
= okf
, okrank
= okr
;
543 static int find_source_square(GAME
, BOARD
, int, int *, int *, int, int);
544 static int check_opponent(GAME g
, BOARD b
, register int file
, register int rank
)
549 for (r
= 1; VALIDRANK(r
); r
++) {
550 for (f
= 1; VALIDFILE(f
); f
++) {
551 p
= b
[RANKTOBOARD(r
)][FILETOBOARD(f
)].icon
;
552 pi
= pgn_piece_to_int(p
);
554 if (pi
== OPEN_SQUARE
|| !val_piece_side(g
.turn
, p
))
557 if (find_source_square(g
, b
, pi
, &f
, &r
, file
, rank
) != 0)
565 static int check_self(GAME g
, BOARD b
, int file
, int rank
)
572 for (r
= 1; VALIDRANK(r
); r
++) {
573 for (f
= 1; VALIDFILE(f
); f
++) {
574 p
= b
[RANKTOBOARD(r
)][FILETOBOARD(f
)].icon
;
575 pi
= pgn_piece_to_int(p
);
577 if (pi
== OPEN_SQUARE
|| !val_piece_side(g
.turn
, p
))
580 if (find_source_square(g
, b
, pi
, &f
, &r
, file
, rank
) != 0) {
591 static int check_test(GAME g
, BOARD b
)
593 check
= check_opponent(g
, b
, okfile
, okrank
);
596 return checkmate_test(g
, b
);
602 static int validate_pawn(GAME g
, BOARD b
, register int sfile
,
603 register int srank
, register int file
, register int rank
)
605 register int n
= abs(srank
- rank
);
608 if (abs(sfile
- file
) > 1)
611 if (g
.turn
== WHITE
) {
612 if ((srank
== 2 && n
> 2) || (srank
> 2 && n
> 1))
616 if ((srank
== 7 && n
> 2) || (srank
< 7 && n
> 1))
620 if (n
> 1 && abs(sfile
- file
) != 0)
623 p
= b
[RANKTOBOARD(srank
)][FILETOBOARD(sfile
)].icon
;
625 if (!val_piece_side(g
.turn
, p
) || pgn_piece_to_int(p
) != PAWN
)
628 if (srank
== rank
|| (g
.turn
== WHITE
&& rank
< srank
) ||
629 (g
.turn
== BLACK
&& rank
> srank
))
633 p
= b
[RANKTOBOARD(rank
)][FILETOBOARD(file
)].icon
;
635 if (pgn_piece_to_int(p
) != OPEN_SQUARE
)
638 p
= (g
.turn
== WHITE
) ? rank
- 1 : rank
+ 1;
639 p
= b
[RANKTOBOARD(p
)][FILETOBOARD(file
)].icon
;
641 if (n
> 1 && pgn_piece_to_int(p
) != OPEN_SQUARE
)
647 p
= b
[RANKTOBOARD(rank
)][FILETOBOARD(file
)].icon
;
649 if (pgn_piece_to_int(p
) != OPEN_SQUARE
) {
650 if (val_piece_side(g
.turn
, p
))
657 p
= (g
.turn
== WHITE
) ? rank
- 1 : rank
+ 1;
658 p
= b
[RANKTOBOARD(p
)][FILETOBOARD(file
)].icon
;
660 if (pgn_piece_to_int(p
) == OPEN_SQUARE
|| val_piece_side(g
.turn
, p
))
663 /* Previous move was not 2 squares and a pawn. */
664 if (!TEST_FLAG(g
.flags
, GF_ENPASSANT
) ||
665 b
[RANKTOBOARD(rank
)][FILETOBOARD(file
)].enpassant
== 0)
669 // GF_ENPASSANT should take care of this
670 if (!b[RANKTOBOARD(rank)][FILETOBOARD(file)].enpassant)
674 if ((g
.turn
== WHITE
&& rank
!= 6) || (g
.turn
== BLACK
&& rank
!= 3))
678 // GF_ENPASSANT should take care of this
679 n
= (g
.turn
== WHITE
) ? rank
- 1 : rank
+ 1;
680 p
= b
[RANKTOBOARD(n
)][FILETOBOARD(file
)].icon
;
682 if (pgn_piece_to_int(p
) != PAWN
)
685 if (val_piece_side(g
.turn
, p
))
692 static int finalize_move(GAME
*g
, BOARD b
, int promo
, int sfile
, int srank
,
694 static int find_source_square(GAME g
, BOARD b
, int piece
, int *sfile
,
695 int *srank
, register int file
, register int rank
)
698 register int r
, f
, i
;
699 register int dist
= 0;
703 if (!*srank
&& *sfile
== file
) {
704 /* Find the first pawn in 'file'. */
705 i
= (g
.turn
== WHITE
) ? -1 : 1;
707 for (r
= rank
+ i
, dist
= 0; VALIDFILE(r
); r
+= i
, dist
++) {
708 p
= pgn_piece_to_int(b
[RANKTOBOARD(r
)][FILETOBOARD(file
)].icon
);
710 if (p
!= OPEN_SQUARE
)
718 *srank
= (g
.turn
== WHITE
) ? rank
- 1 : rank
+ 1;
720 if (!validate_pawn(g
, b
, *sfile
, *srank
, file
, rank
))
726 if (*sfile
&& *srank
)
727 count
= find_ambiguous(g
, b
, piece
, *sfile
, *srank
, file
, rank
);
729 for (r
= 1; VALIDRANK(r
); r
++) {
730 for (f
= 1; VALIDFILE(f
); f
++) {
733 if ((*sfile
&& f
!= *sfile
) || (*srank
&& r
!= *srank
))
736 n
= find_ambiguous(g
, b
, piece
, f
, r
, file
, rank
);
747 if (validate_piece(g
, b
, piece
, *sfile
, *srank
, file
, rank
) != E_PGN_OK
)
754 if (!check_testing
) {
758 int nkfile
, nkrank
, nokfile
, nokrank
;
762 memcpy(tmpb
, b
, sizeof(BOARD
));
763 memcpy(&newg
, &g
, sizeof(GAME
));
765 if (finalize_move(&newg
, tmpb
, 0, *sfile
, *srank
, file
, rank
)
773 find_king_squares(newg
, tmpb
, &nkfile
, &nkrank
, &nokfile
, &nokrank
);
775 nkfile
= kfile
, nkrank
= krank
;
777 if (check_self(newg
, tmpb
, nkfile
, nkrank
) == CHECK_SELF
) {
790 static int finalize_move(GAME
*g
, BOARD b
, int promo
, int sfile
, int srank
,
795 p
= b
[RANKTOBOARD(rank
)][FILETOBOARD(file
)].icon
;
796 pi
= pgn_piece_to_int(p
);
798 if (pi
!= OPEN_SQUARE
&& val_piece_side(g
->turn
, p
))
799 return E_PGN_INVALID
;
802 pgn_reset_enpassant(b
);
803 p
= b
[RANKTOBOARD(srank
)][FILETOBOARD(sfile
)].icon
;
804 pi
= pgn_piece_to_int(p
);
807 p
= b
[RANKTOBOARD(rank
)][FILETOBOARD(file
)].icon
;
809 if (sfile
!= file
&& pgn_piece_to_int(p
) == OPEN_SQUARE
&&
810 TEST_FLAG(g
->flags
, GF_ENPASSANT
)) {
811 p
= (g
->turn
== WHITE
) ? rank
- 1 : rank
+ 1;
812 b
[RANKTOBOARD(p
)][FILETOBOARD(file
)].icon
=
813 pgn_int_to_piece(g
->turn
, OPEN_SQUARE
);
816 if (abs(srank
- rank
) > 1) {
817 SET_FLAG(g
->flags
, GF_ENPASSANT
);
818 b
[RANKTOBOARD(((g
->turn
== WHITE
) ? rank
- 1 : rank
+ 1))][FILETOBOARD(file
)].enpassant
= 1;
821 else if (pi
== ROOK
) {
822 if (g
->turn
== WHITE
) {
823 if (sfile
== FILETOINT('h'))
824 CLEAR_FLAG(g
->flags
, GF_WK_CASTLE
);
825 else if (sfile
== FILETOINT('a'))
826 CLEAR_FLAG(g
->flags
, GF_WQ_CASTLE
);
829 if (sfile
== FILETOINT('h'))
830 CLEAR_FLAG(g
->flags
, GF_BK_CASTLE
);
831 else if (sfile
== FILETOINT('a'))
832 CLEAR_FLAG(g
->flags
, GF_BQ_CASTLE
);
837 CLEAR_FLAG(g
->flags
, GF_ENPASSANT
);
839 if (pi
== KING
&& !castle
) {
840 if (g
->turn
== WHITE
)
841 CLEAR_FLAG(g
->flags
, GF_WK_CASTLE
|GF_WQ_CASTLE
);
843 CLEAR_FLAG(g
->flags
, GF_BK_CASTLE
|GF_BQ_CASTLE
);
847 p
= b
[RANKTOBOARD(srank
)][FILETOBOARD(sfile
)].icon
;
848 b
[RANKTOBOARD(srank
)][FILETOBOARD(sfile
)].icon
=
849 pgn_int_to_piece(g
->turn
, OPEN_SQUARE
);
850 b
[RANKTOBOARD(srank
)][FILETOBOARD(
851 (file
> FILETOINT('e') ? 8 : 1))].icon
=
852 pgn_int_to_piece(g
->turn
, OPEN_SQUARE
);
853 b
[RANKTOBOARD(rank
)][FILETOBOARD(file
)].icon
= p
;
855 if (file
> FILETOINT('e'))
856 b
[RANKTOBOARD(rank
)][FILETOBOARD((file
- 1))].icon
=
857 pgn_int_to_piece(g
->turn
, ROOK
);
859 b
[RANKTOBOARD(rank
)][FILETOBOARD((file
+ 1))].icon
=
860 pgn_int_to_piece(g
->turn
, ROOK
);
862 if (g
->turn
== WHITE
)
863 CLEAR_FLAG(g
->flags
, (file
> FILETOINT('e')) ? GF_WK_CASTLE
:
866 CLEAR_FLAG(g
->flags
, (file
> FILETOINT('e')) ? GF_BK_CASTLE
:
871 p
= pgn_int_to_piece(g
->turn
, promo
);
873 p
= b
[RANKTOBOARD(srank
)][FILETOBOARD(sfile
)].icon
;
875 b
[RANKTOBOARD(srank
)][FILETOBOARD(sfile
)].icon
=
876 pgn_int_to_piece(g
->turn
, OPEN_SQUARE
);
877 b
[RANKTOBOARD(rank
)][FILETOBOARD(file
)].icon
= p
;
883 if (!check_testing
) {
884 if (pgn_piece_to_int(p
) == KING
)
885 find_king_squares(*g
, b
, &kfile
, &krank
, &okfile
, &okrank
);
887 switch (check_test(*g
, b
)) {
895 pgn_tag_add(&g
->tag
, "Result",
896 (g
->turn
== WHITE
) ? "1-0" : "0-1");
897 SET_FLAG(g
->flags
, GF_GAMEOVER
);
905 if (!validate
&& (pgn_fen_tag
> 0 && !done_fen_tag
) &&
906 !pgn_history_total(g
->hp
) && srank
>= 7)
907 SET_FLAG(g
->flags
, GF_BLACK_OPENING
);
911 p
= pgn_piece_to_int(p
);
913 if (p
== PAWN
|| promo
|| capture
)
918 if (g
->ply
/ 2 == 50) {
919 pgn_tag_add(&g
->tag
, "Result", "1-2/1-2");
920 SET_FLAG(g
->flags
, GF_GAMEOVER
);
928 * Converts a2a3 formatted moves to SAN format. The promotion piece should be
931 static int frfrtosan(GAME
*g
, BOARD b
, char **m
)
934 int icon
, p
, dp
, promo
= 0;
935 int sfile
, srank
, file
, rank
;
939 sfile
= FILETOINT(bp
[0]);
940 srank
= RANKTOINT(bp
[1]);
941 file
= FILETOINT(bp
[2]);
942 rank
= RANKTOINT(bp
[3]);
945 if ((promo
= pgn_piece_to_int(bp
[4])) == -1 || promo
== OPEN_SQUARE
)
949 icon
= b
[RANKTOBOARD(srank
)][FILETOBOARD(sfile
)].icon
;
951 if ((p
= pgn_piece_to_int(icon
)) == -1 || p
== OPEN_SQUARE
)
954 if (p
!= PAWN
&& promo
)
955 return E_PGN_INVALID
;
958 if (find_source_square(*g
, b
, p
, &sfile
, &srank
, file
, rank
) != 1)
959 return E_PGN_INVALID
;
964 if (validate_piece(*g
, b
, p
, sfile
, srank
, file
, rank
) != E_PGN_OK
)
965 return E_PGN_INVALID
;
967 if (p
== KING
&& abs(sfile
- file
) > 1) {
968 strcpy(bp
, (file
> FILETOINT('e')) ? "O-O" : "O-O-O");
970 if (finalize_move(g
, b
, promo
, sfile
, srank
, file
, rank
) != E_PGN_OK
)
971 return E_PGN_INVALID
;
974 strcat(bp
, (check
== CHECK
) ? "+" : "#");
979 *bp
++ = toupper(icon
);
980 n
= find_ambiguous(*g
, b
, p
, 0, 0, file
, rank
);
983 return E_PGN_INVALID
;
985 fc
= find_ambiguous(*g
, b
, p
, sfile
, 0, file
, rank
);
986 rc
= find_ambiguous(*g
, b
, p
, 0, srank
, file
, rank
);
989 *bp
++ = INTTOFILE(sfile
);
991 *bp
++ = INTTORANK(srank
);
994 *bp
++ = INTTORANK(srank
);
996 *bp
++ = INTTOFILE(sfile
);
997 *bp
++ = INTTORANK(srank
);
1001 return E_PGN_PARSE
; // not reached.
1005 icon
= b
[RANKTOBOARD(rank
)][FILETOBOARD(file
)].icon
;
1007 if ((dp
= pgn_piece_to_int(icon
)) == -1)
1013 if (dp
!= OPEN_SQUARE
|| (dp
== OPEN_SQUARE
&& p
== PAWN
&& sfile
!= file
)) {
1015 *bp
++ = INTTOFILE(sfile
);
1024 *bp
++ = INTTOFILE(file
);
1025 *bp
++ = INTTORANK(rank
);
1031 if (p
!= PAWN
|| (g
->turn
== WHITE
&& (srank
!= 7 || rank
!= 8)) ||
1032 (g
->turn
== BLACK
&& (srank
!= 2 || rank
!= 1)))
1033 return E_PGN_INVALID
;
1036 *bp
++ = pgn_int_to_piece(WHITE
, promo
);
1041 if (find_source_square(*g
, b
, p
, &sfile
, &srank
, file
, rank
) != 1)
1042 return E_PGN_INVALID
;
1044 if (finalize_move(g
, b
, promo
, sfile
, srank
, file
, rank
) != E_PGN_OK
)
1045 return E_PGN_INVALID
;
1048 *bp
++ = (check
== CHECK
) ? '+' : '#';
1055 * Valididate move 'mp' against the game state 'g' and game board 'b' and
1056 * update board 'b'. 'mp' is updated to SAN format for moves which aren't
1057 * (frfr or e8Q for example). Returns E_PGN_PARSE if there was a move text
1058 * parsing error, E_PGN_INVALID if the move is invalid or E_PGN_OK if
1061 int pgn_parse_move(GAME
*g
, BOARD b
, char **mp
)
1066 int srank
= 0, sfile
= 0, rank
, file
;
1070 capture
= check_testing
= 0;
1071 srank
= rank
= file
= sfile
= promo
= piece
= 0;
1072 find_king_squares(*g
, b
, &kfile
, &krank
, &okfile
, &okrank
);
1074 if (VALIDCOL(*m
) && VALIDROW(*(m
+ 1)) && VALIDCOL(*(m
+ 2)) &&
1076 return frfrtosan(g
, b
, mp
);
1077 else if (*m
== 'O') {
1078 if (strcmp(m
, "O-O") == 0)
1080 else if (strcmp(m
, "O-O-O") == 0)
1085 if (parse_castle_move(*g
, b
, i
, &sfile
, &srank
, &file
, &rank
) !=
1087 return E_PGN_INVALID
;
1089 *m
++ = INTTOFILE(sfile
);
1090 *m
++ = INTTORANK(srank
);
1091 *m
++ = INTTOFILE(file
);
1092 *m
++ = INTTORANK(rank
);
1094 return frfrtosan(g
, b
, mp
);
1101 p
= (m
) + strlen(m
);
1103 while (!isdigit(*--p
) && *p
!= 'O') {
1105 promo
= toupper(pgn_piece_to_int(i
));
1114 /* Alternate promotion text (e8Q). Convert to SAN. */
1115 if (pgn_piece_to_int(i
) != -1) {
1116 p
= (m
) + strlen(m
);
1125 /* Skip 'P' (pawn). */
1126 if (pgn_piece_to_int(*p
) == PAWN
)
1131 for (i
= 0; *p
; i
++) {
1134 file
= FILETOINT(*p
++);
1136 file
= sfile
= FILETOINT(*p
++);
1138 else if (VALIDROW(*p
)) {
1140 rank
= RANKTOINT(*p
++);
1142 rank
= RANKTOINT(*p
++);
1144 else if (*p
== 'x') {
1145 file
= FILETOINT(*++p
);
1146 rank
= RANKTOINT(*++p
);
1149 else if (*p
== '=') {
1150 if (promo
== -1 || promo
== KING
|| promo
== PAWN
)
1154 *p
++ = toupper(pgn_int_to_piece(g
->turn
, promo
));
1160 DUMP("Pawn (move: '%s'): %c\n", m
, *p
++);
1167 if (find_source_square(*g
, b
, PAWN
, &sfile
, &srank
, file
, rank
) != 1)
1168 return E_PGN_INVALID
;
1177 * The first character is the piece but only if not a pawn.
1179 if ((piece
= pgn_piece_to_int(*p
++)) == -1)
1185 if (strlen(m
) > 3) {
1190 srank
= RANKTOINT(*p
++);
1194 else if (VALIDCOL(*p
)) {
1195 sfile
= FILETOINT(*p
++);
1201 srank
= RANKTOINT(*p
++);
1216 * The destination square.
1218 file
= FILETOINT(*p
++);
1219 rank
= RANKTOINT(*p
++);
1224 if ((i
= find_ambiguous(*g
, b
, piece
, sfile
, srank
, file
, rank
))
1226 return (i
== 0) ? E_PGN_INVALID
: E_PGN_AMBIGUOUS
;
1229 * The move is a valid one. Find the source file and rank so we
1230 * can later update the board positions.
1232 if (find_source_square(*g
, b
, piece
, &sfile
, &srank
, file
, rank
)
1234 return E_PGN_INVALID
;
1239 if (finalize_move(g
, b
, promo
, sfile
, srank
, file
, rank
) != E_PGN_OK
)
1240 return E_PGN_INVALID
;
1243 *p
++ = (check
== CHECK
) ? '+' : '#';
1251 * Like pgn_parse_move() but don't modify game flags in 'g' or board 'b'.
1253 int pgn_validate_move(GAME
*g
, BOARD b
, char **m
)
1258 ret
= pgn_parse_move(g
, b
, m
);
1264 * Sets valid moves from game 'g' using board 'b'. The valid moves are for the
1265 * piece on the board 'b' at 'rank' and 'file'. Returns nothing.
1267 void pgn_find_valid_moves(GAME g
, BOARD b
, int file
, int rank
)
1269 register int p
= pgn_piece_to_int(b
[RANKTOBOARD(rank
)][FILETOBOARD(file
)].icon
);
1272 find_king_squares(g
, b
, &kfile
, &krank
, &okfile
, &okrank
);
1274 for (r
= 1; VALIDRANK(r
); r
++) {
1275 for (f
= 1; VALIDFILE(f
); f
++) {
1276 if (val_piece_side(g
.turn
, b
[RANKTOBOARD(r
)][FILETOBOARD(f
)].icon
))
1279 if (find_source_square(g
, b
, p
, &file
, &rank
, f
, r
) != 0)
1280 b
[RANKTOBOARD(r
)][FILETOBOARD(f
)].valid
= 1;