Fix engine status.
[cboard.git] / src / move.c
blob3783c9acdac1cc5d61ec5b6484a2c22536680f6c
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 "conf.h"
30 #include "pgn.h"
31 #include "misc.h"
32 #include "move.h"
34 #ifdef DEBUG
35 #include "debug.h"
36 #endif
38 #ifdef WITH_DMALLOC
39 #include <dmalloc.h>
40 #endif
42 int piece_side(GAME g, int c)
44 if (c == pgn_int_to_piece(g.turn, OPEN_SQUARE))
45 return -1;
47 if (c < 'A')
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))
57 return 1;
59 return 0;
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)
72 int count = 0;
73 int ul = 0, ur = 0, dl = 0, dr = 0;
74 int i;
76 for (i = 1; VALIDFILE(i); i++) {
77 unsigned char n;
78 int r, c;
80 r = row + i;
81 c = col - 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))
88 count++;
90 ul++;
94 r = row + i;
95 c = col + i;
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))
102 count++;
104 ur++;
108 r = row - i;
109 c = col - i;
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))
116 count++;
118 dl++;
122 r = row - i;
123 c = col + i;
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))
130 count++;
132 dr++;
137 return count;
140 int count_knight(GAME g, BOARD b, int row, int col)
142 int r, c;
143 unsigned char p;
144 int count = 0;
146 r = row - 2;
147 c = col - 1;
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))
153 count++;
156 r = row - 2;
157 c = col + 1;
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))
163 count++;
166 r = row - 1;
167 c = col - 2;
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))
173 count++;
175 r = row - 1;
176 c = col + 2;
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))
182 count++;
185 r = row + 2;
186 c = col - 1;
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))
192 count++;
195 r = row + 2;
196 c = col + 1;
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))
202 count++;
205 r = row + 1;
206 c = col - 2;
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))
212 count++;
215 r = row + 1;
216 c = col + 2;
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))
222 count++;
225 return count;
228 int count_by_rank(GAME g, BOARD b, int piece, int row, int col)
230 int i;
231 int count = 0;
232 int u = 0, d = 0;
234 for (i = 1; VALIDRANK(i); i++) {
235 unsigned char n;
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))
242 count++;
244 u++;
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))
253 count++;
255 d++;
260 return count;
263 int count_by_file(GAME g, BOARD b, int piece, int row, int col)
265 int i;
266 int count = 0;
267 int l = 0, r = 0;
269 for (i = 1; VALIDFILE(i); i++) {
270 unsigned char n;
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))
277 count++;
279 r++;
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))
288 count++;
290 l++;
295 return count;
298 int count_by_rank_file(GAME g, BOARD b, int piece, int row, int col)
300 int count;
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)
308 int count = 0;
310 switch (piece) {
311 case PAWN:
312 case ROOK:
313 count = count_by_rank_file(g, b, piece, row, col);
314 break;
315 case BISHOP:
316 count = count_by_diag(g, b, piece, row, col);
317 break;
318 case QUEEN:
319 case KING:
320 count = count_by_rank_file(g, b, piece, row, col);
321 count += count_by_diag(g, b, piece, row, col);
322 break;
323 case KNIGHT:
324 count = count_knight(g, b, row, col);
325 break;
328 return count;
331 int piece_by_col(GAME g, BOARD b, int piece, int row, int col, int *r, int *c)
333 int i;
334 int count = 0;
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)) {
341 *c = i;
342 *r = row;
343 count++;
346 break;
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)) {
355 *c = i;
356 *r = row;
357 count++;
360 break;
364 return count;
367 int piece_by_row(GAME g, BOARD b, int piece, int row, int col, int *r, int *c)
369 int i;
370 int count = 0;
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)) {
377 *r = i;
378 *c = col;
379 count++;
382 break;
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)) {
391 *r = i;
392 *c = col;
393 count++;
396 break;
400 return count;
403 int piece_by_xy(GAME g, BOARD b, int piece, int row, int col, int *srow, int *scol)
405 int count = 0;
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)
414 int p;
416 if (!VALIDFILE(row) || !VALIDFILE(col))
417 return 2;
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)) {
423 *dstr = row;
424 *dstc = col;
425 return 1;
428 return 2;
431 return 0;
434 int piece_by_diag(GAME g, BOARD b, int piece, int row, int col, int *srow, int *scol)
436 int i, n;
437 int ul = 1, ur = 1, dl = 1, dr = 1;
438 int count = 0;
440 for (i = 1; VALIDFILE(i); i++) {
441 if (dr) {
442 n = piece_test(g, b, piece, row - i, col + i, srow, scol);
444 if (n == 1 && count++)
445 return 1;
446 else if (n == 2)
447 dr = 0;
450 if (dl) {
451 n = piece_test(g, b, piece, row - i, col - i, srow, scol);
453 if (n == 1 && count++)
454 return 1;
455 else if (n == 2)
456 dl = 0;
459 if (ur) {
460 n = piece_test(g, b, piece, row + i, col + i, srow, scol);
462 if (n == 1 && count++)
463 return 1;
464 else if (n == 2)
465 ur = 0;
468 if (ul) {
469 n = piece_test(g, b, piece, row + i, col - i, srow, scol);
471 if (n == 1 && count++)
472 return 1;
473 else if (n == 2)
474 ul = 0;
478 return (count) ? 1 : 0;
481 int valid_move(GAME g, BOARD b, int row, int col, int srow, int scol)
483 int p1, p2;
485 if (!VALIDFILE(srow) || !VALIDFILE(scol))
486 return 0;
488 if (row == srow && col == scol)
489 return 0;
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)
495 return 0;
497 if (piece_side(g, p1) == piece_side(g, p2))
498 return 0;
500 if (pgn_piece_to_int(p1) == PAWN && scol == col &&
501 pgn_piece_to_int(p2) != OPEN_SQUARE)
502 return 0;
504 return 1;
507 int castle_move(GAME *g, BOARD b, char which)
509 int row;
510 int n;
511 int p, p2, p3, p4;
513 row = (g->turn == WHITE) ? 1 : 8;
514 n = COLTOINT('e');
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))))
519 return 1;
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)))
527 return 1;
529 if (!validate) {
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);
542 else {
543 if ((g->turn == WHITE && (!TEST_FLAG(g->flags, GF_WQ_CASTLE))) ||
544 (g->turn == BLACK && (!TEST_FLAG(g->flags, GF_BQ_CASTLE))))
545 return 1;
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)))
555 return 1;
557 if (!validate) {
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);
571 return 0;
574 int get_source_yx(GAME *g, BOARD b, int piece, int row, int col, int *srow, int *scol)
576 int p = 0;
577 int count = 0;
578 int r, c;
579 int i;
580 int dist = 0;
582 /* FIXME valid move ambiguities. */
583 switch (piece) {
584 case PAWN:
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))
595 break;
598 if (p != PAWN || dist > 2)
599 return 1;
601 *srow = r;
602 dist = abs(*srow - row);
604 if (g->turn == WHITE) {
605 if ((*srow == 2 && dist > 2) || (*srow > 2 && dist > 1))
606 return 1;
608 else {
609 if ((*srow == 7 && dist > 2) || (*srow < 7 && dist > 1))
610 return 1;
613 if (dist == 2) {
614 p = pgn_piece_to_int(b[ROWTOBOARD(*srow + i)][COLTOBOARD(col)].icon);
615 if (p != OPEN_SQUARE)
616 return 1;
619 else if (*scol != col) {
620 if (abs(*scol - col) != 1)
621 return 1;
623 *srow = (g->turn == WHITE) ? row - 1 : row + 1;
625 if (pgn_piece_to_int(b[ROWTOBOARD(*srow)][COLTOBOARD(*scol)].icon)
626 != PAWN)
627 return 1;
629 piece = pgn_piece_to_int(b[ROWTOBOARD(row)][COLTOBOARD(col)].icon);
631 /* En Passant. */
632 if (piece == OPEN_SQUARE) {
633 /* Previous move was not 2 squares and a pawn. */
634 if (!TEST_FLAG(g->flags, GF_ENPASSANT))
635 return 1;
637 if (!b[ROWTOBOARD(row)][COLTOBOARD(col)].enpassant)
638 return 1;
640 r = (g->turn == WHITE) ? 6 : 3;
642 if (row != r)
643 return 1;
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)
649 return 1;
651 if (!validate)
652 b[ROWTOBOARD(r)][COLTOBOARD(col)].icon =
653 pgn_int_to_piece(g->turn, OPEN_SQUARE);
656 break;
657 case ROOK:
658 if (piece_by_xy(*g, b, ROOK, row, col, srow, scol) == 0)
659 return 1;
661 if (!validate && *scol == 1) {
662 if (g->turn == WHITE)
663 CLEAR_FLAG(g->flags, GF_WQ_CASTLE);
664 else
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);
670 else
671 CLEAR_FLAG(g->flags, GF_BK_CASTLE);
673 break;
674 case KNIGHT:
675 r = row - 2;
676 c = col - 1;
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)) {
682 *srow = r;
683 *scol = c;
684 count++;
686 if ((*srow && *srow == row) || (*scol && *scol == col))
687 break;
691 r = row - 2;
692 c = col + 1;
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)) {
698 *srow = r;
699 *scol = c;
700 count++;
702 if ((*srow && *srow == row) || (*scol && *scol == col))
703 break;
707 r = row + 2;
708 c = col - 1;
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)) {
714 *srow = r;
715 *scol = c;
716 count++;
718 if ((*srow && *srow == row) || (*scol && *scol == col))
719 break;
723 r = row + 2;
724 c = col + 1;
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)) {
730 *srow = r;
731 *scol = c;
732 count++;
734 if ((*srow && *srow == row) || (*scol && *scol == col))
735 break;
739 r = row - 1;
740 c = col - 2;
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)) {
746 *srow = r;
747 *scol = c;
748 count++;
750 if ((*srow && *srow == row) || (*scol && *scol == col))
751 break;
755 r = row - 1;
756 c = col + 2;
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)) {
762 *srow = r;
763 *scol = c;
764 count++;
766 if ((*srow && *srow == row) || (*scol && *scol == col))
767 break;
771 r = row + 1;
772 c = col + 2;
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)) {
778 *srow = r;
779 *scol = c;
780 count++;
782 if ((*srow && *srow == row) || (*scol && *scol == col))
783 break;
787 r = row + 1;
788 c = col - 2;
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)) {
794 *srow = r;
795 *scol = c;
796 count++;
798 if ((*srow && *srow == row) || (*scol && *scol == col))
799 break;
803 if ((count != 1 && !validate) || (validate && !count))
804 return 1;
806 break;
807 case BISHOP:
808 if (piece_by_diag(*g, b, BISHOP, row, col, srow, scol) == 0)
809 return 1;
810 break;
811 case QUEEN:
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)
814 return 1;
816 break;
817 case KING:
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)
820 return 1;
823 if (abs(*srow - row) > 1)
824 return 1;
826 dist = abs(*scol - col);
828 if (*scol == COLTOINT('e')) {
829 if (dist > 2)
830 return 1;
832 if (validate) {
833 if (dist == 2) {
834 if (col == 3) {
835 if (castle_move(g, b, QUEENSIDE))
836 return 1;
838 else if (col == 7) {
839 if (castle_move(g, b, KINGSIDE))
840 return 1;
842 else
843 return 1;
845 break;
848 if (dist > 1)
849 return 1;
851 break;
855 if (dist > 1)
856 return 1;
858 break;
859 default:
860 return 1;
863 if (valid_move(*g, b, row, col, *srow, *scol) == 0)
864 return 1;
866 if (piece == KING) {
867 if (!validate) {
868 if (g->turn == WHITE) {
869 CLEAR_FLAG(g->flags, GF_WK_CASTLE);
870 CLEAR_FLAG(g->flags, GF_WQ_CASTLE);
872 else {
873 CLEAR_FLAG(g->flags, GF_BK_CASTLE);
874 CLEAR_FLAG(g->flags, GF_BQ_CASTLE);
879 return 0;
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
884 * conversion.
886 char *pgn_a2a4tosan(GAME *g, BOARD b, char *m)
888 static char buf[MAX_SAN_MOVE_LEN + 1] = {0}, *cp = buf;
889 char *p = m;
890 int scol, srow, col, row;
891 int piece, piecei, spiece;
892 int trow, tcol;
893 int rowc, colc;
894 int promo = 0;
895 int tenpassant = 0;
896 int n;
898 // Not in a2a4 format. Probably already in SAN format.
899 if (!VALIDCOL(*p) || !VALIDROW(*(p + 1)) || !VALIDCOL(*(p + 2))
900 || !VALIDROW(*(p + 3)))
901 return m;
903 scol = COLTOINT(*p);
904 srow = ROWTOINT(*(p + 1));
905 col = COLTOINT(*(p + 2));
906 row = ROWTOINT(*(p + 3));
908 if (p[4]) {
909 if ((promo = pgn_piece_to_int(p[4])) == -1)
910 return NULL;
913 piece = b[ROWTOBOARD(srow)][COLTOBOARD(scol)].icon;
915 if ((piecei = pgn_piece_to_int(piece)) == -1 || piecei == OPEN_SQUARE)
916 return NULL;
918 spiece = piecei;
919 cp = buf;
920 colc = abs(scol - col);
922 if (srow == row && (row == 1 || row == 8) && scol == COLTOINT('e')
923 && colc > 1 && piecei == KING) {
924 if (scol - col < 0)
925 return "O-O";
926 else if (scol - col > 0)
927 return "O-O-O";
929 return NULL;
932 if (piecei != PAWN)
933 *cp++ = toupper(piece);
934 else {
935 /* En Passant. */
936 if (scol != col && pgn_piece_to_int(b[row][col].icon) == OPEN_SQUARE)
937 tenpassant = 1;
940 colc = piece_by_col(*g, b, piecei, row, col, &trow, &tcol);
941 rowc = piece_by_row(*g, b, piecei, row, col, &trow, &tcol);
942 n = colc + rowc;
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);
953 else if (colc >= 1)
954 *cp++ = INTTOCOL(scol);
955 else if (rowc >= 1)
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);
965 *cp++ = 'x';
968 *cp++ = INTTOCOL(col);
969 *cp++ = INTTOROW(row);
971 if (promo) {
972 *cp++ = '=';
973 *cp++ = toupper(pgn_int_to_piece(g->turn, promo));
976 *cp = '\0';
977 return buf;
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)
987 int row, col;
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)) {
995 *kr = row;
996 *kc = col;
998 else {
999 /* Opponent. */
1000 *okr = row;
1001 *okc = col;
1008 int checktest(GAME *g, BOARD b, int kr, int kc, int okr, int okc, int matetest)
1010 int row, col;
1012 pgn_switch_turn(g);
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)
1022 continue;
1024 if (pi == PAWN)
1025 scol = col;
1027 /* See if the move would leave ourselves in check. */
1028 if (!matetest) {
1029 pgn_switch_turn(g);
1031 if (get_source_yx(g, b, pi, kr, kc, &srow, &scol) == 0)
1032 return -1;
1034 pgn_switch_turn(g);
1037 if (get_source_yx(g, b, pi, okr, okc, &srow, &scol) == 0) {
1038 pgn_switch_turn(g);
1039 return 1;
1044 pgn_switch_turn(g);
1045 return 0;
1048 void board_reset_valid_moves(BOARD b)
1050 int row, col;
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)
1060 int row, col;
1062 validate = 1;
1063 *minr = *maxr = *minc = *maxc = 1;
1065 for (row = 1; VALIDFILE(row); row++) {
1066 for (col = 1; VALIDFILE(col); col++) {
1067 int sr = 0, sc = 0;
1069 if (get_source_yx(g, b, p, row, col, &sr, &sc)) {
1070 sr = 0;
1071 sc = scol;
1073 if (get_source_yx(g, b, p, row, col, &sr, &sc)) {
1074 sc = 0;
1075 sr = srow;
1077 if (get_source_yx(g, b, p, row, col, &sr, &sc)) {
1078 continue;
1083 if (sr != srow || sc != scol)
1084 continue;
1086 b[ROWTOBOARD(row)][COLTOBOARD(col)].valid = 1;
1089 if (row < *minr)
1090 *minr = row;
1092 if (row > *maxr)
1093 *maxr = row;
1095 if (col < *minc)
1096 *minc = col;
1098 if (col > *maxc)
1099 *maxc = col;
1104 validate = 0;
1107 static int checkmate_pawn_test(GAME *g, BOARD b, int row, int col, int *srow, int *scol)
1109 int r, c;
1111 r = 0;
1112 c = col;
1114 if (!get_source_yx(g, b, PAWN, row, col, &r, &c))
1115 return 0;
1117 c = col - 1;
1119 if (!get_source_yx(g, b, PAWN, row, col, &r, &c))
1120 return 0;
1122 c = col + 1;
1124 if (!get_source_yx(g, b, PAWN, row, col, &r, &c))
1125 return 0;
1127 return 1;
1130 static int checkmatetest(GAME *g, BOARD b, int kr, int kc, int okr, int okc)
1132 int row, col;
1133 int srow, scol;
1134 int check;
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++) {
1141 int n;
1143 for (n = 0; n < MAX_PIECES; n++) {
1144 int p;
1145 int nkr = kr, nkc = kc, nokr = okr, nokc = okc;
1146 BOARD oldboard;
1148 srow = scol = 0;
1150 if (n == PAWN) {
1151 if (checkmate_pawn_test(g, b, row, col, &srow, &scol))
1152 continue;
1154 else {
1155 if (get_source_yx(g, b, n, row, col, &srow, &scol))
1156 continue;
1159 /* Valid move. */
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) {
1168 nokr = row;
1169 nokc = col;
1171 else {
1172 nkr = row;
1173 nkc = col;
1177 check = checktest(g, b, nkr, nkc, nokr, nokc, 1);
1178 memcpy(b, oldboard, sizeof(BOARD));
1180 if (check == 0)
1181 goto done;
1186 check = 1;
1188 if (g->turn == WHITE)
1189 result = BLACKWINS;
1190 else
1191 result = WHITEWINS;
1193 done:
1194 return (check != 0) ? 1 : 0;
1197 /* FIXME */
1198 static int drawtest(BOARD b)
1200 int row, col;
1201 int other = 0;
1203 if (game[gindex].ply >= 50)
1204 return 1;
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);
1211 switch (piece) {
1212 case PAWN:
1213 return 0;
1214 break;
1215 case KING:
1216 break;
1217 default:
1218 other++;
1219 break;
1224 if (!other)
1225 return 1;
1227 return 0;
1230 void pgn_reset_enpassant(BOARD b)
1232 int r, c;
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)
1242 char *p;
1243 int piece, dstpiece;
1244 int i = 0;
1245 int srow = 0, scol = 0, row, col;
1246 int dist = 0;
1247 int promo = -1;
1248 int kr = 0, kc = 0, okr = 0, okc = 0;
1250 if (strlen(m) < 2)
1251 return 1;
1253 capture = 0;
1254 update_status_notify(*g, NULL);
1255 srow = row = col = scol = promo = piece = 0;
1256 again:
1257 p = (m) + strlen(m);
1259 while (!isdigit(*--p) && *p != 'O') {
1260 if (*p == '=') {
1261 promo = pgn_piece_to_int(i);
1262 i = 0;
1263 break;
1266 i = *p;
1267 *p = '\0';
1270 // Old promotion text (e8Q). Convert to SAN.
1271 if (pgn_piece_to_int(i) != -1) {
1272 p = (m) + strlen(m);
1273 *p++ = '=';
1274 *p++ = i;
1275 *p = '\0';
1276 goto again;
1279 if (strlen(m) < 2)
1280 return 1;
1282 p = m;
1284 /* Skip 'P'. */
1285 if (pgn_piece_to_int(*p) == PAWN)
1286 p++;
1287 /* Pawn. */
1288 if (VALIDCOL(*p)) {
1289 for (i = 0; *p; i++) {
1290 if (VALIDCOL(*p)) {
1291 if (i > 0)
1292 col = COLTOINT(*p++);
1293 else
1294 col = scol = COLTOINT(*p++);
1296 else if (VALIDROW(*p)) {
1297 if (1 > 1)
1298 row = ROWTOINT(*p++);
1299 else
1300 row = ROWTOINT(*p++);
1302 else if (*p == 'x') {
1303 col = COLTOINT(*++p);
1304 row = ROWTOINT(*++p);
1305 capture++;
1307 else if (*p == '=') {
1308 if (promo == -1 || promo == KING || promo == PAWN)
1309 return 1;
1311 *p++ = '=';
1312 *p++ = toupper(pgn_int_to_piece(g->turn, promo));
1313 *p = '\0';
1314 break;
1316 else {
1317 #ifdef DEBUG
1318 DUMP("Pawn (move: '%s'): %c\n", m, *p++);
1319 #else
1320 p++;
1321 #endif
1325 if (get_source_yx(g, b, PAWN, row, col, &srow, &scol))
1326 return 1;
1328 /* Not a pawn. */
1329 else {
1330 if (strcmp(m, "O-O") == 0)
1331 g->castle = KINGSIDE;
1332 else if (strcmp(m, "O-O-O") == 0)
1333 g->castle = QUEENSIDE;
1334 else {
1335 p = m;
1337 if ((piece = pgn_piece_to_int(*p++)) == -1)
1338 return 1;
1340 if (strlen(m) > 3) {
1341 if (isdigit(*p))
1342 srow = ROWTOINT(*p++);
1343 else if (VALIDCOL(*p))
1344 scol = COLTOINT(*p++);
1346 if (*p == 'x') {
1347 capture++;
1348 p++;
1352 col = COLTOINT(*p++);
1353 row = ROWTOINT(*p++);
1355 /* Get the source row and column. */
1356 if (srow == 0) {
1357 if (scol > 0) {
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)) {
1363 srow = i;
1364 break;
1368 if (srow == 0)
1369 return 1;
1371 else {
1372 if (get_source_yx(g, b, piece, row, col, &srow, &scol))
1373 return 1;
1376 else if (scol == 0) {
1377 if (srow > 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) {
1382 scol = i;
1383 break;
1387 if (scol == 0)
1388 return 1;
1390 else {
1391 if (get_source_yx(g, b, piece, row, col, &srow, &scol))
1392 return 1;
1398 piece = pgn_piece_to_int(b[ROWTOBOARD(srow)][COLTOBOARD(scol)].icon);
1399 dist = abs(srow - row);
1401 if (!validate) {
1402 if (piece == PAWN || capture)
1403 g->ply = 0;
1404 else
1405 g->ply++;
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;
1412 else
1413 b[ROWTOBOARD(srow + 1)][COLTOBOARD(scol)].enpassant = 1;
1415 SET_FLAG(g->flags, GF_ENPASSANT);
1417 else {
1418 CLEAR_FLAG(g->flags, GF_ENPASSANT);
1422 dstpiece = piece = b[ROWTOBOARD(row)][COLTOBOARD(col)].icon;
1424 if (g->castle) {
1425 if (castle_move(g, b, g->castle)) {
1426 g->castle = 0;
1427 return 1;
1430 goto done;
1433 if (pgn_piece_to_int(piece) != OPEN_SQUARE) {
1434 if (val_piece_side(g->turn, piece))
1435 return 2;
1437 /* FIXME
1438 * this doesn't belong in the library. This should update a capture
1439 * count or something.
1442 if (!validate)
1443 update_status_notify(*g, random_agony(*g));
1447 if (!validate) {
1448 if (promo)
1449 piece = pgn_int_to_piece(g->turn, promo);
1450 else
1451 piece = b[ROWTOBOARD(srow)][COLTOBOARD(scol)].icon;
1453 else
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;
1459 done:
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);
1472 pgn_switch_turn(g);
1474 if (g->castle) {
1475 p = m + strlen(m);
1476 g->castle = 0;
1479 CLEAR_FLAG(g->flags, GF_GAMEOVER);
1480 i = validate;
1481 validate = 1;
1483 if (drawtest(b)) {
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);
1488 else {
1489 switch (checktest(g, b, kr, kc, okr, okc, 0)) {
1490 case 0:
1491 break;
1492 case -1:
1493 validate = i;
1494 pgn_switch_turn(g);
1495 return 1;
1496 default:
1497 if (checkmatetest(g, b, kr, kc, okr, okc)) {
1498 *p++ = '#';
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);
1511 else
1512 *p++ = '+';
1514 *p = '\0';
1515 break;
1519 pgn_switch_turn(g);
1520 validate = i;
1521 return 0;