Fix for two human players that actually works.
[cboard.git] / src / move.c
blobc5edad703ae82c772401e92498cb5e8d1225e1a2
1 /* vim:tw=78:ts=8:sw=4:set ft=c: */
2 /*
3 Copyright (C) 2002-2006 Ben Kibbey <bjk@arbornet.org>
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(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 (status.turn == WHITE) ? toupper(p) : p;
64 int piece_side(int c)
66 if (c == int_to_piece(OPEN_SQUARE))
67 return -1;
69 if (c < 'A')
70 c = int_to_piece(c);
72 return (isupper(c)) ? WHITE : BLACK;
75 int val_piece_side(int c)
77 if ((isupper(c) && status.turn == WHITE) ||
78 (islower(c) && status.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(BOARD b, int piece, int row, int col,
120 int *r, int *c)
122 int i;
123 int count = 0;
125 for (i = col - 1; VALIDFILE(i); i--) {
126 int n = b[ROWTOBOARD(row)][COLTOBOARD(i)].icon;
128 if (piece_to_int(n) != OPEN_SQUARE) {
129 if (piece_to_int(n) == piece && val_piece_side(n)) {
130 *c = i;
131 *r = row;
132 count++;
135 break;
139 for (i = col + 1; VALIDFILE(i); i++) {
140 int n = b[ROWTOBOARD(row)][COLTOBOARD(i)].icon;
142 if (piece_to_int(n) != OPEN_SQUARE) {
143 if (piece_to_int(n) == piece && val_piece_side(n)) {
144 *c = i;
145 *r = row;
146 count++;
149 break;
153 return count;
156 int piece_by_row(BOARD b, int piece, int row, int col,
157 int *r, int *c)
159 int i;
160 int count = 0;
162 for (i = row + 1; VALIDFILE(i); i++) {
163 int n = b[ROWTOBOARD(i)][COLTOBOARD(col)].icon;
165 if (piece_to_int(n) != OPEN_SQUARE) {
166 if (piece_to_int(n) == piece && val_piece_side(n)) {
167 *r = i;
168 *c = col;
169 count++;
172 break;
176 for (i = row - 1; VALIDFILE(i); i--) {
177 int n = b[ROWTOBOARD(i)][COLTOBOARD(col)].icon;
179 if (piece_to_int(n) != OPEN_SQUARE) {
180 if (piece_to_int(n) == piece && val_piece_side(n)) {
181 *r = i;
182 *c = col;
183 count++;
186 break;
190 return count;
193 int piece_by_xy(BOARD b, int piece, int row, int col,
194 int *srow, int *scol)
196 int count = 0;
198 count = piece_by_row(b, piece, row, col, srow, scol);
199 count += piece_by_col(b, piece, row, col, srow, scol);
201 return (count != 1) ? 0 : 1;
204 int piece_test(BOARD b, int piece, int row, int col,
205 int *dstr, int *dstc)
207 int p;
209 if (!VALIDFILE(row) || !VALIDFILE(col))
210 return 2;
212 p = b[ROWTOBOARD(row)][COLTOBOARD(col)].icon;
214 if (piece_to_int(p) != OPEN_SQUARE) {
215 if (piece_to_int(p) == piece && val_piece_side(p)) {
216 *dstr = row;
217 *dstc = col;
218 return 1;
221 return 2;
224 return 0;
227 int piece_by_diag(BOARD b, int piece, int row, int col,
228 int *srow, int *scol)
230 int i, n;
231 int ul = 1, ur = 1, dl = 1, dr = 1;
232 int count = 0;
234 for (i = 1; VALIDFILE(i); i++) {
235 if (dr) {
236 n = piece_test(b, piece, row - i, col + i, srow, scol);
238 if (n == 1 && count++)
239 return 1;
240 else if (n == 2)
241 dr = 0;
244 if (dl) {
245 n = piece_test(b, piece, row - i, col - i, srow, scol);
247 if (n == 1 && count++)
248 return 1;
249 else if (n == 2)
250 dl = 0;
253 if (ur) {
254 n = piece_test(b, piece, row + i, col + i, srow, scol);
256 if (n == 1 && count++)
257 return 1;
258 else if (n == 2)
259 ur = 0;
262 if (ul) {
263 n = piece_test(b, piece, row + i, col - i, srow, scol);
265 if (n == 1 && count++)
266 return 1;
267 else if (n == 2)
268 ul = 0;
272 return (count) ? 1 : 0;
275 int valid_move(BOARD b, int row, int col, int srow, int scol)
277 int p1, p2;
279 if (!VALIDFILE(srow) || !VALIDFILE(scol))
280 return 0;
282 if (row == srow && col == scol)
283 return 0;
285 p1 = b[ROWTOBOARD(srow)][COLTOBOARD(scol)].icon;
286 p2 = b[ROWTOBOARD(row)][COLTOBOARD(col)].icon;
288 if (piece_to_int(p1) == OPEN_SQUARE)
289 return 0;
291 if (piece_side(p1) == piece_side(p2))
292 return 0;
294 if (piece_to_int(p1) == PAWN && scol == col &&
295 piece_to_int(p2) != OPEN_SQUARE)
296 return 0;
298 return 1;
301 int castle_move(BOARD b, int which)
303 int row;
304 int n;
305 int p, p2, p3, p4;
307 row = (status.turn == WHITE) ? 1 : 8;
308 n = COLTOINT('e');
310 if (which == KINGSIDE) {
311 if ((status.turn == WHITE && (TEST_FLAG(game[gindex].flags, GF_WK) ||
312 TEST_FLAG(game[gindex].flags, GF_WKR))) ||
313 (status.turn == BLACK && (TEST_FLAG(game[gindex].flags, GF_BK)
314 || TEST_FLAG(game[gindex].flags, GF_BKR))))
315 return 1;
317 p = b[ROWTOBOARD(row)][COLTOBOARD((n + 1))].icon;
318 p2 = b[ROWTOBOARD(row)][COLTOBOARD((n + 2))].icon;
319 p3 = b[ROWTOBOARD(row)][COLTOBOARD((n + 3))].icon;
321 if (piece_to_int(p) != OPEN_SQUARE || piece_to_int(p2) != OPEN_SQUARE
322 || (piece_to_int(p3) != ROOK && val_piece_side(p3)))
323 return 1;
325 if (!validate_move) {
326 b[ROWTOBOARD(row)][COLTOBOARD(COLTOINT('e'))].icon =
327 int_to_piece(OPEN_SQUARE);
328 b[ROWTOBOARD(row)][COLTOBOARD(6)].icon = int_to_piece(ROOK);
329 b[ROWTOBOARD(row)][COLTOBOARD(7)].icon = int_to_piece(KING);
330 b[ROWTOBOARD(row)][COLTOBOARD(8)].icon = int_to_piece(OPEN_SQUARE);
332 if (status.turn == WHITE) {
333 SET_FLAG(game[gindex].flags, GF_WK);
334 SET_FLAG(game[gindex].flags, GF_WKR);
335 update_status_notify("%s", NOTIFY_WCASTLEK);
337 else if (status.turn == BLACK) {
338 SET_FLAG(game[gindex].flags, GF_BK);
339 SET_FLAG(game[gindex].flags, GF_BKR);
340 update_status_notify("%s", NOTIFY_BCASTLEK);
344 else {
345 if ((status.turn == WHITE && (TEST_FLAG(game[gindex].flags, GF_WK) ||
346 TEST_FLAG(game[gindex].flags, GF_WQR))) ||
347 (status.turn == BLACK && (TEST_FLAG(game[gindex].flags, GF_BK)
348 || TEST_FLAG(game[gindex].flags, GF_BQR))))
349 return 1;
351 p = b[ROWTOBOARD(row)][COLTOBOARD((n - 1))].icon;
352 p2 = b[ROWTOBOARD(row)][COLTOBOARD((n - 2))].icon;
353 p3 = b[ROWTOBOARD(row)][COLTOBOARD((n - 3))].icon;
354 p4 = b[ROWTOBOARD(row)][COLTOBOARD((n - 4))].icon;
356 if (piece_to_int(p) != OPEN_SQUARE || piece_to_int(p2) != OPEN_SQUARE
357 || piece_to_int(p3) != OPEN_SQUARE ||
358 (piece_to_int(p4) != ROOK && val_piece_side(p4)))
359 return 1;
361 if (!validate_move) {
362 b[ROWTOBOARD(row)][COLTOBOARD(1)].icon = int_to_piece(OPEN_SQUARE);
363 b[ROWTOBOARD(row)][COLTOBOARD(COLTOINT('e'))].icon =
364 int_to_piece(OPEN_SQUARE);
365 b[ROWTOBOARD(row)][COLTOBOARD(3)].icon = int_to_piece(KING);
366 b[ROWTOBOARD(row)][COLTOBOARD(4)].icon = int_to_piece(ROOK);
368 if (status.turn == WHITE) {
369 SET_FLAG(game[gindex].flags, GF_WK);
370 SET_FLAG(game[gindex].flags, GF_WQR);
371 update_status_notify("%s", NOTIFY_WCASTLEQ);
373 else if (status.turn == BLACK) {
374 SET_FLAG(game[gindex].flags, GF_BK);
375 SET_FLAG(game[gindex].flags, GF_BQR);
376 update_status_notify("%s", NOTIFY_BCASTLEQ);
381 return 0;
384 int get_source_yx(BOARD b, int piece, int row, int col, int *srow, int *scol)
386 int p = 0;
387 int count = 0;
388 int r, c;
389 int i;
390 int dist = 0;
392 /* FIXME valid move ambiguities. */
393 switch (piece) {
394 case PAWN:
395 if (*srow == 0 && *scol == col) {
396 i = (status.turn == WHITE) ? -1 : 1;
398 /* Find the first pawn in the current column. */
399 for (r = row + i, dist = 0; VALIDFILE(r); r += i, dist++) {
400 int n = b[ROWTOBOARD(r)][COLTOBOARD(col)].icon;
402 p = piece_to_int(n);
404 if (p == PAWN && val_piece_side(n))
405 break;
408 if (p != PAWN || dist > 2)
409 return 1;
411 *srow = r;
412 dist = abs(*srow - row);
414 if (status.turn == WHITE) {
415 if ((*srow == 2 && dist > 2) || (*srow > 2 && dist > 1))
416 return 1;
418 else {
419 if ((*srow == 7 && dist > 2) || (*srow < 7 && dist > 1))
420 return 1;
423 if (dist == 2) {
424 p = piece_to_int(b[ROWTOBOARD(*srow + i)][COLTOBOARD(col)].icon);
425 if (p != OPEN_SQUARE)
426 return 1;
429 else if (*scol != col) {
430 if (abs(*scol - col) != 1)
431 return 1;
433 *srow = (status.turn == WHITE) ? row - 1 : row + 1;
435 if (piece_to_int(b[ROWTOBOARD(*srow)][COLTOBOARD(*scol)].icon)
436 != PAWN)
437 return 1;
439 piece = piece_to_int(b[ROWTOBOARD(row)][COLTOBOARD(col)].icon);
441 /* En Passant. */
442 if (piece == OPEN_SQUARE) {
443 /* Previous move was not 2 squares and a pawn. */
444 if (!TEST_FLAG(game[gindex].flags, GF_ENPASSANT))
445 return 1;
447 r = (status.turn == WHITE) ? 6 : 3;
449 if (row != r)
450 return 1;
452 r = (status.turn == WHITE) ? row - 1 : row + 1;
453 piece = b[ROWTOBOARD(r)][COLTOBOARD(col)].icon;
455 if (piece_to_int(piece) != PAWN)
456 return 1;
458 if (!validate_move) {
459 b[ROWTOBOARD(r)][COLTOBOARD(col)].icon =
460 int_to_piece(OPEN_SQUARE);
462 if ((status.turn == WHITE && status.side != WHITE) ||
463 (status.turn == BLACK && status.side != BLACK))
464 update_status_notify("%s", NOTIFY_ENPASSANT);
468 break;
469 case ROOK:
470 if (piece_by_xy(b, ROOK, row, col, srow, scol) == 0)
471 return 1;
473 if (!validate_move && *scol == 1) {
474 if (status.turn == WHITE)
475 SET_FLAG(game[gindex].flags, GF_WQR);
476 else
477 SET_FLAG(game[gindex].flags, GF_BQR);
479 else if (!validate_move && *scol == 8) {
480 if (status.turn == WHITE)
481 SET_FLAG(game[gindex].flags, GF_WKR);
482 else
483 SET_FLAG(game[gindex].flags, GF_BKR);
485 break;
486 case KNIGHT:
487 r = row - 2;
488 c = col - 1;
489 p = b[ROWTOBOARD(r)][COLTOBOARD(c)].icon;
491 if (VALIDFILE(r) && VALIDFILE(c)) {
492 if (piece_to_int(p) == KNIGHT && val_piece_side(p)) {
493 *srow = r;
494 *scol = c;
495 count++;
497 if ((*srow && *srow == row) || (*scol && *scol == col))
498 break;
502 r = row - 2;
503 c = col + 1;
504 p = b[ROWTOBOARD(r)][COLTOBOARD(c)].icon;
506 if (VALIDFILE(r) && VALIDFILE(c)) {
508 if (piece_to_int(p) == KNIGHT && val_piece_side(p)) {
509 *srow = r;
510 *scol = c;
511 count++;
513 if ((*srow && *srow == row) || (*scol && *scol == col))
514 break;
518 r = row + 2;
519 c = col - 1;
520 p = b[ROWTOBOARD(r)][COLTOBOARD(c)].icon;
522 if (VALIDFILE(r) && VALIDFILE(c)) {
523 if (piece_to_int(p) == KNIGHT && val_piece_side(p)) {
524 *srow = r;
525 *scol = c;
526 count++;
528 if ((*srow && *srow == row) || (*scol && *scol == col))
529 break;
533 r = row + 2;
534 c = col + 1;
535 p = b[ROWTOBOARD(r)][COLTOBOARD(c)].icon;
537 if (VALIDFILE(r) && VALIDFILE(c)) {
538 if (piece_to_int(p) == KNIGHT && val_piece_side(p)) {
539 *srow = r;
540 *scol = c;
541 count++;
543 if ((*srow && *srow == row) || (*scol && *scol == col))
544 break;
548 r = row - 1;
549 c = col - 2;
550 p = b[ROWTOBOARD(r)][COLTOBOARD(c)].icon;
552 if (VALIDFILE(r) && VALIDFILE(c)) {
553 if (piece_to_int(p) == KNIGHT && val_piece_side(p)) {
554 *srow = r;
555 *scol = c;
556 count++;
558 if ((*srow && *srow == row) || (*scol && *scol == col))
559 break;
563 r = row - 1;
564 c = col + 2;
565 p = b[ROWTOBOARD(r)][COLTOBOARD(c)].icon;
567 if (VALIDFILE(r) && VALIDFILE(c)) {
568 if (piece_to_int(p) == KNIGHT && val_piece_side(p)) {
569 *srow = r;
570 *scol = c;
571 count++;
573 if ((*srow && *srow == row) || (*scol && *scol == col))
574 break;
578 r = row + 1;
579 c = col + 2;
580 p = b[ROWTOBOARD(r)][COLTOBOARD(c)].icon;
582 if (VALIDFILE(r) && VALIDFILE(c)) {
583 if (piece_to_int(p) == KNIGHT && val_piece_side(p)) {
584 *srow = r;
585 *scol = c;
586 count++;
588 if ((*srow && *srow == row) || (*scol && *scol == col))
589 break;
593 r = row + 1;
594 c = col - 2;
595 p = b[ROWTOBOARD(r)][COLTOBOARD(c)].icon;
597 if (VALIDFILE(r) && VALIDFILE(c)) {
598 if (piece_to_int(p) == KNIGHT && val_piece_side(p)) {
599 *srow = r;
600 *scol = c;
601 count++;
603 if ((*srow && *srow == row) || (*scol && *scol == col))
604 break;
608 if ((count != 1 && !validate_move) || (validate_move && !count))
609 return 1;
611 break;
612 case BISHOP:
613 if (piece_by_diag(b, BISHOP, row, col, srow, scol) == 0)
614 return 1;
615 break;
616 case QUEEN:
617 if (piece_by_xy(b, QUEEN, row, col, srow, scol) == 0) {
618 if (piece_by_diag(b, QUEEN, row, col, srow, scol) == 0)
619 return 1;
621 break;
622 case KING:
623 if (piece_by_xy(b, KING, row, col, srow, scol) == 0) {
624 if (piece_by_diag(b, KING, row, col, srow, scol) == 0)
625 return 1;
628 if (abs(*srow - row) > 1)
629 return 1;
631 dist = abs(*scol - col);
633 if (*scol == COLTOINT('e')) {
634 if (dist > 2)
635 return 1;
637 if (validate_move) {
638 if (dist == 2) {
639 if (col == 3) {
640 if (castle_move(b, QUEENSIDE))
641 return 1;
643 else if (col == 7) {
644 if (castle_move(b, KINGSIDE))
645 return 1;
647 else
648 return 1;
650 break;
653 if (dist > 1)
654 return 1;
656 break;
660 if (dist > 1)
661 return 1;
663 break;
664 default:
665 return 1;
668 if (valid_move(b, row, col, *srow, *scol) == 0)
669 return 1;
671 if (piece == KING) {
672 if (!validate_move) {
673 if (status.turn == WHITE)
674 SET_FLAG(game[gindex].flags, GF_WK);
675 else
676 SET_FLAG(game[gindex].flags, GF_BK);
680 return 0;
683 /* This function converts a2a4 formatted moves to SAN format. Minimal checks
684 * are performed here. The real checks are in parse_move_text() after the
685 * conversion.
687 char *a2a4tosan(BOARD b, char *move)
689 static char buf[MAX_PGN_MOVE_LEN + 1] = {0}, *cp = buf;
690 char *p = move;
691 int scol, srow, col, row;
692 int piece, piecei, spiece;
693 int trow, tcol;
694 int rowc, colc;
695 int promo = 0;
696 int tenpassant = 0;
697 int n;
699 if (!VALIDCOL(*p) || !VALIDROW(*(p + 1)) || !VALIDCOL(*(p + 2))
700 || !VALIDROW(*(p + 3)))
701 return move;
703 scol = COLTOINT(*p);
704 srow = ROWTOINT(*(p + 1));
705 col = COLTOINT(*(p + 2));
706 row = ROWTOINT(*(p + 3));
708 if (p[4]) {
709 if ((promo = piece_to_int(p[4])) == -1)
710 return NULL;
713 piece = b[ROWTOBOARD(srow)][COLTOBOARD(scol)].icon;
715 if ((piecei = piece_to_int(piece)) == -1 || piecei == OPEN_SQUARE)
716 return NULL;
718 spiece = piecei;
719 cp = buf;
720 colc = abs(scol - col);
722 if (srow == row && (row == 1 || row == 8) && scol == COLTOINT('e')
723 && colc > 1 && piecei == KING) {
724 if (scol - col < 0)
725 return "O-O";
726 else if (scol - col > 0)
727 return "O-O-O";
729 return NULL;
732 if (piecei != PAWN)
733 *cp++ = toupper(piece);
734 else {
735 /* En Passant. */
736 if (scol != col && piece_to_int(b[row][col].icon) == OPEN_SQUARE)
737 tenpassant = 1;
740 colc = piece_by_col(b, piecei, row, col, &trow, &tcol);
741 rowc = piece_by_row(b, piecei, row, col, &trow, &tcol);
742 n = colc + rowc;
744 if (piecei == KNIGHT) {
745 if (get_source_yx(b, KNIGHT, row, col, &trow, &tcol) == 1)
746 *cp++ = INTTOCOL(scol);
748 else if (n > 1 && piecei != PAWN) {
749 if (colc > 1 && rowc > 1) {
750 *cp++ = INTTOCOL(scol);
751 *cp++ = INTTOROW(srow);
753 else if (colc >= 1)
754 *cp++ = INTTOCOL(scol);
755 else if (rowc >= 1)
756 *cp++ = INTTOROW(srow);
759 piece = b[ROWTOBOARD(row)][COLTOBOARD(col)].icon;
761 if ((piecei = piece_to_int(piece)) != OPEN_SQUARE || tenpassant) {
762 if (tenpassant || spiece == PAWN)
763 *cp++ = INTTOCOL(scol);
765 *cp++ = 'x';
768 *cp++ = INTTOCOL(col);
769 *cp++ = INTTOROW(row);
771 if (promo) {
772 *cp++ = '=';
773 *cp++ = toupper(int_to_piece(promo));
776 *cp = '\0';
778 return buf;
781 void switch_turn()
783 if (status.turn == WHITE)
784 status.turn = BLACK;
785 else
786 status.turn = WHITE;
788 return;
791 static void kingsquare(BOARD b, int *kr, int *kc, int *okr,
792 int *okc)
794 int row, col;
796 for (row = 1; VALIDFILE(row); row++) {
797 for (col = 1; VALIDFILE(col); col++) {
798 int p = b[ROWTOBOARD(row)][COLTOBOARD(col)].icon;
800 if (piece_to_int(p) == KING) {
801 if (val_piece_side(p)) {
802 *kr = row;
803 *kc = col;
805 else {
806 /* Opponent. */
807 *okr = row;
808 *okc = col;
814 return;
817 int checktest(BOARD b, int kr, int kc, int okr, int okc, int matetest)
819 int row, col;
821 switch_turn();
823 /* See if the move would put our opponent in check. */
824 for (row = 1; VALIDFILE(row); row++) {
825 for (col = 1; VALIDFILE(col); col++) {
826 int srow = 0, scol = 0;
827 int p = b[ROWTOBOARD(row)][COLTOBOARD(col)].icon;
828 int pi = piece_to_int(p);
830 if (pi == OPEN_SQUARE)
831 continue;
833 if (pi == PAWN)
834 scol = col;
836 /* See if the move would leave ourselves in check. */
837 if (!matetest) {
838 switch_turn();
840 if (get_source_yx(b, pi, kr, kc, &srow, &scol) == 0)
841 return -1;
843 switch_turn();
846 if (get_source_yx(b, pi, okr, okc, &srow, &scol) == 0) {
847 switch_turn();
848 return 1;
853 switch_turn();
854 return 0;
857 void reset_valid_moves(BOARD b)
859 int row, col;
861 for (row = 0; row < 8; row++) {
862 for (col = 0; col < 8; col++) {
863 b[row][col].valid = 0;
864 b[row][col].movecount = 0;
868 return;
871 void get_valid_moves(BOARD b, int p, int srow, int scol, int *minr, int *maxr,
872 int *minc, int *maxc)
874 int row, col;
876 validate_move = 1;
877 *minr = *maxr = *minc = *maxc = 1;
879 for (row = 1; VALIDFILE(row); row++) {
880 for (col = 1; VALIDFILE(col); col++) {
881 int sr = 0, sc = 0;
883 if (get_source_yx(b, p, row, col, &sr, &sc)) {
884 sr = 0;
885 sc = scol;
887 if (get_source_yx(b, p, row, col, &sr, &sc)) {
888 sc = 0;
889 sr = srow;
891 if (get_source_yx(b, p, row, col, &sr, &sc)) {
892 continue;
897 if (sr != srow || sc != scol)
898 continue;
900 b[ROWTOBOARD(row)][COLTOBOARD(col)].valid = 1;
903 if (row < *minr)
904 *minr = row;
906 if (row > *maxr)
907 *maxr = row;
909 if (col < *minc)
910 *minc = col;
912 if (col > *maxc)
913 *maxc = col;
918 validate_move = 0;
919 return;
922 static int checkmate_pawn_test(BOARD b, int row, int col, int *srow, int *scol)
924 int r, c;
926 r = 0;
927 c = col;
929 if (!get_source_yx(b, PAWN, row, col, &r, &c))
930 return 0;
932 c = col - 1;
934 if (!get_source_yx(b, PAWN, row, col, &r, &c))
935 return 0;
937 c = col + 1;
939 if (!get_source_yx(b, PAWN, row, col, &r, &c))
940 return 0;
942 return 1;
945 static int checkmatetest(BOARD b, int kr, int kc, int okr, int okc)
947 int row, col;
948 int srow, scol;
949 int check;
951 /* For each square on the board see if each peace has a valid move, and if
952 * so, see if it would leave ourselves or the opponent in check.
954 for (row = 1; VALIDFILE(row); row++) {
955 for (col = 1; VALIDFILE(col); col++) {
956 int n;
958 for (n = 0; n < MAX_PIECES; n++) {
959 int p;
960 int nkr = kr, nkc = kc, nokr = okr, nokc = okc;
961 BOARD t;
963 srow = scol = 0;
965 if (n == PAWN) {
966 if (checkmate_pawn_test(b, row, col, &srow, &scol))
967 continue;
969 else {
970 if (get_source_yx(b, n, row, col, &srow, &scol))
971 continue;
974 /* Valid move. */
975 memcpy(t, b, sizeof(BOARD));
976 p = t[ROWTOBOARD(srow)][COLTOBOARD(scol)].icon;
977 t[ROWTOBOARD(row)][COLTOBOARD(col)].icon = p;
978 t[ROWTOBOARD(srow)][COLTOBOARD(scol)].icon =
979 int_to_piece(OPEN_SQUARE);
981 if (piece_to_int(p) == KING) {
982 if (piece_side(p) == status.turn) {
983 nokr = row;
984 nokc = col;
986 else {
987 nkr = row;
988 nkc = col;
992 check = checktest(t, nkr, nkc, nokr, nokc, 1);
994 if (check == 0)
995 goto done;
1000 check = 1;
1002 if (status.turn == WHITE)
1003 result = BLACKWINS;
1004 else
1005 result = WHITEWINS;
1007 done:
1008 return (check != 0) ? 1 : 0;
1011 /* FIXME */
1012 static int drawtest(BOARD b)
1014 int row, col;
1015 int other = 0;
1018 if (game[gindex].ply >= 50)
1019 return 1;
1022 for (row = 1; VALIDFILE(row); row++) {
1023 for (col = 1; VALIDFILE(col); col++) {
1024 int p = b[ROWTOBOARD(row)][COLTOBOARD(col)].icon;
1025 int piece = piece_to_int(p);
1027 switch (piece) {
1028 case PAWN:
1029 return 0;
1030 break;
1031 case KING:
1032 break;
1033 default:
1034 other++;
1035 break;
1040 if (!other)
1041 return 1;
1043 return 0;
1046 int parse_move_text(BOARD b, char *move)
1048 char *p;
1049 int piece, dstpiece;
1050 int i = 0;
1051 int srow = 0, scol = 0, row, col;
1052 int dist = 0;
1053 int promo = -1;
1054 int kr, kc, okr, okc;
1055 int plyincr = 0;
1057 if (strlen(move) < 2)
1058 return 1;
1060 capture = 0;
1061 update_status_notify(NULL);
1062 srow = row = col = scol = promo = piece = 0;
1063 p = (move) + strlen(move);
1065 while (!isdigit(*--p) && *p != 'O') {
1066 if (*p == '=') {
1067 promo = piece_to_int(i);
1068 break;
1071 i = *p;
1072 *p = '\0';
1075 if (strlen(move) < 2)
1076 return 1;
1078 p = move;
1080 /* Skip 'P'. */
1081 if (piece_to_int(*p) == PAWN)
1082 p++;
1084 /* Pawn. */
1085 if (VALIDCOL(*p)) {
1086 for (i = 0; *p; i++) {
1087 if (VALIDCOL(*p)) {
1088 if (i > 0)
1089 col = COLTOINT(*p++);
1090 else
1091 col = scol = COLTOINT(*p++);
1093 else if (VALIDROW(*p)) {
1094 if (1 > 1)
1095 row = ROWTOINT(*p++);
1096 else
1097 row = ROWTOINT(*p++);
1099 else if (*p == 'x') {
1100 col = COLTOINT(*++p);
1101 row = ROWTOINT(*++p);
1102 plyincr++;
1103 capture++;
1105 else if (*p == '=') {
1106 if (promo == -1 || promo == KING || promo == PAWN)
1107 return 1;
1109 *p++ = '=';
1110 *p++ = toupper(int_to_piece(promo));
1111 *p = '\0';
1112 break;
1114 else {
1115 #ifdef DEBUG
1116 if (debug)
1117 DUMP_F("Pawn (move: '%s'): %c\n", move, *p++);
1118 #else
1119 p++;
1120 #endif
1124 if (get_source_yx(b, PAWN, row, col, &srow, &scol))
1125 return 1;
1127 /* Not a pawn. */
1128 else {
1129 if (strcmp(move, "O-O") == 0)
1130 game[gindex].castle = KINGSIDE;
1131 else if (strcmp(move, "O-O-O") == 0)
1132 game[gindex].castle = QUEENSIDE;
1133 else {
1134 p = move;
1136 if ((piece = piece_to_int(*p++)) == -1)
1137 return 1;
1139 if (strlen(move) > 3) {
1140 if (isdigit(*p))
1141 srow = ROWTOINT(*p++);
1142 else if (VALIDCOL(*p))
1143 scol = COLTOINT(*p++);
1145 if (*p == 'x') {
1146 capture++;
1147 p++;
1151 col = COLTOINT(*p++);
1152 row = ROWTOINT(*p++);
1154 /* Get the source row and column. */
1155 if (srow == 0) {
1156 if (scol > 0) {
1157 for (i = 1; VALIDFILE(i); i++) {
1158 int fpiece = b[ROWTOBOARD(i)][COLTOBOARD(scol)].icon;
1160 if (piece == piece_to_int(fpiece) &&
1161 val_piece_side(fpiece)) {
1162 srow = i;
1163 break;
1167 if (srow == 0)
1168 return 1;
1170 else {
1171 if (get_source_yx(b, piece, row, col, &srow, &scol))
1172 return 1;
1175 else if (scol == 0) {
1176 if (srow > 0) {
1177 for (i = 1; VALIDFILE(i); i++) {
1178 int fpiece = piece_to_int(b[ROWTOBOARD(srow)][COLTOBOARD(i)].icon);
1180 if (piece == fpiece) {
1181 scol = i;
1182 break;
1186 if (scol == 0)
1187 return 1;
1189 else {
1190 if (get_source_yx(b, piece, row, col, &srow, &scol))
1191 return 1;
1197 piece = piece_to_int(b[ROWTOBOARD(srow)][COLTOBOARD(scol)].icon);
1198 dist = abs(srow - row);
1200 if (!validate_move) {
1201 if (piece == PAWN && dist == 2)
1202 SET_FLAG(game[gindex].flags, GF_ENPASSANT);
1203 else
1204 CLEAR_FLAG(game[gindex].flags, GF_ENPASSANT);
1207 if (piece == PAWN)
1208 plyincr++;
1210 dstpiece = piece = b[ROWTOBOARD(row)][COLTOBOARD(col)].icon;
1212 if (game[gindex].castle) {
1213 if (castle_move(b, game[gindex].castle)) {
1214 game[gindex].castle = 0;
1215 return 1;
1218 goto done;
1221 if (piece_to_int(piece) != OPEN_SQUARE) {
1222 if (val_piece_side(piece))
1223 return 2;
1225 if (!validate_move) {
1226 if (piece_side(piece) == WHITE)
1227 game[gindex].bcaptures++;
1228 else
1229 game[gindex].wcaptures++;
1231 update_status_notify("%s", random_agony());
1235 if (!validate_move) {
1236 if (promo) {
1237 piece = int_to_piece(promo);
1239 if ((status.turn == WHITE && status.side != WHITE) ||
1240 (status.turn == BLACK && status.side != BLACK))
1241 update_status_notify("%s", NOTIFY_PROMOTION);
1243 else
1244 piece = b[ROWTOBOARD(srow)][COLTOBOARD(scol)].icon;
1246 else
1247 piece = b[ROWTOBOARD(srow)][COLTOBOARD(scol)].icon;
1249 b[ROWTOBOARD(srow)][COLTOBOARD(scol)].icon = int_to_piece(OPEN_SQUARE);
1250 b[ROWTOBOARD(row)][COLTOBOARD(col)].icon = piece;
1252 done:
1253 if (!validate_move && capture && piece_to_int(dstpiece) == ROOK) {
1254 if (row == 1 && col == 1)
1255 SET_FLAG(game[gindex].flags, GF_WQR);
1256 else if (row == 8 && col == 1)
1257 SET_FLAG(game[gindex].flags, GF_BQR);
1258 else if (row == 1 && col == 8)
1259 SET_FLAG(game[gindex].flags, GF_WKR);
1260 else if (row == 8 && col == 8)
1261 SET_FLAG(game[gindex].flags, GF_BKR);
1264 kingsquare(b, &kr, &kc, &okr, &okc);
1265 switch_turn();
1267 if (game[gindex].castle) {
1268 p = move + strlen(move);
1269 game[gindex].castle = 0;
1272 CLEAR_FLAG(game[gindex].flags, GF_GAMEOVER);
1273 i = validate_move;
1274 validate_move = 1;
1276 if (!plyincr)
1277 game[gindex].ply++;
1278 else
1279 game[gindex].ply = 0;
1281 if (drawtest(b)) {
1282 game[gindex].tag[TAG_RESULT].value =
1283 Realloc(game[gindex].tag[TAG_RESULT].value, 8);
1284 strncpy(game[gindex].tag[TAG_RESULT].value, "1/2-1/2", 8);
1285 update_status_notify("%s", NOTIFY_GAMEOVER_DRAW);
1287 if (curses_initialized)
1288 update_tag_window();
1290 SET_FLAG(game[gindex].flags, GF_GAMEOVER);
1292 else {
1293 switch (checktest(b, kr, kc, okr, okc, 0)) {
1294 case 0:
1295 break;
1296 case -1:
1297 validate_move = i;
1298 switch_turn();
1299 return 1;
1300 default:
1301 if (checkmatetest(b, kr, kc, okr, okc)) {
1302 *p++ = '#';
1304 if (result == WHITEWINS) {
1305 game[gindex].tag[TAG_RESULT].value =
1306 Realloc(game[gindex].tag[TAG_RESULT].value, 4);
1307 strncpy(game[gindex].tag[TAG_RESULT].value, "1-0", 4);
1308 update_status_notify("%s", NOTIFY_GAMEOVER_WWINS);
1310 else if (result == BLACKWINS) {
1311 game[gindex].tag[TAG_RESULT].value =
1312 Realloc(game[gindex].tag[TAG_RESULT].value, 4);
1313 strncpy(game[gindex].tag[TAG_RESULT].value, "0-1", 4);
1314 update_status_notify("%s", NOTIFY_GAMEOVER_BWINS);
1317 if (curses_initialized)
1318 update_tag_window();
1320 SET_FLAG(game[gindex].flags, GF_GAMEOVER);
1322 else {
1323 *p++ = '+';
1325 if ((status.turn == WHITE && status.side == WHITE) ||
1326 (status.turn == BLACK && status.side == BLACK))
1327 update_status_notify("%s", NOTIFY_CHECK);
1330 *p = '\0';
1331 break;
1335 switch_turn();
1336 validate_move = i;
1337 return 0;