more functions use one less argument for the board.
[cboard.git] / src / move.c
blob98863eb9d5ca8b9805876dede97d747f7334e77c
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 "common.h"
29 #include "move.h"
31 int int_to_piece(GAME g, int which)
33 int p = 0;
35 switch (which) {
36 case PAWN:
37 p = 'p';
38 break;
39 case ROOK:
40 p = 'r';
41 break;
42 case KNIGHT:
43 p = 'n';
44 break;
45 case BISHOP:
46 p = 'b';
47 break;
48 case QUEEN:
49 p = 'q';
50 break;
51 case KING:
52 p = 'k';
53 break;
54 case OPEN_SQUARE:
55 p = '.';
56 break;
57 default:
58 break;
61 return (g.turn == WHITE) ? toupper(p) : p;
64 int piece_side(GAME g, int c)
66 if (c == int_to_piece(g, OPEN_SQUARE))
67 return -1;
69 if (c < 'A')
70 c = int_to_piece(g, c);
72 return (isupper(c)) ? WHITE : BLACK;
75 int val_piece_side(GAME g, int c)
77 if ((isupper(c) && g.turn == WHITE) ||
78 (islower(c) && g.turn == BLACK))
79 return 1;
81 return 0;
84 int piece_to_int(int p)
86 if (p == '.')
87 return OPEN_SQUARE;
89 p = tolower(p);
91 switch (p) {
92 case 'p':
93 return PAWN;
94 case 'r':
95 return ROOK;
96 case 'n':
97 return KNIGHT;
98 case 'b':
99 return BISHOP;
100 case 'q':
101 return QUEEN;
102 case 'k':
103 return KING;
104 default:
105 break;
108 return -1;
112 * Get the source row and column for a given piece.
114 * The following two functions find 'piece' from the given square 'col' and
115 * 'row' and store the resulting column or row in 'c' and 'r'. The return
116 * value is the number of 'piece' found (on the current status.turns side) or
117 * zero. Search for 'piece' stops when a non-empty square is found.
119 int piece_by_col(GAME g, int piece, int row, int col, int *r, int *c)
121 int i;
122 int count = 0;
124 for (i = col - 1; VALIDFILE(i); i--) {
125 int n = g.b[ROWTOBOARD(row)][COLTOBOARD(i)].icon;
127 if (piece_to_int(n) != OPEN_SQUARE) {
128 if (piece_to_int(n) == piece && val_piece_side(g, n)) {
129 *c = i;
130 *r = row;
131 count++;
134 break;
138 for (i = col + 1; VALIDFILE(i); i++) {
139 int n = g.b[ROWTOBOARD(row)][COLTOBOARD(i)].icon;
141 if (piece_to_int(n) != OPEN_SQUARE) {
142 if (piece_to_int(n) == piece && val_piece_side(g, n)) {
143 *c = i;
144 *r = row;
145 count++;
148 break;
152 return count;
155 int piece_by_row(GAME g, int piece, int row, int col, int *r, int *c)
157 int i;
158 int count = 0;
160 for (i = row + 1; VALIDFILE(i); i++) {
161 int n = g.b[ROWTOBOARD(i)][COLTOBOARD(col)].icon;
163 if (piece_to_int(n) != OPEN_SQUARE) {
164 if (piece_to_int(n) == piece && val_piece_side(g, n)) {
165 *r = i;
166 *c = col;
167 count++;
170 break;
174 for (i = row - 1; VALIDFILE(i); i--) {
175 int n = g.b[ROWTOBOARD(i)][COLTOBOARD(col)].icon;
177 if (piece_to_int(n) != OPEN_SQUARE) {
178 if (piece_to_int(n) == piece && val_piece_side(g, n)) {
179 *r = i;
180 *c = col;
181 count++;
184 break;
188 return count;
191 int piece_by_xy(GAME g, int piece, int row, int col, int *srow, int *scol)
193 int count = 0;
195 count = piece_by_row(g, piece, row, col, srow, scol);
196 count += piece_by_col(g, piece, row, col, srow, scol);
197 return (count != 1) ? 0 : 1;
200 int piece_test(GAME g, int piece, int row, int col, int *dstr, int *dstc)
202 int p;
204 if (!VALIDFILE(row) || !VALIDFILE(col))
205 return 2;
207 p = g.b[ROWTOBOARD(row)][COLTOBOARD(col)].icon;
209 if (piece_to_int(p) != OPEN_SQUARE) {
210 if (piece_to_int(p) == piece && val_piece_side(g, p)) {
211 *dstr = row;
212 *dstc = col;
213 return 1;
216 return 2;
219 return 0;
222 int piece_by_diag(GAME g, int piece, int row, int col, int *srow, int *scol)
224 int i, n;
225 int ul = 1, ur = 1, dl = 1, dr = 1;
226 int count = 0;
228 for (i = 1; VALIDFILE(i); i++) {
229 if (dr) {
230 n = piece_test(g, piece, row - i, col + i, srow, scol);
232 if (n == 1 && count++)
233 return 1;
234 else if (n == 2)
235 dr = 0;
238 if (dl) {
239 n = piece_test(g, piece, row - i, col - i, srow, scol);
241 if (n == 1 && count++)
242 return 1;
243 else if (n == 2)
244 dl = 0;
247 if (ur) {
248 n = piece_test(g, piece, row + i, col + i, srow, scol);
250 if (n == 1 && count++)
251 return 1;
252 else if (n == 2)
253 ur = 0;
256 if (ul) {
257 n = piece_test(g, piece, row + i, col - i, srow, scol);
259 if (n == 1 && count++)
260 return 1;
261 else if (n == 2)
262 ul = 0;
266 return (count) ? 1 : 0;
269 int valid_move(GAME g, int row, int col, int srow, int scol)
271 int p1, p2;
273 if (!VALIDFILE(srow) || !VALIDFILE(scol))
274 return 0;
276 if (row == srow && col == scol)
277 return 0;
279 p1 = g.b[ROWTOBOARD(srow)][COLTOBOARD(scol)].icon;
280 p2 = g.b[ROWTOBOARD(row)][COLTOBOARD(col)].icon;
282 if (piece_to_int(p1) == OPEN_SQUARE)
283 return 0;
285 if (piece_side(g, p1) == piece_side(g, p2))
286 return 0;
288 if (piece_to_int(p1) == PAWN && scol == col &&
289 piece_to_int(p2) != OPEN_SQUARE)
290 return 0;
292 return 1;
295 int castle_move(GAME *g, int which)
297 int row;
298 int n;
299 int p, p2, p3, p4;
301 row = ((*g).turn == WHITE) ? 1 : 8;
302 n = COLTOINT('e');
304 if (which == KINGSIDE) {
305 if (((*g).turn == WHITE && (TEST_FLAG((*g).flags, GF_WK) ||
306 TEST_FLAG((*g).flags, GF_WKR))) ||
307 ((*g).turn == BLACK && (TEST_FLAG((*g).flags, GF_BK)
308 || TEST_FLAG((*g).flags, GF_BKR))))
309 return 1;
311 p = (*g).b[ROWTOBOARD(row)][COLTOBOARD((n + 1))].icon;
312 p2 = (*g).b[ROWTOBOARD(row)][COLTOBOARD((n + 2))].icon;
313 p3 = (*g).b[ROWTOBOARD(row)][COLTOBOARD((n + 3))].icon;
315 if (piece_to_int(p) != OPEN_SQUARE || piece_to_int(p2) != OPEN_SQUARE
316 || (piece_to_int(p3) != ROOK && val_piece_side(*g, p3)))
317 return 1;
319 if (!validate_move) {
320 (*g).b[ROWTOBOARD(row)][COLTOBOARD(COLTOINT('e'))].icon =
321 int_to_piece(*g, OPEN_SQUARE);
322 (*g).b[ROWTOBOARD(row)][COLTOBOARD(6)].icon = int_to_piece(*g, ROOK);
323 (*g).b[ROWTOBOARD(row)][COLTOBOARD(7)].icon = int_to_piece(*g, KING);
324 (*g).b[ROWTOBOARD(row)][COLTOBOARD(8)].icon = int_to_piece(*g, OPEN_SQUARE);
326 if ((*g).turn == WHITE) {
327 SET_FLAG((*g).flags, GF_WK);
328 SET_FLAG((*g).flags, GF_WKR);
329 update_status_notify(*g, "%s", NOTIFY_WCASTLEK);
331 else if ((*g).turn == BLACK) {
332 SET_FLAG((*g).flags, GF_BK);
333 SET_FLAG((*g).flags, GF_BKR);
334 update_status_notify(*g, "%s", NOTIFY_BCASTLEK);
338 else {
339 if (((*g).turn == WHITE && (TEST_FLAG((*g).flags, GF_WK) ||
340 TEST_FLAG((*g).flags, GF_WQR))) ||
341 ((*g).turn == BLACK && (TEST_FLAG((*g).flags, GF_BK)
342 || TEST_FLAG((*g).flags, GF_BQR))))
343 return 1;
345 p = (*g).b[ROWTOBOARD(row)][COLTOBOARD((n - 1))].icon;
346 p2 = (*g).b[ROWTOBOARD(row)][COLTOBOARD((n - 2))].icon;
347 p3 = (*g).b[ROWTOBOARD(row)][COLTOBOARD((n - 3))].icon;
348 p4 = (*g).b[ROWTOBOARD(row)][COLTOBOARD((n - 4))].icon;
350 if (piece_to_int(p) != OPEN_SQUARE || piece_to_int(p2) != OPEN_SQUARE
351 || piece_to_int(p3) != OPEN_SQUARE ||
352 (piece_to_int(p4) != ROOK && val_piece_side(*g, p4)))
353 return 1;
355 if (!validate_move) {
356 (*g).b[ROWTOBOARD(row)][COLTOBOARD(1)].icon = int_to_piece(*g, OPEN_SQUARE);
357 (*g).b[ROWTOBOARD(row)][COLTOBOARD(COLTOINT('e'))].icon =
358 int_to_piece(*g, OPEN_SQUARE);
359 (*g).b[ROWTOBOARD(row)][COLTOBOARD(3)].icon = int_to_piece(*g, KING);
360 (*g).b[ROWTOBOARD(row)][COLTOBOARD(4)].icon = int_to_piece(*g, ROOK);
362 if ((*g).turn == WHITE) {
363 SET_FLAG((*g).flags, GF_WK);
364 SET_FLAG((*g).flags, GF_WQR);
365 update_status_notify(*g, "%s", NOTIFY_WCASTLEQ);
367 else if ((*g).turn == BLACK) {
368 SET_FLAG((*g).flags, GF_BK);
369 SET_FLAG((*g).flags, GF_BQR);
370 update_status_notify(*g, "%s", NOTIFY_BCASTLEQ);
375 return 0;
378 int get_source_yx(GAME *g, int piece, int row, int col, int *srow, int *scol)
380 int p = 0;
381 int count = 0;
382 int r, c;
383 int i;
384 int dist = 0;
386 /* FIXME valid move ambiguities. */
387 switch (piece) {
388 case PAWN:
389 if (*srow == 0 && *scol == col) {
390 i = ((*g).turn == WHITE) ? -1 : 1;
392 /* Find the first pawn in the current column. */
393 for (r = row + i, dist = 0; VALIDFILE(r); r += i, dist++) {
394 int n = (*g).b[ROWTOBOARD(r)][COLTOBOARD(col)].icon;
396 p = piece_to_int(n);
398 if (p == PAWN && val_piece_side(*g, n))
399 break;
402 if (p != PAWN || dist > 2)
403 return 1;
405 *srow = r;
406 dist = abs(*srow - row);
408 if ((*g).turn == WHITE) {
409 if ((*srow == 2 && dist > 2) || (*srow > 2 && dist > 1))
410 return 1;
412 else {
413 if ((*srow == 7 && dist > 2) || (*srow < 7 && dist > 1))
414 return 1;
417 if (dist == 2) {
418 p = piece_to_int((*g).b[ROWTOBOARD(*srow + i)][COLTOBOARD(col)].icon);
419 if (p != OPEN_SQUARE)
420 return 1;
423 else if (*scol != col) {
424 if (abs(*scol - col) != 1)
425 return 1;
427 *srow = ((*g).turn == WHITE) ? row - 1 : row + 1;
429 if (piece_to_int((*g).b[ROWTOBOARD(*srow)][COLTOBOARD(*scol)].icon)
430 != PAWN)
431 return 1;
433 piece = piece_to_int((*g).b[ROWTOBOARD(row)][COLTOBOARD(col)].icon);
435 /* En Passant. */
436 if (piece == OPEN_SQUARE) {
437 /* Previous move was not 2 squares and a pawn. */
438 if (!TEST_FLAG((*g).flags, GF_ENPASSANT))
439 return 1;
441 if (!(*g).b[ROWTOBOARD(row)][COLTOBOARD(col)].enpassant)
442 return 1;
444 r = ((*g).turn == WHITE) ? 6 : 3;
446 if (row != r)
447 return 1;
449 r = ((*g).turn == WHITE) ? row - 1 : row + 1;
450 piece = (*g).b[ROWTOBOARD(r)][COLTOBOARD(col)].icon;
452 if (piece_to_int(piece) != PAWN)
453 return 1;
455 if (!validate_move) {
456 (*g).b[ROWTOBOARD(r)][COLTOBOARD(col)].icon =
457 int_to_piece(*g, OPEN_SQUARE);
459 if (((*g).turn == WHITE && (*g).side != WHITE) ||
460 ((*g).turn == BLACK && (*g).side != BLACK))
461 update_status_notify(*g, "%s", NOTIFY_ENPASSANT);
465 break;
466 case ROOK:
467 if (piece_by_xy(*g, ROOK, row, col, srow, scol) == 0)
468 return 1;
470 if (!validate_move && *scol == 1) {
471 if ((*g).turn == WHITE)
472 SET_FLAG((*g).flags, GF_WQR);
473 else
474 SET_FLAG((*g).flags, GF_BQR);
476 else if (!validate_move && *scol == 8) {
477 if ((*g).turn == WHITE)
478 SET_FLAG((*g).flags, GF_WKR);
479 else
480 SET_FLAG((*g).flags, GF_BKR);
482 break;
483 case KNIGHT:
484 r = row - 2;
485 c = col - 1;
486 p = (*g).b[ROWTOBOARD(r)][COLTOBOARD(c)].icon;
488 if (VALIDFILE(r) && VALIDFILE(c)) {
489 if (piece_to_int(p) == KNIGHT && val_piece_side(*g, p)) {
490 *srow = r;
491 *scol = c;
492 count++;
494 if ((*srow && *srow == row) || (*scol && *scol == col))
495 break;
499 r = row - 2;
500 c = col + 1;
501 p = (*g).b[ROWTOBOARD(r)][COLTOBOARD(c)].icon;
503 if (VALIDFILE(r) && VALIDFILE(c)) {
505 if (piece_to_int(p) == KNIGHT && val_piece_side(*g, p)) {
506 *srow = r;
507 *scol = c;
508 count++;
510 if ((*srow && *srow == row) || (*scol && *scol == col))
511 break;
515 r = row + 2;
516 c = col - 1;
517 p = (*g).b[ROWTOBOARD(r)][COLTOBOARD(c)].icon;
519 if (VALIDFILE(r) && VALIDFILE(c)) {
520 if (piece_to_int(p) == KNIGHT && val_piece_side(*g, p)) {
521 *srow = r;
522 *scol = c;
523 count++;
525 if ((*srow && *srow == row) || (*scol && *scol == col))
526 break;
530 r = row + 2;
531 c = col + 1;
532 p = (*g).b[ROWTOBOARD(r)][COLTOBOARD(c)].icon;
534 if (VALIDFILE(r) && VALIDFILE(c)) {
535 if (piece_to_int(p) == KNIGHT && val_piece_side(*g, p)) {
536 *srow = r;
537 *scol = c;
538 count++;
540 if ((*srow && *srow == row) || (*scol && *scol == col))
541 break;
545 r = row - 1;
546 c = col - 2;
547 p = (*g).b[ROWTOBOARD(r)][COLTOBOARD(c)].icon;
549 if (VALIDFILE(r) && VALIDFILE(c)) {
550 if (piece_to_int(p) == KNIGHT && val_piece_side(*g, p)) {
551 *srow = r;
552 *scol = c;
553 count++;
555 if ((*srow && *srow == row) || (*scol && *scol == col))
556 break;
560 r = row - 1;
561 c = col + 2;
562 p = (*g).b[ROWTOBOARD(r)][COLTOBOARD(c)].icon;
564 if (VALIDFILE(r) && VALIDFILE(c)) {
565 if (piece_to_int(p) == KNIGHT && val_piece_side(*g, p)) {
566 *srow = r;
567 *scol = c;
568 count++;
570 if ((*srow && *srow == row) || (*scol && *scol == col))
571 break;
575 r = row + 1;
576 c = col + 2;
577 p = (*g).b[ROWTOBOARD(r)][COLTOBOARD(c)].icon;
579 if (VALIDFILE(r) && VALIDFILE(c)) {
580 if (piece_to_int(p) == KNIGHT && val_piece_side(*g, p)) {
581 *srow = r;
582 *scol = c;
583 count++;
585 if ((*srow && *srow == row) || (*scol && *scol == col))
586 break;
590 r = row + 1;
591 c = col - 2;
592 p = (*g).b[ROWTOBOARD(r)][COLTOBOARD(c)].icon;
594 if (VALIDFILE(r) && VALIDFILE(c)) {
595 if (piece_to_int(p) == KNIGHT && val_piece_side(*g, p)) {
596 *srow = r;
597 *scol = c;
598 count++;
600 if ((*srow && *srow == row) || (*scol && *scol == col))
601 break;
605 if ((count != 1 && !validate_move) || (validate_move && !count))
606 return 1;
608 break;
609 case BISHOP:
610 if (piece_by_diag(*g, BISHOP, row, col, srow, scol) == 0)
611 return 1;
612 break;
613 case QUEEN:
614 if (piece_by_xy(*g, QUEEN, row, col, srow, scol) == 0) {
615 if (piece_by_diag(*g, QUEEN, row, col, srow, scol) == 0)
616 return 1;
618 break;
619 case KING:
620 if (piece_by_xy(*g, KING, row, col, srow, scol) == 0) {
621 if (piece_by_diag(*g, KING, row, col, srow, scol) == 0)
622 return 1;
625 if (abs(*srow - row) > 1)
626 return 1;
628 dist = abs(*scol - col);
630 if (*scol == COLTOINT('e')) {
631 if (dist > 2)
632 return 1;
634 if (validate_move) {
635 if (dist == 2) {
636 if (col == 3) {
637 if (castle_move(g, QUEENSIDE))
638 return 1;
640 else if (col == 7) {
641 if (castle_move(g, KINGSIDE))
642 return 1;
644 else
645 return 1;
647 break;
650 if (dist > 1)
651 return 1;
653 break;
657 if (dist > 1)
658 return 1;
660 break;
661 default:
662 return 1;
665 if (valid_move(*g, row, col, *srow, *scol) == 0)
666 return 1;
668 if (piece == KING) {
669 if (!validate_move) {
670 if ((*g).turn == WHITE)
671 SET_FLAG((*g).flags, GF_WK);
672 else
673 SET_FLAG((*g).flags, GF_BK);
677 return 0;
680 /* This function converts a2a4 formatted moves to SAN format. Minimal checks
681 * are performed here. The real checks are in parse_move_text() after the
682 * conversion.
684 char *a2a4tosan(GAME *g, char *m)
686 static char buf[MAX_PGN_MOVE_LEN + 1] = {0}, *cp = buf;
687 char *p = m;
688 int scol, srow, col, row;
689 int piece, piecei, spiece;
690 int trow, tcol;
691 int rowc, colc;
692 int promo = 0;
693 int tenpassant = 0;
694 int n;
696 if (!VALIDCOL(*p) || !VALIDROW(*(p + 1)) || !VALIDCOL(*(p + 2))
697 || !VALIDROW(*(p + 3)))
698 return m;
700 scol = COLTOINT(*p);
701 srow = ROWTOINT(*(p + 1));
702 col = COLTOINT(*(p + 2));
703 row = ROWTOINT(*(p + 3));
705 if (p[4]) {
706 if ((promo = piece_to_int(p[4])) == -1)
707 return NULL;
710 piece = (*g).b[ROWTOBOARD(srow)][COLTOBOARD(scol)].icon;
712 if ((piecei = piece_to_int(piece)) == -1 || piecei == OPEN_SQUARE)
713 return NULL;
715 spiece = piecei;
716 cp = buf;
717 colc = abs(scol - col);
719 if (srow == row && (row == 1 || row == 8) && scol == COLTOINT('e')
720 && colc > 1 && piecei == KING) {
721 if (scol - col < 0)
722 return "O-O";
723 else if (scol - col > 0)
724 return "O-O-O";
726 return NULL;
729 if (piecei != PAWN)
730 *cp++ = toupper(piece);
731 else {
732 /* En Passant. */
733 if (scol != col && piece_to_int((*g).b[row][col].icon) == OPEN_SQUARE)
734 tenpassant = 1;
737 colc = piece_by_col(*g, piecei, row, col, &trow, &tcol);
738 rowc = piece_by_row(*g, piecei, row, col, &trow, &tcol);
739 n = colc + rowc;
741 if (piecei == KNIGHT) {
742 if (get_source_yx(g, KNIGHT, row, col, &trow, &tcol) == 1)
743 *cp++ = INTTOCOL(scol);
745 else if (n > 1 && piecei != PAWN) {
746 if (colc > 1 && rowc > 1) {
747 *cp++ = INTTOCOL(scol);
748 *cp++ = INTTOROW(srow);
750 else if (colc >= 1)
751 *cp++ = INTTOCOL(scol);
752 else if (rowc >= 1)
753 *cp++ = INTTOROW(srow);
756 piece = (*g).b[ROWTOBOARD(row)][COLTOBOARD(col)].icon;
758 if ((piecei = piece_to_int(piece)) != OPEN_SQUARE || tenpassant) {
759 if (tenpassant || spiece == PAWN)
760 *cp++ = INTTOCOL(scol);
762 *cp++ = 'x';
765 *cp++ = INTTOCOL(col);
766 *cp++ = INTTOROW(row);
768 if (promo) {
769 *cp++ = '=';
770 *cp++ = toupper(int_to_piece(*g, promo));
773 *cp = '\0';
774 return buf;
777 void switch_turn(GAME *g)
779 if ((*g).turn == WHITE)
780 (*g).turn = BLACK;
781 else
782 (*g).turn = WHITE;
785 static void kingsquare(GAME g, int *kr, int *kc, int *okr, int *okc)
787 int row, col;
789 for (row = 1; VALIDFILE(row); row++) {
790 for (col = 1; VALIDFILE(col); col++) {
791 int p = g.b[ROWTOBOARD(row)][COLTOBOARD(col)].icon;
793 if (piece_to_int(p) == KING) {
794 if (val_piece_side(g, p)) {
795 *kr = row;
796 *kc = col;
798 else {
799 /* Opponent. */
800 *okr = row;
801 *okc = col;
808 int checktest(GAME *g, int kr, int kc, int okr, int okc, int matetest)
810 int row, col;
812 switch_turn(&(*g));
814 /* See if the move would put our opponent in check. */
815 for (row = 1; VALIDFILE(row); row++) {
816 for (col = 1; VALIDFILE(col); col++) {
817 int srow = 0, scol = 0;
818 int p = (*g).b[ROWTOBOARD(row)][COLTOBOARD(col)].icon;
819 int pi = piece_to_int(p);
821 if (pi == OPEN_SQUARE)
822 continue;
824 if (pi == PAWN)
825 scol = col;
827 /* See if the move would leave ourselves in check. */
828 if (!matetest) {
829 switch_turn(&(*g));
831 if (get_source_yx(g, pi, kr, kc, &srow, &scol) == 0)
832 return -1;
834 switch_turn(&(*g));
837 if (get_source_yx(g, pi, okr, okc, &srow, &scol) == 0) {
838 switch_turn(&(*g));
839 return 1;
844 switch_turn(&(*g));
845 return 0;
848 void reset_valid_moves(BOARD b)
850 int row, col;
852 for (row = 0; row < 8; row++) {
853 for (col = 0; col < 8; col++) {
854 b[row][col].valid = 0;
855 b[row][col].movecount = 0;
860 void get_valid_moves(GAME *g, int p, int srow, int scol, int *minr, int *maxr,
861 int *minc, int *maxc)
863 int row, col;
865 validate_move = 1;
866 *minr = *maxr = *minc = *maxc = 1;
868 for (row = 1; VALIDFILE(row); row++) {
869 for (col = 1; VALIDFILE(col); col++) {
870 int sr = 0, sc = 0;
872 if (get_source_yx(g, p, row, col, &sr, &sc)) {
873 sr = 0;
874 sc = scol;
876 if (get_source_yx(g, p, row, col, &sr, &sc)) {
877 sc = 0;
878 sr = srow;
880 if (get_source_yx(g, p, row, col, &sr, &sc)) {
881 continue;
886 if (sr != srow || sc != scol)
887 continue;
889 (*g).b[ROWTOBOARD(row)][COLTOBOARD(col)].valid = 1;
892 if (row < *minr)
893 *minr = row;
895 if (row > *maxr)
896 *maxr = row;
898 if (col < *minc)
899 *minc = col;
901 if (col > *maxc)
902 *maxc = col;
907 validate_move = 0;
910 static int checkmate_pawn_test(GAME *g, int row, int col, int *srow, int *scol)
912 int r, c;
914 r = 0;
915 c = col;
917 if (!get_source_yx(g, PAWN, row, col, &r, &c))
918 return 0;
920 c = col - 1;
922 if (!get_source_yx(g, PAWN, row, col, &r, &c))
923 return 0;
925 c = col + 1;
927 if (!get_source_yx(g, PAWN, row, col, &r, &c))
928 return 0;
930 return 1;
933 static int checkmatetest(GAME *g, int kr, int kc, int okr, int okc)
935 int row, col;
936 int srow, scol;
937 int check;
939 /* For each square on the board see if each peace has a valid move, and if
940 * so, see if it would leave ourselves or the opponent in check.
942 for (row = 1; VALIDFILE(row); row++) {
943 for (col = 1; VALIDFILE(col); col++) {
944 int n;
946 for (n = 0; n < MAX_PIECES; n++) {
947 int p;
948 int nkr = kr, nkc = kc, nokr = okr, nokc = okc;
949 BOARD oldboard;
951 srow = scol = 0;
953 if (n == PAWN) {
954 if (checkmate_pawn_test(g, row, col, &srow, &scol))
955 continue;
957 else {
958 if (get_source_yx(g, n, row, col, &srow, &scol))
959 continue;
962 /* Valid move. */
963 memcpy(oldboard, (*g).b, sizeof(BOARD));
964 p = (*g).b[ROWTOBOARD(srow)][COLTOBOARD(scol)].icon;
965 (*g).b[ROWTOBOARD(row)][COLTOBOARD(col)].icon = p;
966 (*g).b[ROWTOBOARD(srow)][COLTOBOARD(scol)].icon =
967 int_to_piece(*g, OPEN_SQUARE);
969 if (piece_to_int(p) == KING) {
970 if (piece_side(*g, p) == (*g).turn) {
971 nokr = row;
972 nokc = col;
974 else {
975 nkr = row;
976 nkc = col;
980 check = checktest(g, nkr, nkc, nokr, nokc, 1);
981 memcpy((*g).b, oldboard, sizeof(BOARD));
983 if (check == 0)
984 goto done;
989 check = 1;
991 if ((*g).turn == WHITE)
992 result = BLACKWINS;
993 else
994 result = WHITEWINS;
996 done:
997 return (check != 0) ? 1 : 0;
1000 /* FIXME */
1001 static int drawtest(BOARD b)
1003 int row, col;
1004 int other = 0;
1007 if (game[gindex].ply >= 50)
1008 return 1;
1011 for (row = 1; VALIDFILE(row); row++) {
1012 for (col = 1; VALIDFILE(col); col++) {
1013 int p = b[ROWTOBOARD(row)][COLTOBOARD(col)].icon;
1014 int piece = piece_to_int(p);
1016 switch (piece) {
1017 case PAWN:
1018 return 0;
1019 break;
1020 case KING:
1021 break;
1022 default:
1023 other++;
1024 break;
1029 if (!other)
1030 return 1;
1032 return 0;
1035 static void reset_enpassant(BOARD b)
1037 int r, c;
1039 for (r = 0; r < 8; r++) {
1040 for (c = 0; c < 8; c++)
1041 b[r][c].enpassant = 0;
1045 int parse_move_text(GAME *g, char *m)
1047 char *p;
1048 int piece, dstpiece;
1049 int i = 0;
1050 int srow = 0, scol = 0, row, col;
1051 int dist = 0;
1052 int promo = -1;
1053 int kr = 0, kc = 0, okr = 0, okc = 0;
1054 int plyincr = 0;
1056 if (strlen(m) < 2)
1057 return 1;
1059 capture = 0;
1060 update_status_notify(*g, NULL);
1061 srow = row = col = scol = promo = piece = 0;
1062 again:
1063 p = (m) + strlen(m);
1065 while (!isdigit(*--p) && *p != 'O') {
1066 if (*p == '=') {
1067 promo = piece_to_int(i);
1068 i = 0;
1069 break;
1072 i = *p;
1073 *p = '\0';
1076 // Old promotion text (e8Q). Convert to SAN.
1077 if (piece_to_int(i) != -1) {
1078 p = (m) + strlen(m);
1079 *p++ = '=';
1080 *p++ = i;
1081 *p = '\0';
1082 goto again;
1085 if (strlen(m) < 2)
1086 return 1;
1088 p = m;
1090 /* Skip 'P'. */
1091 if (piece_to_int(*p) == PAWN)
1092 p++;
1094 /* Pawn. */
1095 if (VALIDCOL(*p)) {
1096 for (i = 0; *p; i++) {
1097 if (VALIDCOL(*p)) {
1098 if (i > 0)
1099 col = COLTOINT(*p++);
1100 else
1101 col = scol = COLTOINT(*p++);
1103 else if (VALIDROW(*p)) {
1104 if (1 > 1)
1105 row = ROWTOINT(*p++);
1106 else
1107 row = ROWTOINT(*p++);
1109 else if (*p == 'x') {
1110 col = COLTOINT(*++p);
1111 row = ROWTOINT(*++p);
1112 plyincr++;
1113 capture++;
1115 else if (*p == '=') {
1116 if (promo == -1 || promo == KING || promo == PAWN)
1117 return 1;
1119 *p++ = '=';
1120 *p++ = toupper(int_to_piece(*g, promo));
1121 *p = '\0';
1122 break;
1124 else {
1125 #ifdef DEBUG
1126 if (debug)
1127 DUMP("Pawn (move: '%s'): %c\n", m, *p++);
1128 #else
1129 p++;
1130 #endif
1134 if (get_source_yx(g, PAWN, row, col, &srow, &scol))
1135 return 1;
1137 /* Not a pawn. */
1138 else {
1139 if (strcmp(m, "O-O") == 0)
1140 (*g).castle = KINGSIDE;
1141 else if (strcmp(m, "O-O-O") == 0)
1142 (*g).castle = QUEENSIDE;
1143 else {
1144 p = m;
1146 if ((piece = piece_to_int(*p++)) == -1)
1147 return 1;
1149 if (strlen(m) > 3) {
1150 if (isdigit(*p))
1151 srow = ROWTOINT(*p++);
1152 else if (VALIDCOL(*p))
1153 scol = COLTOINT(*p++);
1155 if (*p == 'x') {
1156 capture++;
1157 p++;
1161 col = COLTOINT(*p++);
1162 row = ROWTOINT(*p++);
1164 /* Get the source row and column. */
1165 if (srow == 0) {
1166 if (scol > 0) {
1167 for (i = 1; VALIDFILE(i); i++) {
1168 int fpiece = (*g).b[ROWTOBOARD(i)][COLTOBOARD(scol)].icon;
1170 if (piece == piece_to_int(fpiece) &&
1171 val_piece_side(*g, fpiece)) {
1172 srow = i;
1173 break;
1177 if (srow == 0)
1178 return 1;
1180 else {
1181 if (get_source_yx(g, piece, row, col, &srow, &scol))
1182 return 1;
1185 else if (scol == 0) {
1186 if (srow > 0) {
1187 for (i = 1; VALIDFILE(i); i++) {
1188 int fpiece = piece_to_int((*g).b[ROWTOBOARD(srow)][COLTOBOARD(i)].icon);
1190 if (piece == fpiece) {
1191 scol = i;
1192 break;
1196 if (scol == 0)
1197 return 1;
1199 else {
1200 if (get_source_yx(g, piece, row, col, &srow, &scol))
1201 return 1;
1207 piece = piece_to_int((*g).b[ROWTOBOARD(srow)][COLTOBOARD(scol)].icon);
1208 dist = abs(srow - row);
1210 if (!validate_move) {
1211 reset_enpassant((*g).b);
1213 if (piece == PAWN && dist == 2) {
1214 if ((*g).turn == WHITE)
1215 (*g).b[ROWTOBOARD(srow - 1)][COLTOBOARD(scol)].enpassant = 1;
1216 else
1217 (*g).b[ROWTOBOARD(srow + 1)][COLTOBOARD(scol)].enpassant = 1;
1219 SET_FLAG((*g).flags, GF_ENPASSANT);
1221 else {
1222 CLEAR_FLAG((*g).flags, GF_ENPASSANT);
1226 if (piece == PAWN)
1227 plyincr++;
1229 dstpiece = piece = (*g).b[ROWTOBOARD(row)][COLTOBOARD(col)].icon;
1231 if ((*g).castle) {
1232 if (castle_move(g, (*g).castle)) {
1233 (*g).castle = 0;
1234 return 1;
1237 goto done;
1240 if (piece_to_int(piece) != OPEN_SQUARE) {
1241 if (val_piece_side(*g, piece))
1242 return 2;
1244 if (!validate_move) {
1245 if (piece_side(*g, piece) == WHITE)
1246 (*g).bcaptures++;
1247 else
1248 (*g).wcaptures++;
1250 update_status_notify(*g, random_agony(*g));
1254 if (!validate_move) {
1255 if (promo) {
1256 piece = int_to_piece(*g, promo);
1258 if (((*g).turn == WHITE && (*g).side != WHITE) ||
1259 ((*g).turn == BLACK && (*g).side != BLACK))
1260 update_status_notify(*g, "%s", NOTIFY_PROMOTION);
1262 else
1263 piece = (*g).b[ROWTOBOARD(srow)][COLTOBOARD(scol)].icon;
1265 else
1266 piece = (*g).b[ROWTOBOARD(srow)][COLTOBOARD(scol)].icon;
1268 (*g).b[ROWTOBOARD(srow)][COLTOBOARD(scol)].icon = int_to_piece(*g, OPEN_SQUARE);
1269 (*g).b[ROWTOBOARD(row)][COLTOBOARD(col)].icon = piece;
1271 done:
1272 if (!validate_move && capture && piece_to_int(dstpiece) == ROOK) {
1273 if (row == 1 && col == 1)
1274 SET_FLAG((*g).flags, GF_WQR);
1275 else if (row == 8 && col == 1)
1276 SET_FLAG((*g).flags, GF_BQR);
1277 else if (row == 1 && col == 8)
1278 SET_FLAG((*g).flags, GF_WKR);
1279 else if (row == 8 && col == 8)
1280 SET_FLAG((*g).flags, GF_BKR);
1283 kingsquare(*g, &kr, &kc, &okr, &okc);
1284 switch_turn(&(*g));
1286 if ((*g).castle) {
1287 p = m + strlen(m);
1288 (*g).castle = 0;
1291 CLEAR_FLAG((*g).flags, GF_GAMEOVER);
1292 i = validate_move;
1293 validate_move = 1;
1295 if (!plyincr)
1296 (*g).ply++;
1297 else
1298 (*g).ply = 0;
1300 if (drawtest((*g).b)) {
1301 (*g).tag[TAG_RESULT].value = Realloc((*g).tag[TAG_RESULT].value, 8);
1302 strncpy((*g).tag[TAG_RESULT].value, "1/2-1/2", 8);
1303 update_status_notify(*g, "%s", NOTIFY_GAMEOVER_DRAW);
1305 if (curses_initialized)
1306 update_tag_window((*g).tag);
1308 SET_FLAG((*g).flags, GF_GAMEOVER);
1310 else {
1311 switch (checktest(g, kr, kc, okr, okc, 0)) {
1312 case 0:
1313 break;
1314 case -1:
1315 validate_move = i;
1316 switch_turn(&(*g));
1317 return 1;
1318 default:
1319 if (checkmatetest(g, kr, kc, okr, okc)) {
1320 *p++ = '#';
1322 if (result == WHITEWINS) {
1323 (*g).tag[TAG_RESULT].value = Realloc((*g).tag[TAG_RESULT].value, 4);
1324 strncpy((*g).tag[TAG_RESULT].value, "1-0", 4);
1325 update_status_notify(*g, "%s", NOTIFY_GAMEOVER_WWINS);
1327 else if (result == BLACKWINS) {
1328 (*g).tag[TAG_RESULT].value = Realloc((*g).tag[TAG_RESULT].value, 4);
1329 strncpy((*g).tag[TAG_RESULT].value, "0-1", 4);
1330 update_status_notify(*g, "%s", NOTIFY_GAMEOVER_BWINS);
1333 if (curses_initialized)
1334 update_tag_window((*g).tag);
1336 SET_FLAG((*g).flags, GF_GAMEOVER);
1338 else {
1339 *p++ = '+';
1341 if (((*g).turn == WHITE && (*g).side == WHITE) ||
1342 ((*g).turn == BLACK && (*g).side == BLACK))
1343 update_status_notify(*g, "%s", NOTIFY_CHECK);
1346 *p = '\0';
1347 break;
1351 switch_turn(&(*g));
1352 validate_move = i;
1353 return 0;