libchess:
[cboard.git] / libchess / move.c
blob25c2929bec224ed2e4dcde269113ce6376031fc4
1 /* vim:tw=78:ts=8:sw=4:set ft=c: */
2 /*
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
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <ctype.h>
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
28 #include "chess.h"
29 #include "pgn.h"
30 #include "move.h"
32 #ifdef DEBUG
33 #include "debug.h"
34 #endif
36 #ifdef WITH_DMALLOC
37 #include <dmalloc.h>
38 #endif
40 static int piece_side(GAME g, int c)
42 if (c == pgn_int_to_piece(g.turn, OPEN_SQUARE))
43 return -1;
45 if (c < 'A')
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))
55 return 1;
57 return 0;
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))
66 return 0;
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))
72 (*count)++;
74 else if (srank && rank == srank) {
75 if (!sfile)
76 (*count)++;
78 else if (!sfile && !srank)
79 (*count)++;
82 return 1;
85 return 0;
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,
97 int file, int rank)
99 int count = 0;
100 int ul = 0, ur = 0, dl = 0, dr = 0;
101 int i;
102 int f, r;
104 for (i = 1; VALIDFILE(i); i++) {
105 r = rank + i;
106 f = file - i;
108 if (!ul && VALIDRANK(r) && VALIDFILE(f))
109 ul = count_piece(g, b, piece, sfile, srank, f, r, &count);
111 r = rank + i;
112 f = file + i;
114 if (!ur && VALIDRANK(r) && VALIDFILE(f))
115 ur = count_piece(g, b, piece, sfile, srank, f, r, &count);
117 r = rank - i;
118 f = file - i;
120 if (!dl && VALIDRANK(r) && VALIDFILE(f))
121 dl = count_piece(g, b, piece, sfile, srank, f, r, &count);
123 r = rank - i;
124 f = file + i;
126 if (!dr && VALIDRANK(r) && VALIDFILE(f))
127 dr = count_piece(g, b, piece, sfile, srank, f, r, &count);
130 return count;
133 static int count_knight(GAME g, BOARD b, int piece, int sfile, int srank,
134 int file, int rank)
136 int count = 0;
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);
146 return count;
149 static int count_by_rank(GAME g, BOARD b, int piece, int sfile, int srank,
150 int file, int rank)
152 int i;
153 int count = 0;
154 int u = 0, d = 0;
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))
159 u++;
162 if (!d && VALIDRANK((rank - i))) {
163 if (count_piece(g, b, piece, sfile, srank, file, rank - i, &count))
164 d++;
168 return count;
171 static int count_by_file(GAME g, BOARD b, int piece, int sfile, int srank,
172 int file, int rank)
174 int i;
175 int count = 0;
176 int l = 0, r = 0;
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))
181 r++;
184 if (!l && VALIDFILE((file - i))) {
185 if (count_piece(g, b, piece, sfile, srank, file - i, rank, &count))
186 l++;
190 return count;
193 static int count_by_rank_file(GAME g, BOARD b, int piece, int sfile, int srank,
194 int file, int rank)
196 int count;
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)
204 int p1, p2;
206 if (!VALIDFILE(srow) || !VALIDFILE(scol))
207 return 0;
209 if (row == srow && col == scol)
210 return 0;
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)
216 return 0;
218 if (piece_side(g, p1) == piece_side(g, p2))
219 return 0;
221 if (pgn_piece_to_int(p1) == PAWN && scol == col &&
222 pgn_piece_to_int(p2) != OPEN_SQUARE)
223 return 0;
225 return 1;
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
233 * the piece 'p'.
235 static int find_ambiguous(GAME g, BOARD b, int p, int sfile, int srank,
236 int file, int rank)
238 int count = 0;
240 switch (p) {
241 case PAWN:
242 count = 1;
243 break;
244 case ROOK:
245 count = count_by_rank_file(g, b, p, sfile, srank, file, rank);
246 break;
247 case KNIGHT:
248 count = count_knight(g, b, p, sfile, srank, file, rank);
249 break;
250 case BISHOP:
251 count = count_by_diag(g, b, p, sfile, srank, file, rank);
252 break;
253 case QUEEN:
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);
256 break;
257 case KING:
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);
260 break;
261 default:
262 break;
265 return count;
268 static void find_king_squares(GAME g, BOARD b, int *file, int *rank, int *ofile,
269 int *orank)
271 int f, r;
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)
278 continue;
280 if (val_piece_side(g.turn, p))
281 *file = f, *rank = r;
282 else
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;
297 else {
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;
310 else {
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;
320 return E_PGN_OK;
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');
331 else
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)
340 int p = 0;
341 int r, f;
342 int i;
343 int dist = 0;
344 int count = 0;
346 if (piece == PAWN) {
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))
357 break;
360 if (p != PAWN || dist > 2)
361 return 0;
363 *srank = r;
364 dist = abs(*srank - rank);
366 if (g.turn == WHITE) {
367 if ((*srank == 2 && dist > 2) || (*srank > 2 && dist > 1))
368 return 0;
370 else {
371 if ((*srank == 7 && dist > 2) || (*srank < 7 && dist > 1))
372 return 0;
375 p = b[RANKTOBOARD(rank)][FILETOBOARD(file)].icon;
377 if (pgn_piece_to_int(p) != OPEN_SQUARE)
378 return 0;
380 else if (*sfile != file) {
381 if (abs(*sfile - file) != 1)
382 return 0;
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))
388 return 0;
390 if (pgn_piece_to_int(p) != PAWN || abs(*srank - rank) != 1)
391 return 0;
393 p = b[RANKTOBOARD(rank)][FILETOBOARD(file)].icon;
395 /* En Passant. */
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))
399 return 0;
401 if (!b[RANKTOBOARD(rank)][FILETOBOARD(file)].enpassant)
402 return 0;
404 r = (g.turn == WHITE) ? 6 : 3;
406 if (rank != r)
407 return 0;
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)
413 return 0;
416 if (val_piece_side(g.turn, p))
417 return 0;
420 return 1;
423 for (r = 1; VALIDRANK(r); r++) {
424 for (f = 1; VALIDFILE(f); f++) {
425 int n;
427 if ((*sfile && f != *sfile) || (*srank && r != *srank))
428 continue;
430 n = find_ambiguous(g, b, piece, f, r, file, rank);
432 if (n) {
433 count += n;
434 *sfile = f;
435 *srank = r;
440 if (count != 1)
441 return count;
443 /* FIXME shouldn't be here.
444 if (valid_move(g, b, rank, file, *srank, *sfile) == 0)
445 return 0;
448 if (piece == KING) {
449 if (abs(*srank - rank) > 1 || abs(*sfile - file) > 2)
450 return 0;
452 if (abs(*sfile - file) == 2) {
453 if (*sfile != FILETOINT('e'))
454 return 0;
455 else {
456 if (validate_castle_move(g, b, (file > FILETOINT('e')) ?
457 KINGSIDE : QUEENSIDE, *sfile, *srank, file, rank)
458 != E_PGN_OK)
459 return 0;
464 return count;
467 static int finalize_move(GAME *g, BOARD b, int promo, int sfile, int srank,
468 int file, int rank)
470 int p, pi;
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;
480 if (!validate) {
481 if (p == PAWN || capture)
482 g->ply = 0;
483 else
484 g->ply++;
486 pgn_reset_enpassant(b);
487 p = b[RANKTOBOARD(srank)][FILETOBOARD(sfile)].icon;
488 pi = pgn_piece_to_int(p);
490 if (pi == PAWN) {
491 if (sfile != file) {
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;
501 else
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 :
507 GF_WQ_CASTLE);
508 else
509 CLEAR_FLAG(g->flags, (file > FILETOINT('e')) ? GF_BK_CASTLE :
510 GF_BQ_CASTLE);
513 if (pi == KING && !castle) {
514 if (g->turn == WHITE)
515 CLEAR_FLAG(g->flags, GF_WK_CASTLE|GF_WQ_CASTLE);
516 else
517 CLEAR_FLAG(g->flags, GF_BK_CASTLE|GF_BQ_CASTLE);
520 if (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);
532 else
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 :
538 GF_WQ_CASTLE);
539 else
540 CLEAR_FLAG(g->flags, (file > FILETOINT('e')) ? GF_BK_CASTLE :
541 GF_BQ_CASTLE);
543 castle = 0;
545 else {
546 if (promo)
547 p = pgn_int_to_piece(g->turn, promo);
548 else
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;
557 return E_PGN_OK;
558 #if 0
559 _kingsquare(*g, b, &kr, &kc, &okr, &okc);
560 pgn_switch_turn(g);
562 if (g->castle) {
563 p = m + strlen(m);
564 g->castle = 0;
567 CLEAR_FLAG(g->flags, GF_GAMEOVER);
568 i = validate;
569 validate = 1;
571 if (_drawtest(g, b)) {
572 pgn_tag_add(&g->tag, "Result", "1/2-1/2");
573 SET_FLAG(g->flags, GF_GAMEOVER);
575 else {
576 switch (_checktest(g, b, kr, kc, okr, okc, 0)) {
577 case 0:
578 break;
579 case -1:
580 validate = i;
581 pgn_switch_turn(g);
582 return E_PGN_INVALID;
583 default:
584 if (_checkmatetest(g, b, kr, kc, okr, okc)) {
585 *p++ = '#';
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);
594 else
595 *p++ = '+';
597 *p = '\0';
598 break;
601 validate = i;
602 #endif
604 // pgn_switch_turn(g);
606 return E_PGN_OK;
610 * Converts a2a3 formatted moves to SAN format. The promotion piece should be
611 * appended (a7a8q).
613 static int frfrtosan(GAME *g, BOARD b, char **m)
615 char *bp = *m;
616 int icon, p, dp, promo = 0;
617 int sfile, srank, file, rank;
618 int n;
619 int fc, rc;
621 sfile = FILETOINT(bp[0]);
622 srank = RANKTOINT(bp[1]);
623 file = FILETOINT(bp[2]);
624 rank = RANKTOINT(bp[3]);
626 if (bp[4]) {
627 if ((promo = pgn_piece_to_int(bp[4])) == -1)
628 return E_PGN_PARSE;
631 icon = b[RANKTOBOARD(srank)][FILETOBOARD(sfile)].icon;
633 if ((p = pgn_piece_to_int(icon)) == -1 || p == OPEN_SQUARE)
634 return 0;
636 if (p != PAWN && promo)
637 return E_PGN_INVALID;
639 if (p == PAWN) {
640 if (find_source_square(*g, b, p, &sfile, &srank, file, rank) != 1)
641 return E_PGN_INVALID;
643 goto capture;
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;
654 castle = 1;
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);
662 if (!n)
663 return E_PGN_INVALID;
664 else if (n > 1) {
665 fc = find_ambiguous(*g, b, p, sfile, 0, file, rank);
666 rc = find_ambiguous(*g, b, p, 0, srank, file, rank);
668 if (fc == 1)
669 *bp++ = INTTOFILE(sfile);
670 else if (!fc && rc)
671 *bp++ = INTTORANK(srank);
672 else if (fc && rc) {
673 *bp++ = INTTOFILE(sfile);
674 *bp++ = INTTORANK(srank);
676 else
677 return E_PGN_PARSE; // FIXME probable a bug in the parser.
680 capture:
681 icon = b[RANKTOBOARD(rank)][FILETOBOARD(file)].icon;
683 if ((dp = pgn_piece_to_int(icon)) == -1)
684 return E_PGN_PARSE;
687 * [Pf][fr]x
689 if (dp != OPEN_SQUARE || (dp == OPEN_SQUARE && p == PAWN && sfile != file)) {
690 if (p == PAWN)
691 *bp++ = INTTOFILE(sfile);
693 *bp++ = 'x';
697 * [Pf][fr][x]fr
700 *bp++ = INTTOFILE(file);
701 *bp++ = INTTORANK(rank);
704 * [Pf][fr][x]fr[=P]
706 if (promo) {
707 if (p != PAWN || (g->turn == WHITE && (srank != 7 || rank != 8)) ||
708 (g->turn == BLACK && (srank != 2 || rank != 1)))
709 return E_PGN_INVALID;
711 *bp++ = '=';
712 *bp++ = pgn_int_to_piece(g->turn, promo);
715 *bp = 0;
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
724 * successful.
726 int pgn_parse_move(GAME *g, BOARD b, char **mp)
728 char *p;
729 int piece;
730 int i = -1;
731 int srank = 0, sfile = 0, rank, file;
732 int promo = -1;
733 char *m = *mp;
735 capture = 0;
736 srank = rank = file = sfile = promo = piece = 0;
738 if (VALIDCOL(*m) && VALIDROW(*(m + 1)) && VALIDCOL(*(m + 2)) &&
739 VALIDROW(*(m + 3)))
740 return frfrtosan(g, b, mp);
741 else if (strcmp(m, "O-O") == 0)
742 i = KINGSIDE;
743 else if (strcmp(m, "O-O-O") == 0)
744 i = QUEENSIDE;
746 if (i >= 0) {
747 if (parse_castle_move(*g, b, i, &sfile, &srank, &file, &rank) !=
748 E_PGN_OK)
749 return E_PGN_INVALID;
751 *m++ = INTTOFILE(sfile);
752 *m++ = INTTORANK(srank);
753 *m++ = INTTOFILE(file);
754 *m++ = INTTORANK(rank);
755 *m = 0;
756 return frfrtosan(g, b, mp);
759 again:
760 if (strlen(m) < 2)
761 return E_PGN_PARSE;
763 p = (m) + strlen(m);
765 while (!isdigit(*--p) && *p != 'O') {
766 if (*p == '=') {
767 promo = pgn_piece_to_int(i);
768 i = 0;
769 break;
772 i = *p;
773 *p = '\0';
776 // Old promotion text (e8Q). Convert to SAN.
777 if (pgn_piece_to_int(i) != -1) {
778 p = (m) + strlen(m);
779 *p++ = '=';
780 *p++ = i;
781 *p = '\0';
782 goto again;
785 p = m;
787 /* Skip 'P' (pawn). */
788 if (pgn_piece_to_int(*p) == PAWN)
789 p++;
791 /* Pawn. */
792 if (VALIDCOL(*p)) {
793 for (i = 0; *p; i++) {
794 if (VALIDCOL(*p)) {
795 if (i > 0)
796 file = FILETOINT(*p++);
797 else
798 file = sfile = FILETOINT(*p++);
800 else if (VALIDROW(*p)) {
801 if (1 > 1)
802 rank = RANKTOINT(*p++);
803 else
804 rank = RANKTOINT(*p++);
806 else if (*p == 'x') {
807 file = FILETOINT(*++p);
808 rank = RANKTOINT(*++p);
809 capture++;
811 else if (*p == '=') {
812 if (promo == -1 || promo == KING || promo == PAWN)
813 return E_PGN_PARSE;
815 *p++ = '=';
816 *p++ = toupper(pgn_int_to_piece(g->turn, promo));
817 *p = '\0';
818 break;
820 else {
821 #ifdef DEBUG
822 DUMP("Pawn (move: '%s'): %c\n", m, *p++);
823 #else
824 p++;
825 #endif
829 if (find_source_square(*g, b, PAWN, &sfile, &srank, file, rank) != 1)
830 return E_PGN_INVALID;
832 /* Not a pawn. */
833 else {
834 if (strcmp(m, "O-O") == 0)
835 castle = KINGSIDE;
836 else if (strcmp(m, "O-O-O") == 0)
837 castle = QUEENSIDE;
838 else {
839 p = m;
842 * P[fr][x]fr
844 * The first character is the piece but only if not a pawn.
846 if ((piece = pgn_piece_to_int(*p++)) == -1)
847 return E_PGN_PARSE;
850 * [fr]fr
852 if (strlen(m) > 3) {
854 * rfr
856 if (isdigit(*p))
857 srank = RANKTOINT(*p++);
859 * f[r]fr
861 else if (VALIDCOL(*p)) {
862 sfile = FILETOINT(*p++);
865 * frfr
867 if (isdigit(*p))
868 srank = RANKTOINT(*p++);
872 * xfr
874 if (*p == 'x') {
875 capture++;
876 p++;
881 * fr
883 * The destination square.
885 file = FILETOINT(*p++);
886 rank = RANKTOINT(*p++);
888 if (*p == '=')
889 promo == *++p;
891 if ((i = find_ambiguous(*g, b, piece, sfile, srank, file, rank))
892 != 1)
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;
904 *mp = m;
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)
913 int ret;
915 validate = 1;
916 ret = pgn_parse_move(g, b, m);
917 validate = 0;
918 return ret;
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);
928 int r, f;
930 validate = 1;
932 for (r = 1; VALIDRANK(r); r++) {
933 for (f = 1; VALIDFILE(f); f++) {
934 int n = 0;
936 if (val_piece_side(g.turn, b[RANKTOBOARD(r)][FILETOBOARD(f)].icon))
937 continue;
939 if (find_source_square(g, b, p, &file, (p == PAWN) ? &n : &rank, f,
940 r) != 0)
941 b[RANKTOBOARD(r)][FILETOBOARD(f)].valid = 1;
945 validate = 0;
948 #if 0
949 void _kingsquare(GAME g, BOARD b, int *kr, int *kc, int *okr, int *okc)
951 int row, col;
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)) {
959 *kr = row;
960 *kc = col;
962 else {
963 /* Opponent. */
964 *okr = row;
965 *okc = col;
972 int _checktest(GAME *g, BOARD b, int kr, int kc, int okr, int okc, int matetest)
974 int row, col;
976 pgn_switch_turn(g);
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)
986 continue;
988 if (pi == PAWN)
989 scol = col;
991 /* See if the move would leave ourselves in check. */
992 if (!matetest) {
993 pgn_switch_turn(g);
995 if (find_source_square(g, b, pi, kr, kc, &srow, &scol) == 0)
996 return -1;
998 pgn_switch_turn(g);
1001 if (find_source_square(g, b, pi, okr, okc, &srow, &scol) == 0) {
1002 pgn_switch_turn(g);
1003 return 1;
1008 pgn_switch_turn(g);
1009 return 0;
1012 int _checkmate_pawn_test(GAME *g, BOARD b, int row, int col, int *srow, int *scol)
1014 int r, c;
1016 r = 0;
1017 c = col;
1019 if (!_get_source_square(g, b, PAWN, row, col, &r, &c))
1020 return 0;
1022 c = col - 1;
1024 if (!_get_source_square(g, b, PAWN, row, col, &r, &c))
1025 return 0;
1027 c = col + 1;
1029 if (!_get_source_square(g, b, PAWN, row, col, &r, &c))
1030 return 0;
1032 return 1;
1035 int _checkmatetest(GAME *g, BOARD b, int kr, int kc, int okr, int okc)
1037 int row, col;
1038 int srow, scol;
1039 int check;
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++) {
1046 int n;
1048 for (n = 0; n < MAX_PIECES; n++) {
1049 int p;
1050 int nkr = kr, nkc = kc, nokr = okr, nokc = okc;
1051 BOARD oldboard;
1053 srow = scol = 0;
1055 if (n == PAWN) {
1056 if (_checkmate_pawn_test(g, b, row, col, &srow, &scol))
1057 continue;
1059 else {
1060 if (_get_source_square(g, b, n, row, col, &srow, &scol))
1061 continue;
1064 /* Valid move. */
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) {
1073 nokr = row;
1074 nokc = col;
1076 else {
1077 nkr = row;
1078 nkc = col;
1082 check = _checktest(g, b, nkr, nkc, nokr, nokc, 1);
1083 memcpy(b, oldboard, sizeof(BOARD));
1085 if (check == 0)
1086 goto done;
1091 check = 1;
1093 if (g->turn == WHITE)
1094 result = BLACKWINS;
1095 else
1096 result = WHITEWINS;
1098 done:
1099 return (check != 0) ? 1 : 0;
1102 /* FIXME */
1103 int _drawtest(GAME *g, BOARD b)
1105 int row, col;
1106 int other = 0;
1108 if (pgn_config.fmd && g->ply >= 50)
1109 return 1;
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);
1116 switch (piece) {
1117 case PAWN:
1118 return 0;
1119 break;
1120 case KING:
1121 break;
1122 default:
1123 other++;
1124 break;
1129 if (!other)
1130 return 1;
1132 return 0;
1134 #endif