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 piece_side(GAME g
, int c
)
42 if (c
== pgn_int_to_piece(g
.turn
, OPEN_SQUARE
))
46 c
= pgn_int_to_piece(g
.turn
, c
);
48 return (isupper(c
)) ? WHITE
: BLACK
;
51 static int val_piece_side(char turn
, int c
)
53 if ((isupper(c
) && turn
== WHITE
) ||
54 (islower(c
) && turn
== BLACK
))
60 static int count_piece(GAME g
, BOARD b
, int piece
, int sfile
, int srank
, int
61 file
, int rank
, int *count
)
63 int n
= b
[RANKTOBOARD(rank
)][FILETOBOARD(file
)].icon
;
65 if (!VALIDRANK(rank
) || !VALIDFILE(file
))
68 if (pgn_piece_to_int(n
) != OPEN_SQUARE
) {
69 if (pgn_piece_to_int(n
) == piece
&& val_piece_side(g
.turn
, n
)) {
70 if (sfile
&& file
== sfile
) {
71 if (!srank
|| (srank
&& rank
== srank
))
74 else if (srank
&& rank
== srank
) {
78 else if (!sfile
&& !srank
)
89 * Get the source row and column for a given piece.
91 * The following two functions find 'piece' from the given square 'col' and
92 * 'row' and store the resulting column or row in 'c' and 'r'. The return
93 * value is the number of 'piece' found (on the current g.side) or zero.
94 * Search for 'piece' stops when a non-empty square is found.
96 static int count_by_diag(GAME g
, BOARD b
, int piece
, int sfile
, int srank
,
100 int ul
= 0, ur
= 0, dl
= 0, dr
= 0;
104 for (i
= 1; VALIDFILE(i
); i
++) {
108 if (!ul
&& VALIDRANK(r
) && VALIDFILE(f
))
109 ul
= count_piece(g
, b
, piece
, sfile
, srank
, f
, r
, &count
);
114 if (!ur
&& VALIDRANK(r
) && VALIDFILE(f
))
115 ur
= count_piece(g
, b
, piece
, sfile
, srank
, f
, r
, &count
);
120 if (!dl
&& VALIDRANK(r
) && VALIDFILE(f
))
121 dl
= count_piece(g
, b
, piece
, sfile
, srank
, f
, r
, &count
);
126 if (!dr
&& VALIDRANK(r
) && VALIDFILE(f
))
127 dr
= count_piece(g
, b
, piece
, sfile
, srank
, f
, r
, &count
);
133 static int count_knight(GAME g
, BOARD b
, int piece
, int sfile
, int srank
,
138 count_piece(g
, b
, piece
, sfile
, srank
, file
- 1, rank
+ 2, &count
);
139 count_piece(g
, b
, piece
, sfile
, srank
, file
+ 1, rank
+ 2, &count
);
140 count_piece(g
, b
, piece
, sfile
, srank
, file
+ 2, rank
+ 1, &count
);
141 count_piece(g
, b
, piece
, sfile
, srank
, file
- 2, rank
+ 1, &count
);
142 count_piece(g
, b
, piece
, sfile
, srank
, file
+ 1, rank
- 2, &count
);
143 count_piece(g
, b
, piece
, sfile
, srank
, file
- 1, rank
- 2, &count
);
144 count_piece(g
, b
, piece
, sfile
, srank
, file
+ 2, rank
- 1, &count
);
145 count_piece(g
, b
, piece
, sfile
, srank
, file
- 2, rank
- 1, &count
);
149 static int count_by_rank(GAME g
, BOARD b
, int piece
, int sfile
, int srank
,
156 for (i
= 1; VALIDRANK(i
); i
++) {
157 if (!u
&& VALIDRANK((rank
+ i
))) {
158 if (count_piece(g
, b
, piece
, sfile
, srank
, file
, rank
+ i
, &count
))
162 if (!d
&& VALIDRANK((rank
- i
))) {
163 if (count_piece(g
, b
, piece
, sfile
, srank
, file
, rank
- i
, &count
))
171 static int count_by_file(GAME g
, BOARD b
, int piece
, int sfile
, int srank
,
178 for (i
= 1; VALIDFILE(i
); i
++) {
179 if (!r
&& VALIDFILE((file
+ i
))) {
180 if (count_piece(g
, b
, piece
, sfile
, srank
, file
+ i
, rank
, &count
))
184 if (!l
&& VALIDFILE((file
- i
))) {
185 if (count_piece(g
, b
, piece
, sfile
, srank
, file
- i
, rank
, &count
))
193 static int count_by_rank_file(GAME g
, BOARD b
, int piece
, int sfile
, int srank
,
198 count
= count_by_rank(g
, b
, piece
, sfile
, srank
, file
, rank
);
199 return count
+ count_by_file(g
, b
, piece
, sfile
, srank
, file
, rank
);
202 static int valid_move(GAME g
, BOARD b
, int row
, int col
, int srow
, int scol
)
206 if (!VALIDFILE(srow
) || !VALIDFILE(scol
))
209 if (row
== srow
&& col
== scol
)
212 p1
= b
[ROWTOBOARD(srow
)][COLTOBOARD(scol
)].icon
;
213 p2
= b
[ROWTOBOARD(row
)][COLTOBOARD(col
)].icon
;
215 if (pgn_piece_to_int(p1
) == OPEN_SQUARE
)
218 if (piece_side(g
, p1
) == piece_side(g
, p2
))
221 if (pgn_piece_to_int(p1
) == PAWN
&& scol
== col
&&
222 pgn_piece_to_int(p2
) != OPEN_SQUARE
)
229 * Returns the number of pieces of type 'p' that can move to the destination
230 * square located at 'file' and 'rank'. The source square 'sfile' and 'srank'
231 * (if available in the move text) should be determined before calling this
232 * function or set to 0 if unknown. Returns 0 if the move is impossible for
235 static int find_ambiguous(GAME g
, BOARD b
, int p
, int sfile
, int srank
,
245 count
= count_by_rank_file(g
, b
, p
, sfile
, srank
, file
, rank
);
248 count
= count_knight(g
, b
, p
, sfile
, srank
, file
, rank
);
251 count
= count_by_diag(g
, b
, p
, sfile
, srank
, file
, rank
);
254 count
= count_by_rank_file(g
, b
, p
, sfile
, srank
, file
, rank
);
255 count
+= count_by_diag(g
, b
, p
, sfile
, srank
, file
, rank
);
258 count
= count_by_rank_file(g
, b
, p
, sfile
, srank
, file
, rank
);
259 count
+= count_by_diag(g
, b
, p
, sfile
, srank
, file
, rank
);
268 static void find_king_squares(GAME g
, BOARD b
, int *file
, int *rank
, int *ofile
,
273 for (r
= 1; VALIDRANK(r
); r
++) {
274 for (f
= 1; VALIDFILE(f
); f
++) {
275 int p
= b
[RANKTOBOARD(r
)][FILETOBOARD(f
)].icon
;
277 if (pgn_piece_to_int(p
) != KING
)
280 if (val_piece_side(g
.turn
, p
))
281 *file
= f
, *rank
= r
;
283 *ofile
= f
, *orank
= r
;
288 // FIXME in-check test
289 static int validate_castle_move(GAME g
, BOARD b
, int side
, int sfile
,
290 int srank
, int file
, int rank
)
292 if (side
== KINGSIDE
) {
293 if ((g
.turn
== WHITE
&& !TEST_FLAG(g
.flags
, GF_WK_CASTLE
)) ||
294 (g
.turn
== BLACK
&& !TEST_FLAG(g
.flags
, GF_BK_CASTLE
)))
295 return E_PGN_INVALID
;
298 if ((g
.turn
== WHITE
&& !TEST_FLAG(g
.flags
, GF_WQ_CASTLE
)) ||
299 (g
.turn
== BLACK
&& !TEST_FLAG(g
.flags
, GF_BQ_CASTLE
)))
300 return E_PGN_INVALID
;
303 if (file
> FILETOINT('e')) {
304 if (b
[RANKTOBOARD(srank
)][FILETOBOARD((sfile
+ 1))].icon
305 != pgn_int_to_piece(g
.turn
, OPEN_SQUARE
) ||
306 b
[RANKTOBOARD(srank
)][FILETOBOARD((sfile
+ 2))].icon
307 != pgn_int_to_piece(g
.turn
, OPEN_SQUARE
))
308 return E_PGN_INVALID
;
311 if (b
[RANKTOBOARD(srank
)][FILETOBOARD((sfile
- 1))].icon
!=
312 pgn_int_to_piece(g
.turn
, OPEN_SQUARE
) ||
313 b
[RANKTOBOARD(srank
)][FILETOBOARD((sfile
- 2))].icon
!=
314 pgn_int_to_piece(g
.turn
, OPEN_SQUARE
) ||
315 b
[RANKTOBOARD(srank
)][FILETOBOARD((sfile
- 3))].icon
!=
316 pgn_int_to_piece(g
.turn
, OPEN_SQUARE
))
317 return E_PGN_INVALID
;
323 static int parse_castle_move(GAME g
, BOARD b
, int side
, int *sfile
,
324 int *srank
, int *file
, int *rank
)
326 *srank
= *rank
= (g
.turn
== WHITE
) ? 1 : 8;
327 *sfile
= FILETOINT('e');
329 if (side
== KINGSIDE
)
330 *file
= FILETOINT('g');
332 *file
= FILETOINT('c');
334 return validate_castle_move(g
, b
, side
, *sfile
, *srank
, *file
, *rank
);
337 static int find_source_square(GAME g
, BOARD b
, int piece
, int *sfile
,
338 int *srank
, int file
, int rank
)
347 if (!*srank
&& *sfile
== file
) {
348 /* Find the first pawn in the current column. */
349 i
= (g
.turn
== WHITE
) ? -1 : 1;
351 for (r
= rank
+ i
, dist
= 0; VALIDFILE(r
); r
+= i
, dist
++) {
352 int n
= b
[RANKTOBOARD(r
)][FILETOBOARD(file
)].icon
;
354 p
= pgn_piece_to_int(n
);
356 if (p
== PAWN
&& val_piece_side(g
.turn
, n
))
360 if (p
!= PAWN
|| dist
> 2)
364 dist
= abs(*srank
- rank
);
366 if (g
.turn
== WHITE
) {
367 if ((*srank
== 2 && dist
> 2) || (*srank
> 2 && dist
> 1))
371 if ((*srank
== 7 && dist
> 2) || (*srank
< 7 && dist
> 1))
375 p
= b
[RANKTOBOARD(rank
)][FILETOBOARD(file
)].icon
;
377 if (pgn_piece_to_int(p
) != OPEN_SQUARE
)
380 else if (*sfile
!= file
) {
381 if (abs(*sfile
- file
) != 1)
384 *srank
= (g
.turn
== WHITE
) ? rank
- 1 : rank
+ 1;
385 p
= b
[RANKTOBOARD(*srank
)][FILETOBOARD(*sfile
)].icon
;
387 if (!val_piece_side(g
.turn
, p
))
390 if (pgn_piece_to_int(p
) != PAWN
|| abs(*srank
- rank
) != 1)
393 p
= b
[RANKTOBOARD(rank
)][FILETOBOARD(file
)].icon
;
396 if (pgn_piece_to_int(p
) == OPEN_SQUARE
) {
397 /* Previous move was not 2 squares and a pawn. */
398 if (!TEST_FLAG(g
.flags
, GF_ENPASSANT
))
401 if (!b
[RANKTOBOARD(rank
)][FILETOBOARD(file
)].enpassant
)
404 r
= (g
.turn
== WHITE
) ? 6 : 3;
409 r
= (g
.turn
== WHITE
) ? rank
- 1 : rank
+ 1;
410 p
= b
[RANKTOBOARD(r
)][FILETOBOARD(file
)].icon
;
412 if (pgn_piece_to_int(p
) != PAWN
)
416 if (val_piece_side(g
.turn
, p
))
423 for (r
= 1; VALIDRANK(r
); r
++) {
424 for (f
= 1; VALIDFILE(f
); f
++) {
427 if ((*sfile
&& f
!= *sfile
) || (*srank
&& r
!= *srank
))
430 n
= find_ambiguous(g
, b
, piece
, f
, r
, file
, rank
);
443 /* FIXME shouldn't be here.
444 if (valid_move(g, b, rank, file, *srank, *sfile) == 0)
449 if (abs(*srank
- rank
) > 1 || abs(*sfile
- file
) > 2)
452 if (abs(*sfile
- file
) == 2) {
453 if (*sfile
!= FILETOINT('e'))
456 if (validate_castle_move(g
, b
, (file
> FILETOINT('e')) ?
457 KINGSIDE
: QUEENSIDE
, *sfile
, *srank
, file
, rank
)
467 static int finalize_move(GAME
*g
, BOARD b
, int promo
, int sfile
, int srank
,
472 p
= b
[RANKTOBOARD(rank
)][FILETOBOARD(file
)].icon
;
473 pi
= pgn_piece_to_int(p
);
475 if (pi
!= OPEN_SQUARE
) {
476 if (val_piece_side(g
->turn
, p
))
477 return E_PGN_INVALID
;
481 if (p
== PAWN
|| capture
)
486 pgn_reset_enpassant(b
);
487 p
= b
[RANKTOBOARD(srank
)][FILETOBOARD(sfile
)].icon
;
488 pi
= pgn_piece_to_int(p
);
492 p
= (g
->turn
== WHITE
) ? rank
- 1 : rank
+ 1;
493 b
[RANKTOBOARD(p
)][FILETOBOARD(file
)].icon
=
494 pgn_int_to_piece(g
->turn
, OPEN_SQUARE
);
497 if (abs(srank
- rank
) > 1) {
498 SET_FLAG(g
->flags
, GF_ENPASSANT
);
499 b
[RANKTOBOARD(((g
->turn
== WHITE
) ? rank
- 1 : rank
+ 1))][FILETOBOARD(file
)].enpassant
= 1;
502 CLEAR_FLAG(g
->flags
, GF_ENPASSANT
);
504 else if (pi
== ROOK
) {
505 if (g
->turn
== WHITE
)
506 CLEAR_FLAG(g
->flags
, (file
> FILETOINT('e')) ? GF_WK_CASTLE
:
509 CLEAR_FLAG(g
->flags
, (file
> FILETOINT('e')) ? GF_BK_CASTLE
:
513 if (pi
== KING
&& !castle
) {
514 if (g
->turn
== WHITE
)
515 CLEAR_FLAG(g
->flags
, GF_WK_CASTLE
|GF_WQ_CASTLE
);
517 CLEAR_FLAG(g
->flags
, GF_BK_CASTLE
|GF_BQ_CASTLE
);
521 p
= b
[RANKTOBOARD(srank
)][FILETOBOARD(sfile
)].icon
;
522 b
[RANKTOBOARD(srank
)][FILETOBOARD(sfile
)].icon
=
523 pgn_int_to_piece(g
->turn
, OPEN_SQUARE
);
524 b
[RANKTOBOARD(srank
)][FILETOBOARD(
525 (file
> FILETOINT('e') ? 8 : 1))].icon
=
526 pgn_int_to_piece(g
->turn
, OPEN_SQUARE
);
527 b
[RANKTOBOARD(rank
)][FILETOBOARD(file
)].icon
= p
;
529 if (file
> FILETOINT('e'))
530 b
[RANKTOBOARD(rank
)][FILETOBOARD((file
- 1))].icon
=
531 pgn_int_to_piece(g
->turn
, ROOK
);
533 b
[RANKTOBOARD(rank
)][FILETOBOARD((file
+ 1))].icon
=
534 pgn_int_to_piece(g
->turn
, ROOK
);
536 if (g
->turn
== WHITE
)
537 CLEAR_FLAG(g
->flags
, (file
> FILETOINT('e')) ? GF_WK_CASTLE
:
540 CLEAR_FLAG(g
->flags
, (file
> FILETOINT('e')) ? GF_BK_CASTLE
:
547 p
= pgn_int_to_piece(g
->turn
, promo
);
549 p
= b
[RANKTOBOARD(srank
)][FILETOBOARD(sfile
)].icon
;
551 b
[RANKTOBOARD(srank
)][FILETOBOARD(sfile
)].icon
=
552 pgn_int_to_piece(g
->turn
, OPEN_SQUARE
);
553 b
[RANKTOBOARD(rank
)][FILETOBOARD(file
)].icon
= p
;
559 _kingsquare(*g
, b
, &kr
, &kc
, &okr
, &okc
);
567 CLEAR_FLAG(g
->flags
, GF_GAMEOVER
);
571 if (_drawtest(g
, b
)) {
572 pgn_tag_add(&g
->tag
, "Result", "1/2-1/2");
573 SET_FLAG(g
->flags
, GF_GAMEOVER
);
576 switch (_checktest(g
, b
, kr
, kc
, okr
, okc
, 0)) {
582 return E_PGN_INVALID
;
584 if (_checkmatetest(g
, b
, kr
, kc
, okr
, okc
)) {
587 if (result
== WHITEWINS
)
588 pgn_tag_add(&g
->tag
, "Result", "1-0");
589 else if (result
== BLACKWINS
)
590 pgn_tag_add(&g
->tag
, "Result", "1-0");
592 SET_FLAG(g
->flags
, GF_GAMEOVER
);
604 // pgn_switch_turn(g);
610 * Converts a2a3 formatted moves to SAN format. The promotion piece should be
613 static int frfrtosan(GAME
*g
, BOARD b
, char **m
)
616 int icon
, p
, dp
, promo
= 0;
617 int sfile
, srank
, file
, rank
;
621 sfile
= FILETOINT(bp
[0]);
622 srank
= RANKTOINT(bp
[1]);
623 file
= FILETOINT(bp
[2]);
624 rank
= RANKTOINT(bp
[3]);
627 if ((promo
= pgn_piece_to_int(bp
[4])) == -1)
631 icon
= b
[RANKTOBOARD(srank
)][FILETOBOARD(sfile
)].icon
;
633 if ((p
= pgn_piece_to_int(icon
)) == -1 || p
== OPEN_SQUARE
)
636 if (p
!= PAWN
&& promo
)
637 return E_PGN_INVALID
;
640 if (find_source_square(*g
, b
, p
, &sfile
, &srank
, file
, rank
) != 1)
641 return E_PGN_INVALID
;
646 if (p
== KING
&& abs(sfile
- file
) > 1) {
647 if (abs(sfile
- file
) > 2)
648 return E_PGN_INVALID
;
650 if (validate_castle_move(*g
, b
, (file
> FILETOINT('e')) ?
651 KINGSIDE
: QUEENSIDE
, sfile
, srank
, file
, rank
) != E_PGN_OK
)
652 return E_PGN_INVALID
;
655 strcpy(bp
, (file
> FILETOINT('e')) ? "O-O" : "O-O-O");
656 return finalize_move(g
, b
, promo
, sfile
, srank
, file
, rank
);
659 *bp
++ = toupper(icon
);
660 n
= find_ambiguous(*g
, b
, p
, 0, 0, file
, rank
);
663 return E_PGN_INVALID
;
665 fc
= find_ambiguous(*g
, b
, p
, sfile
, 0, file
, rank
);
666 rc
= find_ambiguous(*g
, b
, p
, 0, srank
, file
, rank
);
669 *bp
++ = INTTOFILE(sfile
);
671 *bp
++ = INTTORANK(srank
);
673 *bp
++ = INTTOFILE(sfile
);
674 *bp
++ = INTTORANK(srank
);
677 return E_PGN_PARSE
; // FIXME probable a bug in the parser.
681 icon
= b
[RANKTOBOARD(rank
)][FILETOBOARD(file
)].icon
;
683 if ((dp
= pgn_piece_to_int(icon
)) == -1)
689 if (dp
!= OPEN_SQUARE
|| (dp
== OPEN_SQUARE
&& p
== PAWN
&& sfile
!= file
)) {
691 *bp
++ = INTTOFILE(sfile
);
700 *bp
++ = INTTOFILE(file
);
701 *bp
++ = INTTORANK(rank
);
707 if (p
!= PAWN
|| (g
->turn
== WHITE
&& (srank
!= 7 || rank
!= 8)) ||
708 (g
->turn
== BLACK
&& (srank
!= 2 || rank
!= 1)))
709 return E_PGN_INVALID
;
712 *bp
++ = pgn_int_to_piece(g
->turn
, promo
);
716 return finalize_move(g
, b
, promo
, sfile
, srank
, file
, rank
);
720 * Valididate move 'mp' against the game state 'g' and game board 'b' and
721 * update board 'b'. 'mp' is updated to SAN format for moves which aren't
722 * (frfr or e8Q for example). Returns E_PGN_PARSE if there was a move text
723 * parsing error, E_PGN_INVALID if the move is invalid or E_PGN_OK if
726 int pgn_parse_move(GAME
*g
, BOARD b
, char **mp
)
731 int srank
= 0, sfile
= 0, rank
, file
;
736 srank
= rank
= file
= sfile
= promo
= piece
= 0;
738 if (VALIDCOL(*m
) && VALIDROW(*(m
+ 1)) && VALIDCOL(*(m
+ 2)) &&
740 return frfrtosan(g
, b
, mp
);
741 else if (strcmp(m
, "O-O") == 0)
743 else if (strcmp(m
, "O-O-O") == 0)
747 if (parse_castle_move(*g
, b
, i
, &sfile
, &srank
, &file
, &rank
) !=
749 return E_PGN_INVALID
;
751 *m
++ = INTTOFILE(sfile
);
752 *m
++ = INTTORANK(srank
);
753 *m
++ = INTTOFILE(file
);
754 *m
++ = INTTORANK(rank
);
756 return frfrtosan(g
, b
, mp
);
765 while (!isdigit(*--p
) && *p
!= 'O') {
767 promo
= pgn_piece_to_int(i
);
776 // Old promotion text (e8Q). Convert to SAN.
777 if (pgn_piece_to_int(i
) != -1) {
787 /* Skip 'P' (pawn). */
788 if (pgn_piece_to_int(*p
) == PAWN
)
793 for (i
= 0; *p
; i
++) {
796 file
= FILETOINT(*p
++);
798 file
= sfile
= FILETOINT(*p
++);
800 else if (VALIDROW(*p
)) {
802 rank
= RANKTOINT(*p
++);
804 rank
= RANKTOINT(*p
++);
806 else if (*p
== 'x') {
807 file
= FILETOINT(*++p
);
808 rank
= RANKTOINT(*++p
);
811 else if (*p
== '=') {
812 if (promo
== -1 || promo
== KING
|| promo
== PAWN
)
816 *p
++ = toupper(pgn_int_to_piece(g
->turn
, promo
));
822 DUMP("Pawn (move: '%s'): %c\n", m
, *p
++);
829 if (find_source_square(*g
, b
, PAWN
, &sfile
, &srank
, file
, rank
) != 1)
830 return E_PGN_INVALID
;
834 if (strcmp(m
, "O-O") == 0)
836 else if (strcmp(m
, "O-O-O") == 0)
844 * The first character is the piece but only if not a pawn.
846 if ((piece
= pgn_piece_to_int(*p
++)) == -1)
857 srank
= RANKTOINT(*p
++);
861 else if (VALIDCOL(*p
)) {
862 sfile
= FILETOINT(*p
++);
868 srank
= RANKTOINT(*p
++);
883 * The destination square.
885 file
= FILETOINT(*p
++);
886 rank
= RANKTOINT(*p
++);
891 if ((i
= find_ambiguous(*g
, b
, piece
, sfile
, srank
, file
, rank
))
893 return (i
== 0) ? E_PGN_INVALID
: E_PGN_AMBIGUOUS
;
896 * The move is a valid one. Find the source file and rank so we
897 * can later update the board positions.
899 if (find_source_square(*g
, b
, piece
, &sfile
, &srank
, file
, rank
) != 1)
900 return E_PGN_INVALID
;
905 return finalize_move(g
, b
, promo
, sfile
, srank
, file
, rank
);
909 * Like pgn_parse_move() but don't modify game flags in 'g' or board 'b'.
911 int pgn_validate_move(GAME
*g
, BOARD b
, char **m
)
916 ret
= pgn_parse_move(g
, b
, m
);
922 * Sets valid moves from game 'g' using board 'b'. The valid moves are for the
923 * piece on the board 'b' at 'rank' and 'file'. Returns nothing.
925 void pgn_find_valid_moves(GAME g
, BOARD b
, int file
, int rank
)
927 int p
= pgn_piece_to_int(b
[RANKTOBOARD(rank
)][FILETOBOARD(file
)].icon
);
932 for (r
= 1; VALIDRANK(r
); r
++) {
933 for (f
= 1; VALIDFILE(f
); f
++) {
936 if (val_piece_side(g
.turn
, b
[RANKTOBOARD(r
)][FILETOBOARD(f
)].icon
))
939 if (find_source_square(g
, b
, p
, &file
, (p
== PAWN
) ? &n
: &rank
, f
,
941 b
[RANKTOBOARD(r
)][FILETOBOARD(f
)].valid
= 1;
949 void _kingsquare(GAME g
, BOARD b
, int *kr
, int *kc
, int *okr
, int *okc
)
953 for (row
= 1; VALIDFILE(row
); row
++) {
954 for (col
= 1; VALIDFILE(col
); col
++) {
955 int p
= b
[ROWTOBOARD(row
)][COLTOBOARD(col
)].icon
;
957 if (pgn_piece_to_int(p
) == KING
) {
958 if (val_piece_side(g
.turn
, p
)) {
972 int _checktest(GAME
*g
, BOARD b
, int kr
, int kc
, int okr
, int okc
, int matetest
)
978 /* See if the move would put our opponent in check. */
979 for (row
= 1; VALIDFILE(row
); row
++) {
980 for (col
= 1; VALIDFILE(col
); col
++) {
981 int srow
= 0, scol
= 0;
982 int p
= b
[ROWTOBOARD(row
)][COLTOBOARD(col
)].icon
;
983 int pi
= pgn_piece_to_int(p
);
985 if (pi
== OPEN_SQUARE
)
991 /* See if the move would leave ourselves in check. */
995 if (find_source_square(g
, b
, pi
, kr
, kc
, &srow
, &scol
) == 0)
1001 if (find_source_square(g
, b
, pi
, okr
, okc
, &srow
, &scol
) == 0) {
1012 int _checkmate_pawn_test(GAME
*g
, BOARD b
, int row
, int col
, int *srow
, int *scol
)
1019 if (!_get_source_square(g
, b
, PAWN
, row
, col
, &r
, &c
))
1024 if (!_get_source_square(g
, b
, PAWN
, row
, col
, &r
, &c
))
1029 if (!_get_source_square(g
, b
, PAWN
, row
, col
, &r
, &c
))
1035 int _checkmatetest(GAME
*g
, BOARD b
, int kr
, int kc
, int okr
, int okc
)
1041 /* For each square on the board see if each peace has a valid move, and if
1042 * so, see if it would leave ourselves or the opponent in check.
1044 for (row
= 1; VALIDFILE(row
); row
++) {
1045 for (col
= 1; VALIDFILE(col
); col
++) {
1048 for (n
= 0; n
< MAX_PIECES
; n
++) {
1050 int nkr
= kr
, nkc
= kc
, nokr
= okr
, nokc
= okc
;
1056 if (_checkmate_pawn_test(g
, b
, row
, col
, &srow
, &scol
))
1060 if (_get_source_square(g
, b
, n
, row
, col
, &srow
, &scol
))
1065 memcpy(oldboard
, b
, sizeof(BOARD
));
1066 p
= b
[ROWTOBOARD(srow
)][COLTOBOARD(scol
)].icon
;
1067 b
[ROWTOBOARD(row
)][COLTOBOARD(col
)].icon
= p
;
1068 b
[ROWTOBOARD(srow
)][COLTOBOARD(scol
)].icon
=
1069 pgn_int_to_piece(g
->turn
, OPEN_SQUARE
);
1071 if (pgn_piece_to_int(p
) == KING
) {
1072 if (piece_side(*g
, p
) == g
->turn
) {
1082 check
= _checktest(g
, b
, nkr
, nkc
, nokr
, nokc
, 1);
1083 memcpy(b
, oldboard
, sizeof(BOARD
));
1093 if (g
->turn
== WHITE
)
1099 return (check
!= 0) ? 1 : 0;
1103 int _drawtest(GAME
*g
, BOARD b
)
1108 if (pgn_config
.fmd
&& g
->ply
>= 50)
1111 for (row
= 1; VALIDFILE(row
); row
++) {
1112 for (col
= 1; VALIDFILE(col
); col
++) {
1113 int p
= b
[ROWTOBOARD(row
)][COLTOBOARD(col
)].icon
;
1114 int piece
= pgn_piece_to_int(p
);