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
42 int piece_side(GAME g
, int c
)
44 if (c
== pgn_int_to_piece(g
.turn
, OPEN_SQUARE
))
48 c
= pgn_int_to_piece(g
.turn
, c
);
50 return (isupper(c
)) ? WHITE
: BLACK
;
53 int val_piece_side(char turn
, int c
)
55 if ((isupper(c
) && turn
== WHITE
) ||
56 (islower(c
) && turn
== BLACK
))
63 * Get the source row and column for a given piece.
65 * The following two functions find 'piece' from the given square 'col' and
66 * 'row' and store the resulting column or row in 'c' and 'r'. The return
67 * value is the number of 'piece' found (on the current g.side) or zero.
68 * Search for 'piece' stops when a non-empty square is found.
70 int count_by_diag(GAME g
, BOARD b
, int piece
, int row
, int col
)
73 int ul
= 0, ur
= 0, dl
= 0, dr
= 0;
76 for (i
= 1; VALIDFILE(i
); i
++) {
83 if (!ul
&& VALIDRANK(r
) && VALIDFILE(c
)) {
84 n
= b
[ROWTOBOARD(r
)][COLTOBOARD(c
)].icon
;
86 if (pgn_piece_to_int(n
) != OPEN_SQUARE
) {
87 if (pgn_piece_to_int(n
) == piece
&& val_piece_side(g
.turn
, n
))
97 if (!ur
&& VALIDRANK(r
) && VALIDFILE(c
)) {
98 n
= b
[ROWTOBOARD(r
)][COLTOBOARD(c
)].icon
;
100 if (pgn_piece_to_int(n
) != OPEN_SQUARE
) {
101 if (pgn_piece_to_int(n
) == piece
&& val_piece_side(g
.turn
, n
))
111 if (!dl
&& VALIDRANK(r
) && VALIDFILE(c
)) {
112 n
= b
[ROWTOBOARD(r
)][COLTOBOARD(c
)].icon
;
114 if (pgn_piece_to_int(n
) != OPEN_SQUARE
) {
115 if (pgn_piece_to_int(n
) == piece
&& val_piece_side(g
.turn
, n
))
125 if (!dr
&& VALIDRANK(r
) && VALIDFILE(c
)) {
126 n
= b
[ROWTOBOARD(r
)][COLTOBOARD(c
)].icon
;
128 if (pgn_piece_to_int(n
) != OPEN_SQUARE
) {
129 if (pgn_piece_to_int(n
) == piece
&& val_piece_side(g
.turn
, n
))
140 int count_knight(GAME g
, BOARD b
, int row
, int col
)
149 if (VALIDFILE(r
) && VALIDFILE(c
)) {
150 p
= b
[ROWTOBOARD(r
)][COLTOBOARD(c
)].icon
;
152 if (pgn_piece_to_int(p
) == KNIGHT
&& val_piece_side(g
.turn
, p
))
159 if (VALIDFILE(r
) && VALIDFILE(c
)) {
160 p
= b
[ROWTOBOARD(r
)][COLTOBOARD(c
)].icon
;
162 if (pgn_piece_to_int(p
) == KNIGHT
&& val_piece_side(g
.turn
, p
))
169 if (VALIDFILE(r
) && VALIDFILE(c
)) {
170 p
= b
[ROWTOBOARD(r
)][COLTOBOARD(c
)].icon
;
172 if (pgn_piece_to_int(p
) == KNIGHT
&& val_piece_side(g
.turn
, p
))
178 if (VALIDFILE(r
) && VALIDFILE(c
)) {
179 p
= b
[ROWTOBOARD(r
)][COLTOBOARD(c
)].icon
;
181 if (pgn_piece_to_int(p
) == KNIGHT
&& val_piece_side(g
.turn
, p
))
188 if (VALIDFILE(r
) && VALIDFILE(c
)) {
189 p
= b
[ROWTOBOARD(r
)][COLTOBOARD(c
)].icon
;
191 if (pgn_piece_to_int(p
) == KNIGHT
&& val_piece_side(g
.turn
, p
))
198 if (VALIDFILE(r
) && VALIDFILE(c
)) {
199 p
= b
[ROWTOBOARD(r
)][COLTOBOARD(c
)].icon
;
201 if (pgn_piece_to_int(p
) == KNIGHT
&& val_piece_side(g
.turn
, p
))
208 if (VALIDFILE(r
) && VALIDFILE(c
)) {
209 p
= b
[ROWTOBOARD(r
)][COLTOBOARD(c
)].icon
;
211 if (pgn_piece_to_int(p
) == KNIGHT
&& val_piece_side(g
.turn
, p
))
218 if (VALIDFILE(r
) && VALIDFILE(c
)) {
219 p
= b
[ROWTOBOARD(r
)][COLTOBOARD(c
)].icon
;
221 if (pgn_piece_to_int(p
) == KNIGHT
&& val_piece_side(g
.turn
, p
))
228 int count_by_rank(GAME g
, BOARD b
, int piece
, int row
, int col
)
234 for (i
= 1; VALIDRANK(i
); i
++) {
237 if (!u
&& VALIDRANK(row
+ i
)) {
238 n
= b
[ROWTOBOARD(row
+ i
)][COLTOBOARD(col
)].icon
;
240 if (pgn_piece_to_int(n
) != OPEN_SQUARE
) {
241 if (pgn_piece_to_int(n
) == piece
&& val_piece_side(g
.turn
, n
))
248 if (!d
&& VALIDRANK(row
- i
)) {
249 n
= b
[ROWTOBOARD(row
- i
)][COLTOBOARD(col
)].icon
;
251 if (pgn_piece_to_int(n
) != OPEN_SQUARE
) {
252 if (pgn_piece_to_int(n
) == piece
&& val_piece_side(g
.turn
, n
))
263 int count_by_file(GAME g
, BOARD b
, int piece
, int row
, int col
)
269 for (i
= 1; VALIDFILE(i
); i
++) {
272 if (!r
&& VALIDFILE(col
+ i
)) {
273 n
= b
[ROWTOBOARD(row
)][COLTOBOARD(col
+ i
)].icon
;
275 if (pgn_piece_to_int(n
) != OPEN_SQUARE
) {
276 if (pgn_piece_to_int(n
) == piece
&& val_piece_side(g
.turn
, n
))
283 if (!l
&& VALIDFILE(col
- i
)) {
284 n
= b
[ROWTOBOARD(row
)][COLTOBOARD(col
- i
)].icon
;
286 if (pgn_piece_to_int(n
) != OPEN_SQUARE
) {
287 if (pgn_piece_to_int(n
) == piece
&& val_piece_side(g
.turn
, n
))
298 int count_by_rank_file(GAME g
, BOARD b
, int piece
, int row
, int col
)
302 count
= count_by_rank(g
, b
, piece
, row
, col
);
303 return count
+ count_by_file(g
, b
, piece
, row
, col
);
306 int piece_count(GAME g
, BOARD b
, int piece
, int row
, int col
, int *r
, int *c
)
313 count
= count_by_rank_file(g
, b
, piece
, row
, col
);
316 count
= count_by_diag(g
, b
, piece
, row
, col
);
320 count
= count_by_rank_file(g
, b
, piece
, row
, col
);
321 count
+= count_by_diag(g
, b
, piece
, row
, col
);
324 count
= count_knight(g
, b
, row
, col
);
331 int piece_by_col(GAME g
, BOARD b
, int piece
, int row
, int col
, int *r
, int *c
)
336 for (i
= col
- 1; VALIDFILE(i
); i
--) {
337 int n
= b
[ROWTOBOARD(row
)][COLTOBOARD(i
)].icon
;
339 if (pgn_piece_to_int(n
) != OPEN_SQUARE
) {
340 if (pgn_piece_to_int(n
) == piece
&& val_piece_side(g
.side
, n
)) {
350 for (i
= col
+ 1; VALIDFILE(i
); i
++) {
351 int n
= b
[ROWTOBOARD(row
)][COLTOBOARD(i
)].icon
;
353 if (pgn_piece_to_int(n
) != OPEN_SQUARE
) {
354 if (pgn_piece_to_int(n
) == piece
&& val_piece_side(g
.side
, n
)) {
367 int piece_by_row(GAME g
, BOARD b
, int piece
, int row
, int col
, int *r
, int *c
)
372 for (i
= row
+ 1; VALIDFILE(i
); i
++) {
373 int n
= b
[ROWTOBOARD(i
)][COLTOBOARD(col
)].icon
;
375 if (pgn_piece_to_int(n
) != OPEN_SQUARE
) {
376 if (pgn_piece_to_int(n
) == piece
&& val_piece_side(g
.turn
, n
)) {
386 for (i
= row
- 1; VALIDFILE(i
); i
--) {
387 int n
= b
[ROWTOBOARD(i
)][COLTOBOARD(col
)].icon
;
389 if (pgn_piece_to_int(n
) != OPEN_SQUARE
) {
390 if (pgn_piece_to_int(n
) == piece
&& val_piece_side(g
.turn
, n
)) {
403 int piece_by_xy(GAME g
, BOARD b
, int piece
, int row
, int col
, int *srow
, int *scol
)
407 count
= piece_by_row(g
, b
, piece
, row
, col
, srow
, scol
);
408 count
+= piece_by_col(g
, b
, piece
, row
, col
, srow
, scol
);
409 return (count
!= 1) ? 0 : 1;
412 int piece_test(GAME g
, BOARD b
, int piece
, int row
, int col
, int *dstr
, int *dstc
)
416 if (!VALIDFILE(row
) || !VALIDFILE(col
))
419 p
= b
[ROWTOBOARD(row
)][COLTOBOARD(col
)].icon
;
421 if (pgn_piece_to_int(p
) != OPEN_SQUARE
) {
422 if (pgn_piece_to_int(p
) == piece
&& val_piece_side(g
.turn
, p
)) {
434 int piece_by_diag(GAME g
, BOARD b
, int piece
, int row
, int col
, int *srow
, int *scol
)
437 int ul
= 1, ur
= 1, dl
= 1, dr
= 1;
440 for (i
= 1; VALIDFILE(i
); i
++) {
442 n
= piece_test(g
, b
, piece
, row
- i
, col
+ i
, srow
, scol
);
444 if (n
== 1 && count
++)
451 n
= piece_test(g
, b
, piece
, row
- i
, col
- i
, srow
, scol
);
453 if (n
== 1 && count
++)
460 n
= piece_test(g
, b
, piece
, row
+ i
, col
+ i
, srow
, scol
);
462 if (n
== 1 && count
++)
469 n
= piece_test(g
, b
, piece
, row
+ i
, col
- i
, srow
, scol
);
471 if (n
== 1 && count
++)
478 return (count
) ? 1 : 0;
481 int valid_move(GAME g
, BOARD b
, int row
, int col
, int srow
, int scol
)
485 if (!VALIDFILE(srow
) || !VALIDFILE(scol
))
488 if (row
== srow
&& col
== scol
)
491 p1
= b
[ROWTOBOARD(srow
)][COLTOBOARD(scol
)].icon
;
492 p2
= b
[ROWTOBOARD(row
)][COLTOBOARD(col
)].icon
;
494 if (pgn_piece_to_int(p1
) == OPEN_SQUARE
)
497 if (piece_side(g
, p1
) == piece_side(g
, p2
))
500 if (pgn_piece_to_int(p1
) == PAWN
&& scol
== col
&&
501 pgn_piece_to_int(p2
) != OPEN_SQUARE
)
507 int castle_move(GAME
*g
, BOARD b
, char which
)
513 row
= (g
->turn
== WHITE
) ? 1 : 8;
516 if (which
== KINGSIDE
) {
517 if ((g
->turn
== WHITE
&& (!TEST_FLAG(g
->flags
, GF_WK_CASTLE
))) ||
518 (g
->turn
== BLACK
&& (!TEST_FLAG(g
->flags
, GF_BK_CASTLE
))))
521 p
= b
[ROWTOBOARD(row
)][COLTOBOARD((n
+ 1))].icon
;
522 p2
= b
[ROWTOBOARD(row
)][COLTOBOARD((n
+ 2))].icon
;
523 p3
= b
[ROWTOBOARD(row
)][COLTOBOARD((n
+ 3))].icon
;
525 if (pgn_piece_to_int(p
) != OPEN_SQUARE
|| pgn_piece_to_int(p2
) != OPEN_SQUARE
526 || (pgn_piece_to_int(p3
) != ROOK
&& val_piece_side(g
->turn
, p3
)))
530 b
[ROWTOBOARD(row
)][COLTOBOARD(COLTOINT('e'))].icon
=
531 pgn_int_to_piece(g
->turn
, OPEN_SQUARE
);
532 b
[ROWTOBOARD(row
)][COLTOBOARD(6)].icon
= pgn_int_to_piece(g
->turn
, ROOK
);
533 b
[ROWTOBOARD(row
)][COLTOBOARD(7)].icon
= pgn_int_to_piece(g
->turn
, KING
);
534 b
[ROWTOBOARD(row
)][COLTOBOARD(8)].icon
= pgn_int_to_piece(g
->turn
, OPEN_SQUARE
);
536 if (g
->turn
== WHITE
)
537 CLEAR_FLAG(g
->flags
, GF_WK_CASTLE
);
538 else if (g
->turn
== BLACK
)
539 CLEAR_FLAG(g
->flags
, GF_BK_CASTLE
);
543 if ((g
->turn
== WHITE
&& (!TEST_FLAG(g
->flags
, GF_WQ_CASTLE
))) ||
544 (g
->turn
== BLACK
&& (!TEST_FLAG(g
->flags
, GF_BQ_CASTLE
))))
547 p
= b
[ROWTOBOARD(row
)][COLTOBOARD((n
- 1))].icon
;
548 p2
= b
[ROWTOBOARD(row
)][COLTOBOARD((n
- 2))].icon
;
549 p3
= b
[ROWTOBOARD(row
)][COLTOBOARD((n
- 3))].icon
;
550 p4
= b
[ROWTOBOARD(row
)][COLTOBOARD((n
- 4))].icon
;
552 if (pgn_piece_to_int(p
) != OPEN_SQUARE
|| pgn_piece_to_int(p2
) != OPEN_SQUARE
553 || pgn_piece_to_int(p3
) != OPEN_SQUARE
||
554 (pgn_piece_to_int(p4
) != ROOK
&& val_piece_side(g
->turn
, p4
)))
558 b
[ROWTOBOARD(row
)][COLTOBOARD(1)].icon
= pgn_int_to_piece(g
->turn
, OPEN_SQUARE
);
559 b
[ROWTOBOARD(row
)][COLTOBOARD(COLTOINT('e'))].icon
=
560 pgn_int_to_piece(g
->turn
, OPEN_SQUARE
);
561 b
[ROWTOBOARD(row
)][COLTOBOARD(3)].icon
= pgn_int_to_piece(g
->turn
, KING
);
562 b
[ROWTOBOARD(row
)][COLTOBOARD(4)].icon
= pgn_int_to_piece(g
->turn
, ROOK
);
564 if (g
->turn
== WHITE
)
565 CLEAR_FLAG(g
->flags
, GF_WQ_CASTLE
);
566 else if (g
->turn
== BLACK
)
567 CLEAR_FLAG(g
->flags
, GF_BQ_CASTLE
);
574 int get_source_yx(GAME
*g
, BOARD b
, int piece
, int row
, int col
, int *srow
, int *scol
)
582 /* FIXME valid move ambiguities. */
585 if (*srow
== 0 && *scol
== col
) {
586 i
= (g
->turn
== WHITE
) ? -1 : 1;
588 /* Find the first pawn in the current column. */
589 for (r
= row
+ i
, dist
= 0; VALIDFILE(r
); r
+= i
, dist
++) {
590 int n
= b
[ROWTOBOARD(r
)][COLTOBOARD(col
)].icon
;
592 p
= pgn_piece_to_int(n
);
594 if (p
== PAWN
&& val_piece_side(g
->turn
, n
))
598 if (p
!= PAWN
|| dist
> 2)
602 dist
= abs(*srow
- row
);
604 if (g
->turn
== WHITE
) {
605 if ((*srow
== 2 && dist
> 2) || (*srow
> 2 && dist
> 1))
609 if ((*srow
== 7 && dist
> 2) || (*srow
< 7 && dist
> 1))
614 p
= pgn_piece_to_int(b
[ROWTOBOARD(*srow
+ i
)][COLTOBOARD(col
)].icon
);
615 if (p
!= OPEN_SQUARE
)
619 else if (*scol
!= col
) {
620 if (abs(*scol
- col
) != 1)
623 *srow
= (g
->turn
== WHITE
) ? row
- 1 : row
+ 1;
625 if (pgn_piece_to_int(b
[ROWTOBOARD(*srow
)][COLTOBOARD(*scol
)].icon
)
629 piece
= pgn_piece_to_int(b
[ROWTOBOARD(row
)][COLTOBOARD(col
)].icon
);
632 if (piece
== OPEN_SQUARE
) {
633 /* Previous move was not 2 squares and a pawn. */
634 if (!TEST_FLAG(g
->flags
, GF_ENPASSANT
))
637 if (!b
[ROWTOBOARD(row
)][COLTOBOARD(col
)].enpassant
)
640 r
= (g
->turn
== WHITE
) ? 6 : 3;
645 r
= (g
->turn
== WHITE
) ? row
- 1 : row
+ 1;
646 piece
= b
[ROWTOBOARD(r
)][COLTOBOARD(col
)].icon
;
648 if (pgn_piece_to_int(piece
) != PAWN
)
652 b
[ROWTOBOARD(r
)][COLTOBOARD(col
)].icon
=
653 pgn_int_to_piece(g
->turn
, OPEN_SQUARE
);
658 if (piece_by_xy(*g
, b
, ROOK
, row
, col
, srow
, scol
) == 0)
661 if (!validate
&& *scol
== 1) {
662 if (g
->turn
== WHITE
)
663 CLEAR_FLAG(g
->flags
, GF_WQ_CASTLE
);
665 CLEAR_FLAG(g
->flags
, GF_BQ_CASTLE
);
667 else if (!validate
&& *scol
== 8) {
668 if (g
->turn
== WHITE
)
669 CLEAR_FLAG(g
->flags
, GF_WK_CASTLE
);
671 CLEAR_FLAG(g
->flags
, GF_BK_CASTLE
);
678 if (VALIDFILE(r
) && VALIDFILE(c
)) {
679 p
= b
[ROWTOBOARD(r
)][COLTOBOARD(c
)].icon
;
681 if (pgn_piece_to_int(p
) == KNIGHT
&& val_piece_side(g
->turn
, p
)) {
686 if ((*srow
&& *srow
== row
) || (*scol
&& *scol
== col
))
694 if (VALIDFILE(r
) && VALIDFILE(c
)) {
695 p
= b
[ROWTOBOARD(r
)][COLTOBOARD(c
)].icon
;
697 if (pgn_piece_to_int(p
) == KNIGHT
&& val_piece_side(g
->turn
, p
)) {
702 if ((*srow
&& *srow
== row
) || (*scol
&& *scol
== col
))
710 if (VALIDFILE(r
) && VALIDFILE(c
)) {
711 p
= b
[ROWTOBOARD(r
)][COLTOBOARD(c
)].icon
;
713 if (pgn_piece_to_int(p
) == KNIGHT
&& val_piece_side(g
->turn
, p
)) {
718 if ((*srow
&& *srow
== row
) || (*scol
&& *scol
== col
))
726 if (VALIDFILE(r
) && VALIDFILE(c
)) {
727 p
= b
[ROWTOBOARD(r
)][COLTOBOARD(c
)].icon
;
729 if (pgn_piece_to_int(p
) == KNIGHT
&& val_piece_side(g
->turn
, p
)) {
734 if ((*srow
&& *srow
== row
) || (*scol
&& *scol
== col
))
742 if (VALIDFILE(r
) && VALIDFILE(c
)) {
743 p
= b
[ROWTOBOARD(r
)][COLTOBOARD(c
)].icon
;
745 if (pgn_piece_to_int(p
) == KNIGHT
&& val_piece_side(g
->turn
, p
)) {
750 if ((*srow
&& *srow
== row
) || (*scol
&& *scol
== col
))
758 if (VALIDFILE(r
) && VALIDFILE(c
)) {
759 p
= b
[ROWTOBOARD(r
)][COLTOBOARD(c
)].icon
;
761 if (pgn_piece_to_int(p
) == KNIGHT
&& val_piece_side(g
->turn
, p
)) {
766 if ((*srow
&& *srow
== row
) || (*scol
&& *scol
== col
))
774 if (VALIDFILE(r
) && VALIDFILE(c
)) {
775 p
= b
[ROWTOBOARD(r
)][COLTOBOARD(c
)].icon
;
777 if (pgn_piece_to_int(p
) == KNIGHT
&& val_piece_side(g
->turn
, p
)) {
782 if ((*srow
&& *srow
== row
) || (*scol
&& *scol
== col
))
790 if (VALIDFILE(r
) && VALIDFILE(c
)) {
791 p
= b
[ROWTOBOARD(r
)][COLTOBOARD(c
)].icon
;
793 if (pgn_piece_to_int(p
) == KNIGHT
&& val_piece_side(g
->turn
, p
)) {
798 if ((*srow
&& *srow
== row
) || (*scol
&& *scol
== col
))
803 if ((count
!= 1 && !validate
) || (validate
&& !count
))
808 if (piece_by_diag(*g
, b
, BISHOP
, row
, col
, srow
, scol
) == 0)
812 if (piece_by_xy(*g
, b
, QUEEN
, row
, col
, srow
, scol
) == 0) {
813 if (piece_by_diag(*g
, b
, QUEEN
, row
, col
, srow
, scol
) == 0)
818 if (piece_by_xy(*g
, b
, KING
, row
, col
, srow
, scol
) == 0) {
819 if (piece_by_diag(*g
, b
, KING
, row
, col
, srow
, scol
) == 0)
823 if (abs(*srow
- row
) > 1)
826 dist
= abs(*scol
- col
);
828 if (*scol
== COLTOINT('e')) {
835 if (castle_move(g
, b
, QUEENSIDE
))
839 if (castle_move(g
, b
, KINGSIDE
))
863 if (valid_move(*g
, b
, row
, col
, *srow
, *scol
) == 0)
868 if (g
->turn
== WHITE
) {
869 CLEAR_FLAG(g
->flags
, GF_WK_CASTLE
);
870 CLEAR_FLAG(g
->flags
, GF_WQ_CASTLE
);
873 CLEAR_FLAG(g
->flags
, GF_BK_CASTLE
);
874 CLEAR_FLAG(g
->flags
, GF_BQ_CASTLE
);
882 /* This function converts a2a4 formatted moves to SAN format. Minimal checks
883 * are performed here. The real checks are in validate_move() after the
886 char *pgn_a2a4tosan(GAME
*g
, BOARD b
, char *m
)
888 static char buf
[MAX_SAN_MOVE_LEN
+ 1] = {0}, *cp
= buf
;
890 int scol
, srow
, col
, row
;
891 int piece
, piecei
, spiece
;
898 // Not in a2a4 format. Probably already in SAN format.
899 if (!VALIDCOL(*p
) || !VALIDROW(*(p
+ 1)) || !VALIDCOL(*(p
+ 2))
900 || !VALIDROW(*(p
+ 3)))
904 srow
= ROWTOINT(*(p
+ 1));
905 col
= COLTOINT(*(p
+ 2));
906 row
= ROWTOINT(*(p
+ 3));
909 if ((promo
= pgn_piece_to_int(p
[4])) == -1)
913 piece
= b
[ROWTOBOARD(srow
)][COLTOBOARD(scol
)].icon
;
915 if ((piecei
= pgn_piece_to_int(piece
)) == -1 || piecei
== OPEN_SQUARE
)
920 colc
= abs(scol
- col
);
922 if (srow
== row
&& (row
== 1 || row
== 8) && scol
== COLTOINT('e')
923 && colc
> 1 && piecei
== KING
) {
926 else if (scol
- col
> 0)
933 *cp
++ = toupper(piece
);
936 if (scol
!= col
&& pgn_piece_to_int(b
[row
][col
].icon
) == OPEN_SQUARE
)
940 colc
= piece_by_col(*g
, b
, piecei
, row
, col
, &trow
, &tcol
);
941 rowc
= piece_by_row(*g
, b
, piecei
, row
, col
, &trow
, &tcol
);
944 if (piecei
== KNIGHT
) {
945 if (get_source_yx(g
, b
, KNIGHT
, row
, col
, &trow
, &tcol
) == 1)
946 *cp
++ = INTTOCOL(scol
);
948 else if (n
> 1 && piecei
!= PAWN
) {
949 if (colc
> 1 && rowc
> 1) {
950 *cp
++ = INTTOCOL(scol
);
951 *cp
++ = INTTOROW(srow
);
954 *cp
++ = INTTOCOL(scol
);
956 *cp
++ = INTTOROW(srow
);
959 piece
= b
[ROWTOBOARD(row
)][COLTOBOARD(col
)].icon
;
961 if ((piecei
= pgn_piece_to_int(piece
)) != OPEN_SQUARE
|| tenpassant
) {
962 if (tenpassant
|| spiece
== PAWN
)
963 *cp
++ = INTTOCOL(scol
);
968 *cp
++ = INTTOCOL(col
);
969 *cp
++ = INTTOROW(row
);
973 *cp
++ = toupper(pgn_int_to_piece(g
->turn
, promo
));
980 void pgn_switch_turn(GAME
*g
)
982 g
->turn
= (g
->turn
== WHITE
) ? BLACK
: WHITE
;
985 static void kingsquare(GAME g
, BOARD b
, int *kr
, int *kc
, int *okr
, int *okc
)
989 for (row
= 1; VALIDFILE(row
); row
++) {
990 for (col
= 1; VALIDFILE(col
); col
++) {
991 int p
= b
[ROWTOBOARD(row
)][COLTOBOARD(col
)].icon
;
993 if (pgn_piece_to_int(p
) == KING
) {
994 if (val_piece_side(g
.turn
, p
)) {
1008 int checktest(GAME
*g
, BOARD b
, int kr
, int kc
, int okr
, int okc
, int matetest
)
1014 /* See if the move would put our opponent in check. */
1015 for (row
= 1; VALIDFILE(row
); row
++) {
1016 for (col
= 1; VALIDFILE(col
); col
++) {
1017 int srow
= 0, scol
= 0;
1018 int p
= b
[ROWTOBOARD(row
)][COLTOBOARD(col
)].icon
;
1019 int pi
= pgn_piece_to_int(p
);
1021 if (pi
== OPEN_SQUARE
)
1027 /* See if the move would leave ourselves in check. */
1031 if (get_source_yx(g
, b
, pi
, kr
, kc
, &srow
, &scol
) == 0)
1037 if (get_source_yx(g
, b
, pi
, okr
, okc
, &srow
, &scol
) == 0) {
1048 void board_reset_valid_moves(BOARD b
)
1052 for (row
= 0; row
< 8; row
++) {
1053 for (col
= 0; col
< 8; col
++)
1054 b
[row
][col
].valid
= 0;
1058 void board_get_valid_moves(GAME
*g
, BOARD b
, int p
, int srow
, int scol
, int *minr
, int *maxr
, int *minc
, int *maxc
)
1063 *minr
= *maxr
= *minc
= *maxc
= 1;
1065 for (row
= 1; VALIDFILE(row
); row
++) {
1066 for (col
= 1; VALIDFILE(col
); col
++) {
1069 if (get_source_yx(g
, b
, p
, row
, col
, &sr
, &sc
)) {
1073 if (get_source_yx(g
, b
, p
, row
, col
, &sr
, &sc
)) {
1077 if (get_source_yx(g
, b
, p
, row
, col
, &sr
, &sc
)) {
1083 if (sr
!= srow
|| sc
!= scol
)
1086 b
[ROWTOBOARD(row
)][COLTOBOARD(col
)].valid
= 1;
1107 static int checkmate_pawn_test(GAME
*g
, BOARD b
, int row
, int col
, int *srow
, int *scol
)
1114 if (!get_source_yx(g
, b
, PAWN
, row
, col
, &r
, &c
))
1119 if (!get_source_yx(g
, b
, PAWN
, row
, col
, &r
, &c
))
1124 if (!get_source_yx(g
, b
, PAWN
, row
, col
, &r
, &c
))
1130 static int checkmatetest(GAME
*g
, BOARD b
, int kr
, int kc
, int okr
, int okc
)
1136 /* For each square on the board see if each peace has a valid move, and if
1137 * so, see if it would leave ourselves or the opponent in check.
1139 for (row
= 1; VALIDFILE(row
); row
++) {
1140 for (col
= 1; VALIDFILE(col
); col
++) {
1143 for (n
= 0; n
< MAX_PIECES
; n
++) {
1145 int nkr
= kr
, nkc
= kc
, nokr
= okr
, nokc
= okc
;
1151 if (checkmate_pawn_test(g
, b
, row
, col
, &srow
, &scol
))
1155 if (get_source_yx(g
, b
, n
, row
, col
, &srow
, &scol
))
1160 memcpy(oldboard
, b
, sizeof(BOARD
));
1161 p
= b
[ROWTOBOARD(srow
)][COLTOBOARD(scol
)].icon
;
1162 b
[ROWTOBOARD(row
)][COLTOBOARD(col
)].icon
= p
;
1163 b
[ROWTOBOARD(srow
)][COLTOBOARD(scol
)].icon
=
1164 pgn_int_to_piece(g
->turn
, OPEN_SQUARE
);
1166 if (pgn_piece_to_int(p
) == KING
) {
1167 if (piece_side(*g
, p
) == g
->turn
) {
1177 check
= checktest(g
, b
, nkr
, nkc
, nokr
, nokc
, 1);
1178 memcpy(b
, oldboard
, sizeof(BOARD
));
1188 if (g
->turn
== WHITE
)
1194 return (check
!= 0) ? 1 : 0;
1198 static int drawtest(BOARD b
)
1203 if (game
[gindex
].ply
>= 50)
1206 for (row
= 1; VALIDFILE(row
); row
++) {
1207 for (col
= 1; VALIDFILE(col
); col
++) {
1208 int p
= b
[ROWTOBOARD(row
)][COLTOBOARD(col
)].icon
;
1209 int piece
= pgn_piece_to_int(p
);
1230 void pgn_reset_enpassant(BOARD b
)
1234 for (r
= 0; r
< 8; r
++) {
1235 for (c
= 0; c
< 8; c
++)
1236 b
[r
][c
].enpassant
= 0;
1240 int pgn_validate_move(GAME
*g
, BOARD b
, char *m
)
1243 int piece
, dstpiece
;
1245 int srow
= 0, scol
= 0, row
, col
;
1248 int kr
= 0, kc
= 0, okr
= 0, okc
= 0;
1254 update_status_notify(*g
, NULL
);
1255 srow
= row
= col
= scol
= promo
= piece
= 0;
1257 p
= (m
) + strlen(m
);
1259 while (!isdigit(*--p
) && *p
!= 'O') {
1261 promo
= pgn_piece_to_int(i
);
1270 // Old promotion text (e8Q). Convert to SAN.
1271 if (pgn_piece_to_int(i
) != -1) {
1272 p
= (m
) + strlen(m
);
1285 if (pgn_piece_to_int(*p
) == PAWN
)
1289 for (i
= 0; *p
; i
++) {
1292 col
= COLTOINT(*p
++);
1294 col
= scol
= COLTOINT(*p
++);
1296 else if (VALIDROW(*p
)) {
1298 row
= ROWTOINT(*p
++);
1300 row
= ROWTOINT(*p
++);
1302 else if (*p
== 'x') {
1303 col
= COLTOINT(*++p
);
1304 row
= ROWTOINT(*++p
);
1307 else if (*p
== '=') {
1308 if (promo
== -1 || promo
== KING
|| promo
== PAWN
)
1312 *p
++ = toupper(pgn_int_to_piece(g
->turn
, promo
));
1318 DUMP("Pawn (move: '%s'): %c\n", m
, *p
++);
1325 if (get_source_yx(g
, b
, PAWN
, row
, col
, &srow
, &scol
))
1330 if (strcmp(m
, "O-O") == 0)
1331 g
->castle
= KINGSIDE
;
1332 else if (strcmp(m
, "O-O-O") == 0)
1333 g
->castle
= QUEENSIDE
;
1337 if ((piece
= pgn_piece_to_int(*p
++)) == -1)
1340 if (strlen(m
) > 3) {
1342 srow
= ROWTOINT(*p
++);
1343 else if (VALIDCOL(*p
))
1344 scol
= COLTOINT(*p
++);
1352 col
= COLTOINT(*p
++);
1353 row
= ROWTOINT(*p
++);
1355 /* Get the source row and column. */
1358 for (i
= 1; VALIDFILE(i
); i
++) {
1359 int fpiece
= b
[ROWTOBOARD(i
)][COLTOBOARD(scol
)].icon
;
1361 if (piece
== pgn_piece_to_int(fpiece
) &&
1362 val_piece_side(g
->turn
, fpiece
)) {
1372 if (get_source_yx(g
, b
, piece
, row
, col
, &srow
, &scol
))
1376 else if (scol
== 0) {
1378 for (i
= 1; VALIDFILE(i
); i
++) {
1379 int fpiece
= pgn_piece_to_int(b
[ROWTOBOARD(srow
)][COLTOBOARD(i
)].icon
);
1381 if (piece
== fpiece
) {
1391 if (get_source_yx(g
, b
, piece
, row
, col
, &srow
, &scol
))
1398 piece
= pgn_piece_to_int(b
[ROWTOBOARD(srow
)][COLTOBOARD(scol
)].icon
);
1399 dist
= abs(srow
- row
);
1402 if (piece
== PAWN
|| capture
)
1407 pgn_reset_enpassant(b
);
1409 if (piece
== PAWN
&& dist
== 2) {
1410 if (g
->turn
== WHITE
)
1411 b
[ROWTOBOARD(srow
- 1)][COLTOBOARD(scol
)].enpassant
= 1;
1413 b
[ROWTOBOARD(srow
+ 1)][COLTOBOARD(scol
)].enpassant
= 1;
1415 SET_FLAG(g
->flags
, GF_ENPASSANT
);
1418 CLEAR_FLAG(g
->flags
, GF_ENPASSANT
);
1422 dstpiece
= piece
= b
[ROWTOBOARD(row
)][COLTOBOARD(col
)].icon
;
1425 if (castle_move(g
, b
, g
->castle
)) {
1433 if (pgn_piece_to_int(piece
) != OPEN_SQUARE
) {
1434 if (val_piece_side(g
->turn
, piece
))
1438 * this doesn't belong in the library. This should update a capture
1439 * count or something.
1443 update_status_notify(*g, random_agony(*g));
1449 piece
= pgn_int_to_piece(g
->turn
, promo
);
1451 piece
= b
[ROWTOBOARD(srow
)][COLTOBOARD(scol
)].icon
;
1454 piece
= b
[ROWTOBOARD(srow
)][COLTOBOARD(scol
)].icon
;
1456 b
[ROWTOBOARD(srow
)][COLTOBOARD(scol
)].icon
= pgn_int_to_piece(g
->turn
, OPEN_SQUARE
);
1457 b
[ROWTOBOARD(row
)][COLTOBOARD(col
)].icon
= piece
;
1460 if (!validate
&& capture
&& pgn_piece_to_int(dstpiece
) == ROOK
) {
1461 if (row
== 1 && col
== 1)
1462 CLEAR_FLAG(g
->flags
, GF_WQ_CASTLE
);
1463 else if (row
== 8 && col
== 1)
1464 CLEAR_FLAG(g
->flags
, GF_BQ_CASTLE
);
1465 else if (row
== 1 && col
== 8)
1466 CLEAR_FLAG(g
->flags
, GF_WK_CASTLE
);
1467 else if (row
== 8 && col
== 8)
1468 CLEAR_FLAG(g
->flags
, GF_BK_CASTLE
);
1471 kingsquare(*g
, b
, &kr
, &kc
, &okr
, &okc
);
1479 CLEAR_FLAG(g
->flags
, GF_GAMEOVER
);
1484 g
->tag
[TAG_RESULT
]->value
= Realloc(g
->tag
[TAG_RESULT
]->value
, 8);
1485 strncpy(g
->tag
[TAG_RESULT
]->value
, "1/2-1/2", 8);
1486 SET_FLAG(g
->flags
, GF_GAMEOVER
);
1489 switch (checktest(g
, b
, kr
, kc
, okr
, okc
, 0)) {
1497 if (checkmatetest(g
, b
, kr
, kc
, okr
, okc
)) {
1500 if (result
== WHITEWINS
) {
1501 g
->tag
[TAG_RESULT
]->value
= Realloc(g
->tag
[TAG_RESULT
]->value
, 4);
1502 strncpy(g
->tag
[TAG_RESULT
]->value
, "1-0", 4);
1504 else if (result
== BLACKWINS
) {
1505 g
->tag
[TAG_RESULT
]->value
= Realloc(g
->tag
[TAG_RESULT
]->value
, 4);
1506 strncpy(g
->tag
[TAG_RESULT
]->value
, "0-1", 4);
1509 SET_FLAG(g
->flags
, GF_GAMEOVER
);