2 Copyright (c) 1993 Richard V. Nash.
3 Copyright (c) 2000 Dan Papasian
4 Copyright (C) Andrew Tridgell 2002
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 // [HGM] some explanation of the parser code:
24 // The routine parse_move() calls is_move() to recognize some simple cases,
25 // like OO-castle and long algebraic with or without dash. What does not fall
26 // in this class is handed to alg_is_move(), whch is really a continuation
27 // of is_move(), to recognize SAN.
28 // Depending on the type of move syntax, parse_move can extract from- and to-square
29 // immediately, or transate the OO-castling to internal from-to representation.
30 // Only for SAN syntax the routine alg_pars_move() is called to extract the
31 // given elements, (through get_move_info(), which is the same as alg_is_move(),
32 // xcept that it does not discard the value of the elements), and disambiguate
33 // the move (i.e. determines the from-square) by looking for a piece of the given
34 // type on the board for which the move is pseudo-legal (using legal_move() ).
37 /* Simply tests if the input string is a move or not. */
38 /* If it matches patterns below */
39 /* Add to this list as you improve the move parser */
41 /* MS_COMPDASH e2-e4 */
42 /* MS_CASTLE o-o, o-o-o */
44 /* MS_ALG e4, Nd5 Ncd5 */
45 int is_move(const char *mstr)
47 int len = strlen(mstr);
48 /* remove the 'mates' marker */
49 if (mstr[len - 1] == '#')
52 if ((len > 3) && (mstr[len - 2] == '=' || mstr[len - 2] == '/'))
55 if (len == 4) { /* Test for e2e4 */
56 if (isfile(mstr[0]) && isrank(mstr[1]) &&
57 isfile(mstr[2]) && isrank(mstr[3])) {
61 if (len == 5) { /* Test for e2-e4 */
62 if (isfile(mstr[0]) && isrank(mstr[1]) &&
64 isfile(mstr[3]) && isrank(mstr[4])) {
68 if (len == 3) { /* Test for o-o */
69 if ((mstr[0] == 'o') && (mstr[1] == '-') && (mstr[2] == 'o')) {
72 if ((mstr[0] == 'O') && (mstr[1] == '-') && (mstr[2] == 'O')) {
75 if ((mstr[0] == '0') && (mstr[1] == '-') && (mstr[2] == '0')) {
79 if (len == 2) { /* Test for oo */
80 if ((mstr[0] == 'o') && (mstr[1] == 'o')) {
83 if ((mstr[0] == 'O') && (mstr[1] == 'O')) {
86 if ((mstr[0] == '0') && (mstr[1] == '0')) {
90 if (len == 5) { /* Test for o-o-o */
91 if ((mstr[0] == 'o') && (mstr[1] == '-') && (mstr[2] == 'o') && (mstr[3] == '-') && (mstr[4] == 'o')) {
94 if ((mstr[0] == 'O') && (mstr[1] == '-') && (mstr[2] == 'O') && (mstr[3] == '-') && (mstr[4] == 'O')) {
97 if ((mstr[0] == '0') && (mstr[1] == '-') && (mstr[2] == '0') && (mstr[3] == '-') && (mstr[4] == '0')) {
101 if (len == 3) { /* Test for ooo */
102 if ((mstr[0] == 'o') && (mstr[1] == 'o') && (mstr[2] == 'o')) {
105 if ((mstr[0] == 'O') && (mstr[1] == 'O') && (mstr[2] == 'O')) {
108 if ((mstr[0] == '0') && (mstr[1] == '0') && (mstr[2] == '0')) {
112 return alg_is_move(mstr);
116 int NextPieceLoop(board_t b, int *f, int *r, int color, int w, int h)
126 if ((b[*f][*r] != NOPIECE) && iscolor(b[*f][*r], color))
132 int InitPieceLoop(board_t b, int *f, int *r, int color)
139 /* All of the routines assume that the obvious problems have been checked */
140 /* See legal_move() */
141 static int legal_pawn_move( struct game_state_t *gs, int ff, int fr, int tf, int tr )
144 if (gs->board[tf][tr] != NOPIECE && !gs->palace && gs->promoType != 3) return 0; // [HGM] XQ and Shogi pawns can capture straight ahead
145 if (gs->onMove == WHITE) {
146 if (tr - fr == 1) return 1;
147 if ((fr <= gs->pawnDblStep) && (tr - fr == 2) && gs->board[ff][fr+1]==NOPIECE) return 1;
149 if (fr - tr == 1) return 1;
150 if ((fr >= gs->ranks - 1 - gs->pawnDblStep) && (fr - tr == 2) && gs->board[ff][fr-1]==NOPIECE) return 1;
154 if (ff != tf && gs->promoType != 3) { /* Capture ? ([HGM] but not in Shogi) */
155 if ((ff - tf != 1) && (tf - ff != 1)) return 0;
156 if (gs->onMove == WHITE) {
157 if(gs->palace) return (fr >= gs->ranks/2 && fr == tr); // [HGM] XQ promoted pawns
158 if (tr != fr+1) return 0;
159 if ((gs->board[tf][tr] != NOPIECE) && iscolor(gs->board[tf][tr],BLACK))
161 if (gs->ep_possible[0][ff] == 1) {
162 if ((tf==ff+1) && (gs->board[ff+1][fr] == B_PAWN)) return 1;
163 } else if (gs->ep_possible[0][ff] == -1) {
164 if ((tf==ff-1) && (gs->board[ff-1][fr] == B_PAWN)) return 1;
167 if(gs->palace) return (fr < gs->ranks/2 && fr == tr); // [HGM] XQ promoted pawns
168 if (tr != fr-1) return 0;
169 if ((gs->board[tf][tr] != NOPIECE) && iscolor(gs->board[tf][tr],WHITE))
171 if (gs->ep_possible[1][ff] == 1) {
172 if ((tf==ff+1) && (gs->board[ff+1][fr] == W_PAWN)) return 1;
173 } else if (gs->ep_possible[1][ff] == -1) {
174 if ((tf==ff-1) && (gs->board[ff-1][fr] == W_PAWN)) return 1;
181 static int legal_hoplite_move( struct game_state_t *gs, int ff, int fr, int tf, int tr )
183 if (ff == tf) { /* Capture ? */
184 if (gs->board[tf][tr] == NOPIECE) return 0;
185 if (gs->onMove == WHITE) {
186 if(iscolor(gs->board[tf][tr],WHITE)) return 0;
187 if (tr - fr == 1) return 1;
189 if(iscolor(gs->board[tf][tr],BLACK)) return 0;
190 if (fr - tr == 1) return 1;
195 if (gs->board[tf][tr] != NOPIECE) return 0;
196 if (gs->onMove == WHITE) {
197 if (tr == fr+1 && abs(tf-ff) == 1) return 1;
198 if (tr != fr+2 || abs(tf-ff) != 2) return 0;
199 if (fr > gs->pawnDblStep) return 0;
202 if (tr == fr-1 && abs(tf-ff) == 1) return 1;
203 if (tr != fr-2 || abs(tf-ff) != 2) return 0;
204 if (fr < gs->ranks - 1 - gs->pawnDblStep) return 0;
211 static int legal_knight_move(struct game_state_t * gs, int ff, int fr, int tf, int tr)
228 static int legal_horse_move(struct game_state_t * gs, int ff, int fr, int tf, int tr)
235 if (abs(dy) == 1 && gs->board[(ff+tf)/2][fr] == NOPIECE)
239 if (abs(dx) == 1 && gs->board[ff][(fr+tr)/2] == NOPIECE)
245 static int legal_honorablehorse_move(struct game_state_t * gs, int ff, int fr, int tf, int tr)
251 if (dy == (gs->onMove == WHITE ? -2 : 2)) {
258 static int legal_bishop_move(struct game_state_t * gs, int ff, int fr, int tf, int tr)
282 return 0; /* Not diagonal */
284 return 1; /* One square, ok */
286 for (x = startx, y = starty; count; x += incx, y += incy, count--) {
287 if (gs->board[x][y] != NOPIECE)
293 static int legal_rook_move(struct game_state_t * gs, int ff, int fr, int tf, int tr)
299 if (abs(fr - tr) == 1)
308 for (i = start; i <= stop; i++) {
309 if (gs->board[ff][i] != NOPIECE)
313 } else if (fr == tr) {
314 if (abs(ff - tf) == 1)
323 for (i = start; i <= stop; i++) {
324 if (gs->board[i][fr] != NOPIECE)
333 static int legal_cannon_move(struct game_state_t * gs, int ff, int fr, int tf, int tr)
346 for (i = start; i <= stop; i++) {
347 if (gs->board[ff][i] != NOPIECE) cnt++;
349 return (cnt == 0 && gs->board[tf][tr] == NOPIECE) ||
350 (cnt == 1 && gs->board[tf][tr] != NOPIECE);
351 } else if (fr == tr) {
359 for (i = start; i <= stop; i++) {
360 if (gs->board[i][fr] != NOPIECE) cnt++;
362 return (cnt == 0 && gs->board[tf][tr] == NOPIECE) ||
363 (cnt == 1 && gs->board[tf][tr] != NOPIECE);
369 static int legal_lance_move(struct game_state_t * gs, int ff, int fr, int tf, int tr)
375 if (abs(fr - tr) == 1)
378 if(gs->onMove != WHITE) return 0;
382 if(gs->onMove == WHITE) return 0;
386 for (i = start; i <= stop; i++) {
387 if (gs->board[ff][i] != NOPIECE)
394 static int legal_queen_move(struct game_state_t * gs, int ff, int fr, int tf, int tr)
396 return legal_rook_move(gs, ff, fr, tf, tr) || legal_bishop_move(gs, ff, fr, tf, tr);
399 static int legal_cardinal_move(struct game_state_t * gs, int ff, int fr, int tf, int tr)
401 return legal_knight_move(gs, ff, fr, tf, tr) || legal_bishop_move(gs, ff, fr, tf, tr);
404 static int legal_marshall_move(struct game_state_t * gs, int ff, int fr, int tf, int tr)
406 return legal_rook_move(gs, ff, fr, tf, tr) || legal_knight_move(gs, ff, fr, tf, tr);
409 /* Ckeck, if square (kf,kr) is attacked by enemy piece.
410 * Used in castling from/through check testing.
413 /* new one from soso: */
414 static int is_square_attacked (struct game_state_t *gs, int kf, int kr)
416 struct game_state_t fakeMove;
417 int oldk = gs->onMove == WHITE ? gs->wkmoved : gs->bkmoved;
420 fakeMove.board[oldk][kr] = NOPIECE; // [HGM] castle: this routine is called only when King has not moved
421 fakeMove.board[kf][kr] = KING | fakeMove.onMove;
422 fakeMove.onMove = CToggle (fakeMove.onMove);
423 if (in_check(&fakeMove)) return 1;
428 static int is_square_attacked(struct game_state_t * gs, int kf, int kr)
431 gs->onMove = CToggle(gs->onMove);
433 for (InitPieceLoop(gs->board, &f, &r, gs->onMove);
434 NextPieceLoop(gs->board, &f, &r, gs->onMove, gs->files, gs->ranks);) {
435 if (legal_move(gs, f, r, kf, kr)) {
436 gs->onMove = CToggle(gs->onMove);
440 gs->onMove = CToggle(gs->onMove);
445 static int legal_man_move(struct game_state_t * gs, int ff, int fr, int tf, int tr)
447 if (abs(ff - tf) > 1)
449 if (abs(fr - tr) > 1)
454 static int legal_wazir_move(struct game_state_t * gs, int ff, int fr, int tf, int tr)
456 if(gs->palace && (tr > gs->palace && tr < gs->ranks - gs->palace ||
457 tf < (gs->files - gs->palace)/2 || tf >= (gs->files + gs->palace)/2))
459 if (abs(ff - tf) == 1 && fr == tr)
461 if (abs(fr - tr) == 1 && ff == tf)
466 static int legal_dababba_move(struct game_state_t * gs, int ff, int fr, int tf, int tr)
468 if (abs(ff - tf) == 2 && fr == tr)
470 if (abs(fr - tr) == 2 && ff == tf)
475 static int legal_ferz_move(struct game_state_t * gs, int ff, int fr, int tf, int tr)
477 if (abs(ff - tf) != 1)
479 if (abs(fr - tr) != 1)
484 static int legal_mandarin_move(struct game_state_t * gs, int ff, int fr, int tf, int tr)
486 if(gs->palace && (tr > gs->palace && tr < gs->ranks - gs->palace ||
487 tf < (gs->files - gs->palace)/2 || tf >= (gs->files + gs->palace)/2))
489 if (abs(ff - tf) != 1)
491 if (abs(fr - tr) != 1)
496 static int legal_alfil_move(struct game_state_t * gs, int ff, int fr, int tf, int tr)
498 if (abs(ff - tf) != 2)
500 if (abs(fr - tr) != 2)
505 static int legal_elephant_move(struct game_state_t * gs, int ff, int fr, int tf, int tr)
507 if (abs(ff - tf) != 2)
509 if (abs(fr - tr) != 2)
511 if(gs->board[(ff+tf)/2][(fr+tr)/2] != NOPIECE) return 0; // blocked
512 if((tr >= gs->ranks/2) != (fr >= gs->ranks/2)) return 0; // do not cross river
516 static int legal_gold_move(struct game_state_t * gs, int ff, int fr, int tf, int tr)
518 return legal_wazir_move(gs, ff, fr, tf, tr) || (abs(ff-tf) == 1 && tr == fr + (gs->onMove==WHITE ? 1 : -1));
521 static int legal_silver_move(struct game_state_t * gs, int ff, int fr, int tf, int tr)
523 return legal_ferz_move(gs, ff, fr, tf, tr) || (tf == ff && tr == fr + (gs->onMove==WHITE ? 1 : -1) );
526 static int legal_woody_move(struct game_state_t * gs, int ff, int fr, int tf, int tr)
528 return legal_wazir_move(gs, ff, fr, tf, tr) || legal_dababba_move(gs, ff, fr, tf, tr);
531 static int legal_squirrel_move(struct game_state_t * gs, int ff, int fr, int tf, int tr)
533 return legal_alfil_move(gs, ff, fr, tf, tr) || legal_dababba_move(gs, ff, fr, tf, tr)
534 || legal_knight_move(gs, ff, fr, tf, tr);
537 static int legal_mastodon_move(struct game_state_t * gs, int ff, int fr, int tf, int tr)
539 return legal_man_move(gs, ff, fr, tf, tr) || legal_alfil_move(gs, ff, fr, tf, tr)
540 || legal_dababba_move(gs, ff, fr, tf, tr);
543 static int legal_centaur_move(struct game_state_t * gs, int ff, int fr, int tf, int tr)
545 return legal_man_move(gs, ff, fr, tf, tr) || legal_knight_move(gs, ff, fr, tf, tr);
548 static int legal_amazon_move(struct game_state_t * gs, int ff, int fr, int tf, int tr)
550 return legal_queen_move(gs, ff, fr, tf, tr) || legal_knight_move(gs, ff, fr, tf, tr);
553 static int legal_dragonking_move(struct game_state_t * gs, int ff, int fr, int tf, int tr)
555 return legal_rook_move(gs, ff, fr, tf, tr) || legal_ferz_move(gs, ff, fr, tf, tr);
558 static int legal_dragonhorse_move(struct game_state_t * gs, int ff, int fr, int tf, int tr)
560 return legal_bishop_move(gs, ff, fr, tf, tr) || legal_wazir_move(gs, ff, fr, tf, tr);
563 static int legal_modernelephant_move(struct game_state_t * gs, int ff, int fr, int tf, int tr)
565 return legal_ferz_move(gs, ff, fr, tf, tr) || legal_alfil_move(gs, ff, fr, tf, tr);
568 static int legal_lieutenant_move(struct game_state_t * gs, int ff, int fr, int tf, int tr)
570 return legal_modernelephant_move(gs, ff, fr, tf, tr) ||
571 fr == tr && abs(ff - tf) == 1 && gs->board[tf][tr] == NOPIECE;
574 static int legal_priestess_move(struct game_state_t * gs, int ff, int fr, int tf, int tr)
576 return legal_knight_move(gs, ff, fr, tf, tr) || legal_ferz_move(gs, ff, fr, tf, tr)
577 || legal_alfil_move(gs, ff, fr, tf, tr);
580 static int legal_minister_move(struct game_state_t * gs, int ff, int fr, int tf, int tr)
582 return legal_knight_move(gs, ff, fr, tf, tr) || legal_wazir_move(gs, ff, fr, tf, tr)
583 || legal_dababba_move(gs, ff, fr, tf, tr);
586 static int legal_king_move(struct game_state_t * gs, int ff, int fr, int tf, int tr)
590 // [HGM] castle: test first if valid as regular King move; result = 1 or 0
592 result = legal_knight_move(gs, ff, fr, tf, tr);
593 else if(gs->palace) {
594 result = legal_wazir_move(gs, ff, fr, tf, tr);
595 if(!result && ff == tf && piecetype(gs->board[tf][tr]) == KING) { // XQ regicide
596 int i, d = (tr>fr ? 1 : -1);
597 for(i=fr+d; i!=tr; i+=d)
598 if(gs->board[ff][i] != NOPIECE) return 0; // line of sight blocked
602 result = legal_man_move(gs, ff, fr, tf, tr);
605 // [HGM] castle: orthodox legal castlings given as King move return 2
607 if (gs->onMove == WHITE) {
608 /* King side castling */
609 if ((fr == 0) && (tr == 0) && ((ff == gs->files/2) && (tf == gs->files-2) ||
610 gs->drops == 2 && (tf == gs->files/2) && (ff == gs->files-1)) // [HGM] reverse Seirawan gating
611 && (gs->wkmoved >= 0) && (gs->wkrmoved >= 0) && (gs->board[gs->files-3][0] == NOPIECE) &&
612 (gs->board[gs->files-2][0] == NOPIECE) && (gs->board[gs->files-1][0] == W_ROOK) &&
613 (gs->board[gs->files/2+1][0] == NOPIECE) && (!is_square_attacked(gs, gs->files/2+1, 0)) &&
614 (!is_square_attacked(gs, gs->files/2, 0)) && (!is_square_attacked(gs, gs->files-3, 0))) {
617 /* Queen side castling */
618 if ((fr == 0) && (tr == 0) && ((ff == gs->files/2) && (tf == 2) ||
619 gs->drops == 2 && (tf == gs->files/2) && (ff == 0)) // [HGM] reverse Seirawan gating
620 && (gs->wkmoved >= 0) && (gs->wqrmoved >= 0) && (gs->board[3][0] == NOPIECE) &&
621 (gs->board[2][0] == NOPIECE) && (gs->board[1][0] == NOPIECE) &&
622 (gs->board[0][0] == W_ROOK) &&
623 (gs->board[gs->files/2-1][0] == NOPIECE) && (!is_square_attacked(gs, gs->files/2-1, 0)) &&
624 (!is_square_attacked(gs, gs->files/2, 0)) && (!is_square_attacked(gs, 3, 0))) {
628 /* King side castling */
629 if ((fr == gs->ranks-1) && (tr == gs->ranks-1) && ((ff == gs->files/2) && (tf == gs->files-2) ||
630 gs->drops == 2 && (tf == gs->files/2) && (ff == gs->files-1)) // [HGM] reverse Seirawan gating
631 && (gs->bkmoved >= 0) && (gs->bkrmoved >= 0) && (gs->board[gs->files-3][7] == NOPIECE) &&
632 (gs->board[gs->files-2][gs->ranks-1] == NOPIECE) && (gs->board[gs->files-1][gs->ranks-1] == B_ROOK) &&
633 (gs->board[gs->files/2+1][gs->ranks-1] == NOPIECE) && (!is_square_attacked(gs, gs->files/2+1, gs->ranks-1)) &&
634 (!is_square_attacked(gs, gs->files/2, gs->ranks-1)) && (!is_square_attacked(gs, gs->files-3, gs->ranks-1))) {
637 /* Queen side castling */
638 if ((fr == gs->ranks-1) && (tr == gs->ranks-1) && ((ff == gs->files/2) && (tf == 2) ||
639 gs->drops == 2 && (tf == gs->files/2) && (ff == 0)) // [HGM] reverse Seirawan gating
640 && (gs->bkmoved >= 0) && (gs->bqrmoved >= 0) && (gs->board[3][gs->ranks-1] == NOPIECE) &&
641 (gs->board[2][gs->ranks-1] == NOPIECE) && (gs->board[1][gs->ranks-1] == NOPIECE) &&
642 (gs->board[0][gs->ranks-1] == B_ROOK) &&
643 (gs->board[gs->files/2-1][gs->ranks-1] == NOPIECE) && (!is_square_attacked(gs, gs->files/2-1, gs->ranks-1)) &&
644 (!is_square_attacked(gs, gs->files/2, gs->ranks-1)) && (!is_square_attacked(gs, 3, gs->ranks-1))) {
649 return 0; // neither regular King move nor castling
652 static void add_pos(int tof, int tor, int *posf, int *posr, int *numpos)
659 static void possible_pawn_moves(struct game_state_t * gs,
661 int *posf, int *posr, int *numpos)
663 if (gs->onMove == WHITE) {
664 if (gs->board[onf][onr + 1] == NOPIECE || gs->palace || gs->promoType == 3) {
665 add_pos(onf, onr + 1, posf, posr, numpos);
666 if ((onr <= gs->pawnDblStep) && (gs->board[onf][onr + 2] == NOPIECE))
667 add_pos(onf, onr + 2, posf, posr, numpos);
670 if (gs->board[onf - 1][onr + 1] != NOPIECE &&
671 iscolor(gs->board[onf - 1][onr + 1], BLACK) &&
672 !gs->palace && gs->promoType != 3) // no diagonal capture in XQ and Shogi
673 add_pos(onf - 1, onr + 1, posf, posr, numpos);
674 if(gs->palace && onr >= gs->ranks/2 && (gs->board[onf-1][onr] || iscolor(gs->board[onf-1][onr], BLACK)))
675 add_pos(onf - 1, onr, posf, posr, numpos); // XQ promoted pawn
677 if (onf < gs->files-1) {
678 if (gs->board[onf + 1][onr + 1] != NOPIECE &&
679 iscolor(gs->board[onf + 1][onr + 1], BLACK) &&
680 !gs->palace && gs->promoType != 3) // no diagonal capture in XQ and Shogi
681 add_pos(onf + 1, onr + 1, posf, posr, numpos);
682 if(gs->palace && onr >= gs->ranks/2 && (gs->board[onf+1][onr] || iscolor(gs->board[onf+1][onr], BLACK)))
683 add_pos(onf + 1, onr, posf, posr, numpos); // XQ promoted pawn
685 if (gs->ep_possible[0][onf] == -1)
686 add_pos(onf - 1, onr + 1, posf, posr, numpos);
687 if (gs->ep_possible[0][onf] == 1)
688 add_pos(onf + 1, onr + 1, posf, posr, numpos);
690 if (gs->board[onf][onr - 1] == NOPIECE || gs->palace || gs->promoType == 3) {
691 add_pos(onf, onr - 1, posf, posr, numpos);
692 if ((onr >= gs->ranks - gs->pawnDblStep - 1) && (gs->board[onf][onr - 2] == NOPIECE))
693 add_pos(onf, onr - 2, posf, posr, numpos);
696 if (gs->board[onf - 1][onr - 1] != NOPIECE &&
697 iscolor(gs->board[onf - 1][onr - 1], WHITE) &&
698 !gs->palace && gs->promoType != 3) // no diagonal capture in XQ and Shogi
699 add_pos(onf - 1, onr - 1, posf, posr, numpos);
700 if(gs->palace && onr < gs->ranks/2 && !iscolor(gs->board[onf-1][onr], BLACK))
701 add_pos(onf - 1, onr, posf, posr, numpos); // XQ promoted pawn
703 if (onf < gs->files-1) {
704 if (gs->board[onf + 1][onr - 1] != NOPIECE &&
705 iscolor(gs->board[onf + 1][onr - 1], WHITE) &&
706 !gs->palace && gs->promoType != 3) // no diagonal capture in XQ and Shogi
707 add_pos(onf + 1, onr - 1, posf, posr, numpos);
708 if(gs->palace && onr < gs->ranks/2 && !iscolor(gs->board[onf+1][onr], BLACK))
709 add_pos(onf + 1, onr, posf, posr, numpos); // XQ promoted pawn
711 if (gs->ep_possible[1][onf] == -1)
712 add_pos(onf - 1, onr - 1, posf, posr, numpos);
713 if (gs->ep_possible[1][onf] == 1)
714 add_pos(onf + 1, onr - 1, posf, posr, numpos);
718 static void possible_hoplite_moves(struct game_state_t * gs,
720 int *posf, int *posr, int *numpos)
722 if (gs->onMove == WHITE) { // in Spartan Chess there are no white hoplites...
723 if (gs->board[onf][onr + 1] != NOPIECE &&
724 iscolor(gs->board[onf][onr + 1], BLACK)) {
725 add_pos(onf, onr + 1, posf, posr, numpos);
728 if (gs->board[onf - 1][onr + 1] == NOPIECE) {
729 add_pos(onf - 1, onr + 1, posf, posr, numpos);
730 if (onf > 1 && (onr <= gs->pawnDblStep) /*&& (gs->board[onf-2][onr + 2] == NOPIECE)*/)
731 add_pos(onf - 2, onr + 2, posf, posr, numpos);
734 if (onf < gs->files-1) {
735 if (gs->board[onf + 1][onr + 1] == NOPIECE) {
736 add_pos(onf + 1, onr + 1, posf, posr, numpos);
737 if (onf < gs->files-2 && (onr <= gs->pawnDblStep) /*&& (gs->board[onf+2][onr + 2] == NOPIECE)*/)
738 add_pos(onf + 2, onr + 2, posf, posr, numpos);
742 if (gs->board[onf][onr - 1] != NOPIECE &&
743 iscolor(gs->board[onf][onr - 1], WHITE)) {
744 add_pos(onf, onr - 1, posf, posr, numpos);
747 if (gs->board[onf - 1][onr - 1] == NOPIECE) {
748 add_pos(onf - 1, onr - 1, posf, posr, numpos);
749 if (onf > 1 && (onr >= gs->ranks - gs->pawnDblStep - 1) /*&& (gs->board[onf - 2][onr - 2] == NOPIECE)*/)
750 add_pos(onf - 2, onr - 2, posf, posr, numpos);
753 if (onf < gs->files-1) {
754 if (gs->board[onf + 1][onr - 1] == NOPIECE) {
755 add_pos(onf + 1, onr - 1, posf, posr, numpos);
756 if (onf < gs->files-2 && (onr >= gs->ranks - gs->pawnDblStep - 1) /*&& (gs->board[onf + 2][onr - 2] == NOPIECE)*/)
757 add_pos(onf + 2, onr - 2, posf, posr, numpos);
763 static void possible_knight_moves(struct game_state_t * gs,
765 int *posf, int *posr, int *numpos)
767 static int knightJumps[8][2] = {{-1, 2}, {1, 2}, {2, -1}, {2, 1},
768 {-1, -2}, {1, -2}, {-2, 1}, {-2, -1}};
772 for (j = 0; j < 8; j++) {
773 f = knightJumps[j][0] + onf;
774 r = knightJumps[j][1] + onr;
775 if ((f < 0) || (f >= gs->files))
777 if ((r < 0) || (r >= gs->ranks))
779 if ((gs->board[f][r] == NOPIECE) ||
780 (iscolor(gs->board[f][r], CToggle(gs->onMove))))
781 add_pos(f, r, posf, posr, numpos);
785 static void possible_horse_moves(struct game_state_t * gs,
787 int *posf, int *posr, int *numpos)
789 static int knightJumps[8][4] = {{-1, 2, 0, 1}, {1, 2, 0, 1}, {2, -1, 1, 0}, {2, 1, 1, 0},
790 {-1, -2, 0, -1}, {1, -2, 0, -1}, {-2, 1, -1, 0}, {-2, -1, -1, 0}};
794 for (j = 0; j < 8; j++) {
795 f = knightJumps[j][0] + onf;
796 r = knightJumps[j][1] + onr;
797 if ((f < 0) || (f >= gs->files))
799 if ((r < 0) || (r >= gs->ranks))
801 if ((gs->board[knightJumps[j][2] + onf][knightJumps[j][3] + onr] == NOPIECE) &&
802 ((gs->board[f][r] == NOPIECE) || (iscolor(gs->board[f][r], CToggle(gs->onMove)))))
803 add_pos(f, r, posf, posr, numpos);
807 static void possible_bishop_moves(struct game_state_t * gs,
809 int *posf, int *posr, int *numpos)
819 if ((f < 0) || (f >= gs->files))
821 if ((r < 0) || (r >= gs->ranks))
823 if ((gs->board[f][r] != NOPIECE) && (iscolor(gs->board[f][r], gs->onMove)))
825 add_pos(f, r, posf, posr, numpos);
826 if (gs->board[f][r] != NOPIECE)
835 if ((f < 0) || (f >= gs->files))
837 if ((r < 0) || (r >= gs->ranks))
839 if ((gs->board[f][r] != NOPIECE) && (iscolor(gs->board[f][r], gs->onMove)))
841 add_pos(f, r, posf, posr, numpos);
842 if (gs->board[f][r] != NOPIECE)
851 if ((f < 0) || (f >= gs->files))
853 if ((r < 0) || (r >= gs->ranks))
855 if ((gs->board[f][r] != NOPIECE) && (iscolor(gs->board[f][r], gs->onMove)))
857 add_pos(f, r, posf, posr, numpos);
858 if (gs->board[f][r] != NOPIECE)
867 if ((f < 0) || (f >= gs->files))
869 if ((r < 0) || (r >= gs->ranks))
871 if ((gs->board[f][r] != NOPIECE) && (iscolor(gs->board[f][r], gs->onMove)))
873 add_pos(f, r, posf, posr, numpos);
874 if (gs->board[f][r] != NOPIECE)
879 static void possible_rook_moves(struct game_state_t * gs,
881 int *posf, int *posr, int *numpos)
890 if ((f < 0) || (f >= gs->files))
892 if ((r < 0) || (r >= gs->ranks))
894 if ((gs->board[f][r] != NOPIECE) && (iscolor(gs->board[f][r], gs->onMove)))
896 add_pos(f, r, posf, posr, numpos);
897 if (gs->board[f][r] != NOPIECE)
905 if ((f < 0) || (f >= gs->files))
907 if ((r < 0) || (r >= gs->ranks))
909 if ((gs->board[f][r] != NOPIECE) && (iscolor(gs->board[f][r], gs->onMove)))
911 add_pos(f, r, posf, posr, numpos);
912 if (gs->board[f][r] != NOPIECE)
920 if ((f < 0) || (f >= gs->files))
922 if ((r < 0) || (r >= gs->ranks))
924 if ((gs->board[f][r] != NOPIECE) && (iscolor(gs->board[f][r], gs->onMove)))
926 add_pos(f, r, posf, posr, numpos);
927 if (gs->board[f][r] != NOPIECE)
935 if ((f < 0) || (f >= gs->files))
937 if ((r < 0) || (r >= gs->ranks))
939 if ((gs->board[f][r] != NOPIECE) && (iscolor(gs->board[f][r], gs->onMove)))
941 add_pos(f, r, posf, posr, numpos);
942 if (gs->board[f][r] != NOPIECE)
947 static void possible_cannon_moves(struct game_state_t * gs,
949 int *posf, int *posr, int *numpos)
958 if ((f < 0) || (f >= gs->files))
960 if ((r < 0) || (r >= gs->ranks))
962 if ((gs->board[f][r] != NOPIECE) && i++ == 0) continue;
964 add_pos(f, r, posf, posr, numpos); // no hop: non-capt
965 else if(i == 2 && !iscolor(gs->board[f][r], gs->onMove))
966 add_pos(f, r, posf, posr, numpos); // hop: capt
967 if (gs->board[f][r] != NOPIECE)
975 if ((f < 0) || (f >= gs->files))
977 if ((r < 0) || (r >= gs->ranks))
979 if ((gs->board[f][r] != NOPIECE) && i++ == 0) continue;
981 add_pos(f, r, posf, posr, numpos); // no hop: non-capt
982 else if(i == 2 && !iscolor(gs->board[f][r], gs->onMove))
983 add_pos(f, r, posf, posr, numpos); // hop: capt
984 if (gs->board[f][r] != NOPIECE)
992 if ((f < 0) || (f >= gs->files))
994 if ((r < 0) || (r >= gs->ranks))
996 if ((gs->board[f][r] != NOPIECE) && i++ == 0) continue;
998 add_pos(f, r, posf, posr, numpos); // no hop: non-capt
999 else if(i == 2 && !iscolor(gs->board[f][r], gs->onMove))
1000 add_pos(f, r, posf, posr, numpos); // hop: capt
1001 if (gs->board[f][r] != NOPIECE)
1009 if ((f < 0) || (f >= gs->files))
1011 if ((r < 0) || (r >= gs->ranks))
1013 if ((gs->board[f][r] != NOPIECE) && i++ == 0) continue;
1015 add_pos(f, r, posf, posr, numpos); // no hop: non-capt
1016 else if(i == 2 && !iscolor(gs->board[f][r], gs->onMove))
1017 add_pos(f, r, posf, posr, numpos); // hop: capt
1018 if (gs->board[f][r] != NOPIECE)
1023 static void possible_lance_moves(struct game_state_t * gs,
1025 int *posf, int *posr, int *numpos)
1032 for (;gs->onMove == WHITE;) {
1034 if ((f < 0) || (f >= gs->files))
1036 if ((r < 0) || (r >= gs->ranks))
1038 if ((gs->board[f][r] != NOPIECE) && (iscolor(gs->board[f][r], gs->onMove)))
1040 add_pos(f, r, posf, posr, numpos);
1041 if (gs->board[f][r] != NOPIECE)
1047 for (;gs->onMove == BLACK;) {
1049 if ((f < 0) || (f >= gs->files))
1051 if ((r < 0) || (r >= gs->ranks))
1053 if ((gs->board[f][r] != NOPIECE) && (iscolor(gs->board[f][r], gs->onMove)))
1055 add_pos(f, r, posf, posr, numpos);
1056 if (gs->board[f][r] != NOPIECE)
1061 static void possible_cardinal_moves(struct game_state_t * gs,
1063 int *posf, int *posr, int *numpos)
1065 possible_knight_moves(gs, onf, onr, posf, posr, numpos);
1066 possible_bishop_moves(gs, onf, onr, posf, posr, numpos);
1069 static void possible_marshall_moves(struct game_state_t * gs,
1071 int *posf, int *posr, int *numpos)
1073 possible_rook_moves(gs, onf, onr, posf, posr, numpos);
1074 possible_knight_moves(gs, onf, onr, posf, posr, numpos);
1077 static void possible_queen_moves(struct game_state_t * gs,
1079 int *posf, int *posr, int *numpos)
1081 possible_rook_moves(gs, onf, onr, posf, posr, numpos);
1082 possible_bishop_moves(gs, onf, onr, posf, posr, numpos);
1085 static void possible_alfil_moves(struct game_state_t * gs,
1087 int *posf, int *posr, int *numpos)
1089 static int kingJumps[4][2] = {{-1, -1}, {1, -1}, {-1, 1}, {1, 1}};
1093 for (j = 0; j < 4; j++) {
1094 f = 2*kingJumps[j][0] + onf;
1095 r = 2*kingJumps[j][1] + onr;
1096 if ((f < 0) || (f >= gs->files))
1098 if ((r < 0) || (r >= gs->ranks))
1100 if ((gs->board[f][r] == NOPIECE) ||
1101 (iscolor(gs->board[f][r], CToggle(gs->onMove))))
1102 add_pos(f, r, posf, posr, numpos);
1106 static void possible_ferz_moves(struct game_state_t * gs,
1108 int *posf, int *posr, int *numpos)
1110 static int kingJumps[4][2] = {{-1, -1}, {1, -1}, {-1, 1}, {1, 1}};
1114 for (j = 0; j < 4; j++) {
1115 f = kingJumps[j][0] + onf;
1116 r = kingJumps[j][1] + onr;
1117 if ((f < 0) || (f >= gs->files))
1119 if ((r < 0) || (r >= gs->ranks))
1121 if ((gs->board[f][r] == NOPIECE) ||
1122 (iscolor(gs->board[f][r], CToggle(gs->onMove))))
1123 add_pos(f, r, posf, posr, numpos);
1127 static void possible_mandarin_moves(struct game_state_t * gs,
1129 int *posf, int *posr, int *numpos)
1131 static int kingJumps[4][2] = {{-1, -1}, {1, -1}, {-1, 1}, {1, 1}};
1135 for (j = 0; j < 4; j++) {
1136 f = kingJumps[j][0] + onf;
1137 r = kingJumps[j][1] + onr;
1138 if ((f < 0) || (f >= gs->files))
1140 if ((r < 0) || (r >= gs->ranks))
1142 if(gs->palace && (r >= gs->palace && r < gs->ranks - gs->palace ||
1143 f < (gs->files - gs->palace)/2 || f >= (gs->files + gs->palace)/2))
1145 if ((gs->board[f][r] == NOPIECE) ||
1146 (iscolor(gs->board[f][r], CToggle(gs->onMove))))
1147 add_pos(f, r, posf, posr, numpos);
1151 static void possible_wazir_moves(struct game_state_t * gs,
1153 int *posf, int *posr, int *numpos)
1155 static int kingJumps[4][2] = {{0, -1}, {0, 1}, {-1, 0}, {1, 0}};
1159 for (j = 0; j < 4; j++) {
1160 f = kingJumps[j][0] + onf;
1161 r = kingJumps[j][1] + onr;
1162 if ((f < 0) || (f >= gs->files))
1164 if ((r < 0) || (r >= gs->ranks))
1166 if(gs->palace && (r >= gs->palace && r < gs->ranks - gs->palace ||
1167 f < (gs->files - gs->palace)/2 || f >= (gs->files + gs->palace)/2))
1169 if ((gs->board[f][r] == NOPIECE) ||
1170 (iscolor(gs->board[f][r], CToggle(gs->onMove))))
1171 add_pos(f, r, posf, posr, numpos);
1175 static void possible_dababba_moves(struct game_state_t * gs,
1177 int *posf, int *posr, int *numpos)
1179 static int kingJumps[4][2] = {{0, -1}, {0, 1}, {-1, 0}, {1, 0}};
1183 for (j = 0; j < 4; j++) {
1184 f = 2*kingJumps[j][0] + onf;
1185 r = 2*kingJumps[j][1] + onr;
1186 if ((f < 0) || (f >= gs->files))
1188 if ((r < 0) || (r >= gs->ranks))
1190 if ((gs->board[f][r] == NOPIECE) ||
1191 (iscolor(gs->board[f][r], CToggle(gs->onMove))))
1192 add_pos(f, r, posf, posr, numpos);
1196 static void possible_man_moves(struct game_state_t * gs,
1198 int *posf, int *posr, int *numpos)
1200 possible_wazir_moves(gs, onf, onr, posf, posr, numpos);
1201 possible_ferz_moves(gs, onf, onr, posf, posr, numpos);
1204 static void possible_dragonking_moves(struct game_state_t * gs,
1206 int *posf, int *posr, int *numpos)
1208 possible_rook_moves(gs, onf, onr, posf, posr, numpos);
1209 possible_ferz_moves(gs, onf, onr, posf, posr, numpos);
1212 static void possible_dragonhorse_moves(struct game_state_t * gs,
1214 int *posf, int *posr, int *numpos)
1216 possible_wazir_moves(gs, onf, onr, posf, posr, numpos);
1217 possible_bishop_moves(gs, onf, onr, posf, posr, numpos);
1220 static void possible_centaur_moves(struct game_state_t * gs,
1222 int *posf, int *posr, int *numpos)
1224 possible_man_moves(gs, onf, onr, posf, posr, numpos);
1225 possible_knight_moves(gs, onf, onr, posf, posr, numpos);
1228 static void possible_woody_moves(struct game_state_t * gs,
1230 int *posf, int *posr, int *numpos)
1232 possible_wazir_moves(gs, onf, onr, posf, posr, numpos);
1233 possible_dababba_moves(gs, onf, onr, posf, posr, numpos);
1236 static void possible_squirrel_moves(struct game_state_t * gs,
1238 int *posf, int *posr, int *numpos)
1240 possible_alfil_moves(gs, onf, onr, posf, posr, numpos);
1241 possible_dababba_moves(gs, onf, onr, posf, posr, numpos);
1242 possible_knight_moves(gs, onf, onr, posf, posr, numpos);
1245 static void possible_mastodon_moves(struct game_state_t * gs,
1247 int *posf, int *posr, int *numpos)
1249 possible_man_moves(gs, onf, onr, posf, posr, numpos);
1250 possible_alfil_moves(gs, onf, onr, posf, posr, numpos);
1251 possible_dababba_moves(gs, onf, onr, posf, posr, numpos);
1254 static void possible_amazon_moves(struct game_state_t * gs,
1256 int *posf, int *posr, int *numpos)
1258 possible_queen_moves(gs, onf, onr, posf, posr, numpos);
1259 possible_knight_moves(gs, onf, onr, posf, posr, numpos);
1262 static void possible_modernelephant_moves(struct game_state_t * gs,
1264 int *posf, int *posr, int *numpos)
1266 possible_ferz_moves(gs, onf, onr, posf, posr, numpos);
1267 possible_alfil_moves(gs, onf, onr, posf, posr, numpos);
1270 static void possible_lieutenant_moves(struct game_state_t * gs,
1272 int *posf, int *posr, int *numpos)
1274 possible_modernelephant_moves(gs, onf, onr, posf, posr, numpos);
1275 if (onf < gs->files-1 && (gs->board[onf+1][onr] == NOPIECE))
1276 add_pos(onf + 1, onr, posf, posr, numpos);
1277 if (onf > 0 && (gs->board[onf-1][onr] == NOPIECE))
1278 add_pos(onf - 1, onr, posf, posr, numpos);
1281 static void possible_priestess_moves(struct game_state_t * gs,
1283 int *posf, int *posr, int *numpos)
1285 possible_ferz_moves(gs, onf, onr, posf, posr, numpos);
1286 possible_alfil_moves(gs, onf, onr, posf, posr, numpos);
1287 possible_knight_moves(gs, onf, onr, posf, posr, numpos);
1290 static void possible_minister_moves(struct game_state_t * gs,
1292 int *posf, int *posr, int *numpos)
1294 possible_wazir_moves(gs, onf, onr, posf, posr, numpos);
1295 possible_dababba_moves(gs, onf, onr, posf, posr, numpos);
1296 possible_knight_moves(gs, onf, onr, posf, posr, numpos);
1299 static void possible_gold_moves(struct game_state_t * gs,
1301 int *posf, int *posr, int *numpos)
1303 possible_wazir_moves(gs, onf, onr, posf, posr, numpos);
1304 if(gs->onMove == WHITE) {
1305 if(onr < gs->ranks-1)
1306 if(onf > 0 && !iscolor(gs->board[onf-1][onr+1], WHITE))
1307 add_pos(onf-1, onr+1, posf, posr, numpos);
1308 if(onf < gs->files-1 && !iscolor(gs->board[onf+1][onr+1], WHITE))
1309 add_pos(onf+1, onr+1, posf, posr, numpos);
1312 if(onf > 0 && !iscolor(gs->board[onf-1][onr-1], BLACK))
1313 add_pos(onf-1, onr-1, posf, posr, numpos);
1314 if(onf < gs->files-1 && !iscolor(gs->board[onf+1][onr-1], BLACK))
1315 add_pos(onf+1, onr-1, posf, posr, numpos);
1319 static void possible_silver_moves(struct game_state_t * gs,
1321 int *posf, int *posr, int *numpos)
1323 possible_ferz_moves(gs, onf, onr, posf, posr, numpos);
1324 if(gs->onMove == WHITE) {
1325 if(onr < gs->ranks-1 && !iscolor(gs->board[onf][onr+1], WHITE))
1326 add_pos(onf, onr+1, posf, posr, numpos);
1328 if(onr > 0 && !iscolor(gs->board[onf][onr-1], BLACK))
1329 add_pos(onf, onr-1, posf, posr, numpos);
1333 static void possible_honorablehorse_moves(struct game_state_t * gs,
1335 int *posf, int *posr, int *numpos)
1337 int f, r = onr + (gs->onMove == WHITE ? 2 : -2);
1339 if(r < 0 || r >= gs->ranks) return;
1341 if ((gs->board[onf-1][r] == NOPIECE) ||
1342 (iscolor(gs->board[onf-1][r], CToggle(gs->onMove))))
1343 add_pos(onf - 1, r, posf, posr, numpos);
1345 if(onf < gs->files - 1) {
1346 if ((gs->board[onf+1][r] == NOPIECE) ||
1347 (iscolor(gs->board[onf+1][r], CToggle(gs->onMove))))
1348 add_pos(onf + 1, r, posf, posr, numpos);
1352 static void possible_king_moves(struct game_state_t * gs,
1354 int *posf, int *posr, int *numpos)
1357 possible_knight_moves(gs, onf, onr, posf, posr, numpos);
1359 possible_wazir_moves(gs, onf, onr, posf, posr, numpos);
1361 possible_man_moves(gs, onf, onr, posf, posr, numpos);
1364 static void possible_elephant_moves(struct game_state_t * gs,
1366 int *posf, int *posr, int *numpos)
1368 static int kingJumps[4][2] = {{-1, -1}, {1, -1}, {-1, 1}, {1, 1}};
1372 for (j = 0; j < 4; j++) {
1373 f = 2*kingJumps[j][0] + onf;
1374 r = 2*kingJumps[j][1] + onr;
1375 if ((f < 0) || (f >= gs->files))
1377 if ((r < 0) || (r >= gs->ranks))
1379 if ((gs->board[(f+onf)/2][(r+onr)/2] == NOPIECE) && ((gs->board[f][r] == NOPIECE) ||
1380 (iscolor(gs->board[f][r], CToggle(gs->onMove)))))
1381 add_pos(f, r, posf, posr, numpos);
1385 /* Doesn't check for check */
1386 int legal_move(struct game_state_t * gs,
1387 int fFile, int fRank,
1388 int tFile, int tRank)
1390 int move_piece, victim;
1393 if (fFile == ALG_DROP) {
1395 if(gs->drops != 1) return 0; // [HGM] variants: no drops in this variant
1396 if (move_piece == KING)
1398 if (gs->holding[gs->onMove==WHITE ? 0 : 1][move_piece-1] == 0)
1400 if (gs->board[tFile][tRank] != NOPIECE)
1402 if (gs->promoType == 3) { // Shogi
1404 switch(move_piece) {
1405 case PAWN: // check for own Pawn in same file
1406 for(r=0; r<gs->ranks; r++) if(gs->board[tFile][r] == (gs->onMove|PAWN)) return 0;
1407 case LANCE: // Pawns and Lances not on last rank
1408 if(gs->onMove == WHITE && tRank >= gs->ranks-1) return 0;
1409 if(gs->onMove == BLACK && tRank < 1) return 0;
1411 case HONORABLEHORSE: // Knights at least two ranks from edge
1412 if(gs->onMove == WHITE && tRank >= gs->ranks-2) return 0;
1413 if(gs->onMove == BLACK && tRank < 2) return 0;
1417 if (move_piece == PAWN && (tRank == 0 || tRank == gs->ranks-1))
1420 } else if(fFile == ALG_CASTLE) {
1421 // [HGM] castle: this code can handle any type of free castling
1422 // it does not check if Rook and King from squares correspond to the rights, as the
1423 // user never enters such squares, but they are copied from the rights on entering o-o-o
1424 int backRank, kRook, qRook, fKing, leftEmpty, rightEmpty, leftCheck, rightCheck, f;
1425 if(!gs->castlingStyle) return 0; // no castling in this variant
1426 if(gs->onMove == WHITE) {
1427 if(gs->wkmoved < 0) return 0; // King moved
1428 fKing = gs->wkmoved;
1430 kRook = gs->wkrmoved;
1431 qRook = gs->wqrmoved;
1433 if(gs->bkmoved < 0) return 0; // King moved
1434 fKing = gs->bkmoved;
1435 backRank = gs->ranks-1;
1436 kRook = gs->bkrmoved;
1437 qRook = gs->bqrmoved;
1439 if((tRank > tFile ? qRook : kRook) < 0) return 0; // Rook moved
1440 // here we verified rights do exist, so from squares (fRank and fKing) must be valid
1441 if(gs->board[fRank][backRank] != (ROOK | gs->onMove) ) return 0; // only with own Rook
1442 if(gs->board[fKing][backRank] != (KING | gs->onMove) ) return 0; // only with own King
1444 // by now we know that K and R are in correct position, and still have rights
1445 if(tRank > tFile) { // R ends right of K: q-side
1446 leftEmpty = fRank < tFile ? fRank+1 : tFile+1;
1447 rightEmpty = tRank < fKing ? fKing-1 : tRank-1;
1449 leftEmpty = tRank < fKing ? tRank+1 : fKing+1;
1450 rightEmpty = fRank < tFile ? fRank-1 : tFile-1;
1452 for(f=leftEmpty; f<=rightEmpty; f++) // check if other pieces block castling
1453 if(f != fRank && f != fKing && gs->board[f][backRank] != NOPIECE) return 0;
1455 leftCheck = fKing < tFile ? fKing : tFile+1;
1456 rightCheck = fKing < tFile ? tFile-1 : fKing;
1457 for(f=leftCheck; f<=rightCheck; f++) // check if King passes attacked square or was in check
1458 if(is_square_attacked(gs, f, backRank)) return 0;
1460 return 1; // passed all tests
1462 move_piece = piecetype(gs->board[fFile][fRank]);
1464 if (gs->board[fFile][fRank] == NOPIECE)
1466 if (!iscolor(gs->board[fFile][fRank], gs->onMove)) /* Wrong color */
1468 if (((victim = gs->board[tFile][tRank]) != NOPIECE) &&
1469 iscolor(gs->board[tFile][tRank], gs->onMove)) {
1470 if(piecetype(move_piece) == KING && piecetype(victim) == ROOK) { // [HGM] could be FRC castling
1472 if(gs->drops== 2 && piecetype(move_piece) == ROOK && piecetype(victim) == KING) { // [HGM] could be Seirawan reverse gating
1473 return legal_king_move(gs, fFile, fRank, tFile, tRank);
1475 return 0; /* Can't capture own */
1477 if ((fFile == tFile) && (fRank == tRank)) /* Same square */
1479 switch (move_piece) {
1481 legal = legal_pawn_move(gs, fFile, fRank, tFile, tRank);
1484 legal = legal_hoplite_move(gs, fFile, fRank, tFile, tRank);
1487 legal = legal_knight_move(gs, fFile, fRank, tFile, tRank);
1490 legal = legal_bishop_move(gs, fFile, fRank, tFile, tRank);
1493 legal = legal_rook_move(gs, fFile, fRank, tFile, tRank);
1499 legal = legal_cardinal_move(gs, fFile, fRank, tFile, tRank);
1504 legal = legal_marshall_move(gs, fFile, fRank, tFile, tRank);
1508 legal = legal_man_move(gs, fFile, fRank, tFile, tRank);
1511 legal = legal_queen_move(gs, fFile, fRank, tFile, tRank);
1514 legal = legal_elephant_move(gs, fFile, fRank, tFile, tRank);
1517 legal = legal_amazon_move(gs, fFile, fRank, tFile, tRank);
1521 legal = legal_woody_move(gs, fFile, fRank, tFile, tRank);
1524 legal = legal_squirrel_move(gs, fFile, fRank, tFile, tRank);
1527 legal = legal_mastodon_move(gs, fFile, fRank, tFile, tRank);
1530 legal = legal_centaur_move(gs, fFile, fRank, tFile, tRank);
1533 legal = legal_horse_move(gs, fFile, fRank, tFile, tRank);
1537 legal = legal_ferz_move(gs, fFile, fRank, tFile, tRank);
1540 legal = legal_mandarin_move(gs, fFile, fRank, tFile, tRank);
1543 legal = legal_wazir_move(gs, fFile, fRank, tFile, tRank);
1546 legal = legal_lieutenant_move(gs, fFile, fRank, tFile, tRank);
1550 legal = legal_alfil_move(gs, fFile, fRank, tFile, tRank);
1552 case MODERNELEPHANT:
1553 legal = legal_modernelephant_move(gs, fFile, fRank, tFile, tRank);
1556 legal = legal_priestess_move(gs, fFile, fRank, tFile, tRank);
1559 legal = legal_minister_move(gs, fFile, fRank, tFile, tRank);
1562 legal = legal_silver_move(gs, fFile, fRank, tFile, tRank);
1565 legal = legal_gold_move(gs, fFile, fRank, tFile, tRank);
1568 legal = legal_lance_move(gs, fFile, fRank, tFile, tRank);
1571 legal = legal_cannon_move(gs, fFile, fRank, tFile, tRank);
1574 legal = legal_dragonhorse_move(gs, fFile, fRank, tFile, tRank);
1578 legal = legal_dragonking_move(gs, fFile, fRank, tFile, tRank);
1580 case HONORABLEHORSE:
1581 legal = legal_honorablehorse_move(gs, fFile, fRank, tFile, tRank);
1584 legal = legal_king_move(gs, fFile, fRank, tFile, tRank);
1593 #define DROP_CHAR '@'
1595 /* This fills in the rest of the mt structure once it is determined that
1596 * the move is legal. Returns MOVE_ILLEGAL if move leaves you in check */
1597 static int move_calculate(struct game_state_t * gs, struct move_t * mt, int promote)
1599 struct game_state_t fakeMove;
1600 int gating = 0, stm;
1602 mt->pieceCaptured = gs->board[mt->toFile][mt->toRank];
1603 mt->enPassant = 0; /* Don't know yet, let execute move take care
1605 if (mt->fromFile == ALG_DROP) {
1606 mt->piecePromotionTo = NOPIECE;
1607 sprintf(mt->moveString, "%s/%c%c-%c%d",
1608 wpstring[mt->fromRank],
1609 DROP_CHAR, DROP_CHAR,
1610 mt->toFile + 'a', mt->toRank + 1 - (gs->ranks>9));
1611 } else if(mt->fromFile == ALG_CASTLE) {
1612 // [HGM] castle: generalized castling, fr and tr give from and to file of Rook.
1613 sprintf(mt->moveString, mt->toRank > mt->toFile ? "o-o-o" : "o-o");
1614 if(gs->drops == 2 && promote && gs->holding[gs->onMove == BLACK][abs(promote)-1]) { // promote can be flipped (reverse gating kludge)
1615 int c = gs->onMove == WHITE ? 0 : gs->ranks-1;
1616 mt->piecePromotionTo = promote; gating = 1;
1617 if(promote < 0) sprintf(mt->moveString, "R/%c%d-e%d", mt->fromRank + 'a', c, c); // use RxK notation for Rook-square gatings
1620 stm = colorval(gs->board[mt->fromFile][mt->fromRank]);
1621 if(gs->promoType == 3) { // Shogi-style promotions: not just Pawns, but many pieces can promote
1622 int piece = gs->board[mt->fromFile][mt->fromRank];
1623 mt->piecePromotionTo = NOPIECE;
1624 if(colorval(piece) == WHITE && mt->fromRank < gs->ranks - gs->ranks/3
1625 && mt->toRank < gs->ranks - gs->ranks/3 ||
1626 colorval(piece) == BLACK && mt->fromRank >= gs->ranks/3
1627 && mt->toRank >= gs->ranks/3 )
1628 promote = NOPIECE; // suppress promotion outside zone
1629 if(promote) { // promotion piece determined by original, no matter what was requested
1630 switch(piecetype(piece)) {
1633 case HONORABLEHORSE:
1635 promote = GOLD; break;
1637 promote = DRAGONHORSE; break;
1639 promote = DRAGONKING; break;
1640 default: promote = NOPIECE; // not a promotion
1643 switch(piecetype(piece)) { // force mandatory promotions
1644 case HONORABLEHORSE:
1645 if(mt->toRank == 1 || mt->toRank == gs->files-2) promote = GOLD;
1648 if(mt->toRank == 0 || mt->toRank == gs->files-1) promote = GOLD;
1651 if(promote) mt->piecePromotionTo = promote | (colorval(gs->board[mt->fromFile][mt->fromRank]));
1653 if ((piecetype(gs->board[mt->fromFile][mt->fromRank]) == PAWN) &&
1654 !gs->palace && // [HGM] XQ: no promotions in xiangqi
1655 ((mt->toRank < gs->promoZone) || (mt->toRank >= gs->ranks - gs->promoZone))) {
1656 if(promote == KING) return MOVE_ILLEGAL; // no king in normal chess
1657 if(!promote && (mt->toRank == 0 || mt->toRank == gs->ranks-1)) { // promotion obligatory, but not specified
1658 if(gs->promoType != 2) promote = QUEEN; else { // choose a default
1659 for(promote=PIECES-1; promote>PAWN; promote--) if(gs->holding[stm == BLACK][promote-1]) break;
1660 if(promote == PAWN) return MOVE_ILLEGAL; // nothing available
1662 } // if not obligatory, we defer unless promotion was explicitly specified!
1663 if(!gs->pawnDblStep && promote == PRINCESS) promote = MAN2;
1664 if(!gs->pawnDblStep && promote != FERZ2 && promote != MAN2) promote = FERZ; // [HGM] kludge to recognize shatranj
1665 // non-promotion can still be an option for deeper promotion zones
1666 mt->piecePromotionTo = promote ? (promote | stm) : NOPIECE;
1667 if(promote && gs->promoType == 2 && !gs->holding[stm == BLACK][promote-1]) return MOVE_ILLEGAL; // unavailable piece specified
1668 if(promote == KNIGHT && gs->royalKnight) return MOVE_ILLEGAL; // Knight not allowed in Knightmate
1669 if(gs->promoType != 2 && promote > QUEEN) { // for promoType != 2 we must check explicitly if the requested pieceis compatible with the variant
1673 if(gs->drops != 2) return MOVE_ILLEGAL; // allowed only in S-Chess
1677 if(!gs->capablancaPieces) return MOVE_ILLEGAL; // allowed when flagged so
1681 if(!gs->pawnDblStep) return MOVE_ILLEGAL; // allowed in Shatranj and Courier
1684 if(!gs->royalKnight) return MOVE_ILLEGAL; // allowed only in Knightmate
1687 return MOVE_ILLEGAL;
1691 if ((piecetype(gs->board[mt->fromFile][mt->fromRank]) == HOPLITE) &&
1692 ((mt->toRank < gs->promoZone) || (mt->toRank >= gs->ranks - gs->promoZone))) {
1693 if(!promote || promote == KING) {
1694 int f, r, k=0, king = gs->onMove == WHITE ? W_KING : B_KING;
1695 for(r=0; r<gs->ranks;r++) for(f=0; f<gs->files; f++) k += (gs->board[f][r] == king);
1696 if(k > 1) { // we already have two kings
1697 if(promote == KING) return MOVE_ILLEGAL; // three kings not allowed
1698 promote = WARLORD; // use strongest piece as default
1699 } else promote = KING; // if no promo-piece given, this could be mate test, so test if promoting to King evades
1701 if(promote == MASTODON) promote = GENERAL; else
1702 if(promote == WOODY) promote = WARLORD; else
1703 if(promote == MARSHALL) promote = CAPTAIN; else
1704 if(promote != LIEUTENANT) return MOVE_ILLEGAL;
1705 mt->piecePromotionTo = (promote | stm);
1706 } else if(gs->drops == 2 && promote && mt->fromRank == (stm == WHITE ? 0 : gs->ranks-1)) { // [HGM] Seirawan-style gating
1707 int i; struct game *g = &game_globals.garray[gs->gameNum];
1708 if(!gs->holding[stm == BLACK][promote-1]) return MOVE_ILLEGAL; // unavailable piece specified
1709 // now we must test virginity of the moved piece. Yegh!
1710 for (i = g->numHalfMoves-1; i >= 0; i--) {
1711 if (g->moveList[i].fromFile == mt->fromFile && g->moveList[i].fromRank == mt->fromRank ||
1712 g->moveList[i].toFile == mt->fromFile && g->moveList[i].toRank == mt->fromRank ||
1713 g->moveList[i].fromFile == ALG_CASTLE && (gs->onMove == WHITE ? 0 : gs->ranks-1) == mt->fromRank &&
1714 (g->moveList[i].fromRank == mt->fromFile || gs->files>>1 == mt->fromFile )) return MOVE_ILLEGAL;
1716 mt->piecePromotionTo = promote; // gating OK
1717 gating = 1; // remember we did it for check test
1719 mt->piecePromotionTo = NOPIECE;
1721 if ((piecetype(gs->board[mt->fromFile][mt->fromRank]) == PAWN) &&
1722 ((mt->fromRank - mt->toRank == 2) || (mt->toRank - mt->fromRank == 2))) {
1723 mt->doublePawn = mt->fromFile;
1725 mt->doublePawn = -1;
1728 if ((piecetype(gs->board[mt->fromFile][mt->fromRank]) == KING) &&
1729 (mt->fromFile == gs->files/2) && (mt->toFile == 2) &&
1730 mt->fromRank == mt->toRank) {
1731 sprintf(mt->moveString, "o-o-o");
1732 } else if ((piecetype(gs->board[mt->fromFile][mt->fromRank]) == KING) &&
1733 (mt->fromFile == gs->files/2) && (mt->toFile == gs->files-2) &&
1734 mt->fromRank == mt->toRank) {
1735 sprintf(mt->moveString, "o-o");
1740 sprintf(mt->moveString, "%s/%c%d-%c%d",
1741 wpstring[piecetype(gs->board[mt->fromFile][mt->fromRank])],
1742 mt->fromFile + 'a', mt->fromRank + 1 - (gs->ranks>9),
1743 mt->toFile + 'a', mt->toRank + 1 - (gs->ranks>9));
1746 /* Replace this with an algabraic de-parser */
1748 sprintf(mt->algString, alg_unparse(gs, mt));
1750 /* Calculates enPassant also */
1751 execute_move(&fakeMove, mt, 0);
1752 if(gating) fakeMove.board[mt->fromFile][mt->fromRank] = NOPIECE; // [HGM] gating is only legal if non-gating move was (weird, but true)
1754 /* Does making this move leave ME in check? */
1755 if (in_check(&fakeMove))
1756 return MOVE_ILLEGAL;
1757 /* IanO: bughouse variants: drop cannot be check/checkmate */
1762 int legal_andcheck_move(struct game_state_t * gs,
1763 int fFile, int fRank,
1764 int tFile, int tRank)
1768 if (!legal_move(gs, fFile, fRank, tFile, tRank))
1770 mt.color = gs->onMove;
1771 mt.fromFile = fFile;
1772 mt.fromRank = fRank;
1775 /* This should take into account a pawn promoting to another piece */
1776 if (move_calculate(gs, &mt, NOPIECE) == MOVE_OK)
1782 /* in_check: checks if the side that is NOT about to move is in check
1784 int in_check(struct game_state_t * gs)
1787 int kf = -1, kr = -1;
1790 if (gs->onMove == WHITE) {
1791 for (f = 0; f < gs->files && kf < 0; f++)
1792 for (r = 0; r < gs->ranks && kf < 0; r++)
1793 if (gs->board[f][r] == B_KING) {
1798 for (f = 0; f < gs->files && kf < 0; f++)
1799 for (r = 0; r < gs->ranks && kf < 0; r++)
1800 if (gs->board[f][r] == W_KING) {
1806 // d_printf( "CHESSD: Error game with no king!\n");
1809 for (InitPieceLoop(gs->board, &f, &r, gs->onMove);
1810 NextPieceLoop(gs->board, &f, &r, gs->onMove, gs->files, gs->ranks);) {
1811 if (legal_move(gs, f, r, kf, kr)) { /* In Check? */
1812 if(gs->onMove == WHITE && !strcmp(gs->variant, "spartan")) { // first king is in check, but we might have spare
1813 //printf("spartan K-capt %c%d%c%d\n",f+'a',r+1,kf+'a',kr+1);
1814 gs->board[kf][kr] = B_MAN; // temporarily cure the check on the first King by replacing the latter;
1816 gs->board[kf][kr] = B_KING; // and put it back
1817 //printf("duple = %d\n",r);
1818 return r != 0; // if we have no second king (r = -1) or the second is also attacked (r = 1) we are in check.
1826 int has_legal_move(struct game_state_t * gs)
1831 int possiblef[500], possibler[500];
1832 int numpossible = 0;
1834 for (InitPieceLoop(gs->board, &f, &r, gs->onMove);
1835 NextPieceLoop(gs->board, &f, &r, gs->onMove, gs->files, gs->ranks);) {
1836 switch (piecetype(gs->board[f][r])) {
1838 possible_pawn_moves(gs, f, r, possiblef, possibler, &numpossible);
1841 possible_hoplite_moves(gs, f, r, possiblef, possibler, &numpossible);
1844 possible_knight_moves(gs, f, r, possiblef, possibler, &numpossible);
1847 possible_bishop_moves(gs, f, r, possiblef, possibler, &numpossible);
1850 possible_rook_moves(gs, f, r, possiblef, possibler, &numpossible);
1856 possible_cardinal_moves(gs, f, r, possiblef, possibler, &numpossible);
1861 possible_marshall_moves(gs, f, r, possiblef, possibler, &numpossible);
1865 possible_man_moves(gs, f, r, possiblef, possibler, &numpossible);
1868 possible_queen_moves(gs, f, r, possiblef, possibler, &numpossible);
1871 possible_elephant_moves(gs, f, r, possiblef, possibler, &numpossible);
1874 possible_amazon_moves(gs, f, r, possiblef, possibler, &numpossible);
1878 possible_woody_moves(gs, f, r, possiblef, possibler, &numpossible);
1881 possible_squirrel_moves(gs, f, r, possiblef, possibler, &numpossible);
1884 possible_mastodon_moves(gs, f, r, possiblef, possibler, &numpossible);
1887 possible_centaur_moves(gs, f, r, possiblef, possibler, &numpossible);
1890 possible_horse_moves(gs, f, r, possiblef, possibler, &numpossible);
1894 possible_ferz_moves(gs, f, r, possiblef, possibler, &numpossible);
1897 possible_mandarin_moves(gs, f, r, possiblef, possibler, &numpossible);
1900 possible_wazir_moves(gs, f, r, possiblef, possibler, &numpossible);
1904 possible_alfil_moves(gs, f, r, possiblef, possibler, &numpossible);
1906 case MODERNELEPHANT:
1907 possible_modernelephant_moves(gs, f, r, possiblef, possibler, &numpossible);
1910 possible_lieutenant_moves(gs, f, r, possiblef, possibler, &numpossible);
1913 possible_priestess_moves(gs, f, r, possiblef, possibler, &numpossible);
1916 possible_minister_moves(gs, f, r, possiblef, possibler, &numpossible);
1919 possible_silver_moves(gs, f, r, possiblef, possibler, &numpossible);
1922 possible_gold_moves(gs, f, r, possiblef, possibler, &numpossible);
1925 possible_cannon_moves(gs, f, r, possiblef, possibler, &numpossible);
1928 possible_lance_moves(gs, f, r, possiblef, possibler, &numpossible);
1931 possible_dragonhorse_moves(gs, f, r, possiblef, possibler, &numpossible);
1935 possible_dragonking_moves(gs, f, r, possiblef, possibler, &numpossible);
1937 case HONORABLEHORSE:
1938 possible_honorablehorse_moves(gs, f, r, possiblef, possibler, &numpossible);
1943 possible_king_moves(gs, f, r, possiblef, possibler, &numpossible);
1946 if (numpossible >= 500) {
1947 d_printf( "CHESSD: Possible move overrun\n");
1949 for (i = 0; i < numpossible; i++)
1950 if (legal_andcheck_move(gs, f, r, possiblef[i], possibler[i])) {
1955 /* IanO: if we got here, then kf and kr must be set */
1956 if (gs->gameNum >=0 && game_globals.garray[gs->gameNum].link >= 0
1957 || gs->holdings) { // [HGM] zh: also in 2-player games with drops
1958 /* bughouse: potential drops as check interpositions */
1959 gs->holding[gs->onMove==WHITE ? 0 : 1][QUEEN - 1]++;
1960 for (f=kf-1; f<=kf+1; f++) for (r=kr-1; r<=kr+1; r++) {
1961 if (f>=0 && f<gs->files && r>=0 && r<gs->ranks && gs->board[f][r] == NOPIECE) {
1962 /* try a drop next to the king */
1963 if (legal_andcheck_move(gs, ALG_DROP, QUEEN, f, r)) {
1964 gs->holding[gs->onMove==WHITE ? 0 : 1][QUEEN - 1]--;
1965 // OK, so we have an interposing drop. But do we have something to drop?
1966 if(game_globals.garray[gs->gameNum].link < 0) {
1967 // we have no partner, so we must have something to drop now
1968 for(i=QUEEN; i>=PAWN; i--)
1969 if (legal_andcheck_move(gs, ALG_DROP, i, f, r)) return 1;
1976 gs->holding[gs->onMove==WHITE ? 0 : 1][QUEEN - 1]--;
1982 /* This will end up being a very complicated function */
1983 int parse_move(char *mstr, struct game_state_t * gs, struct move_t * mt, int promote)
1985 int type = is_move(mstr);
1986 int result, flipflag = 1;
1988 mt->piecePromotionTo = NOPIECE;
1989 mt->color = gs->onMove;
1992 return MOVE_ILLEGAL;
1995 mt->fromFile = mstr[0] - 'a';
1996 mt->fromRank = mstr[1] - '1' + (gs->ranks>9);
1997 mt->toFile = mstr[2] - 'a';
1998 mt->toRank = mstr[3] - '1' + (gs->ranks>9);
2001 mt->fromFile = mstr[0] - 'a';
2002 mt->fromRank = mstr[1] - '1' + (gs->ranks>9);
2003 mt->toFile = mstr[3] - 'a';
2004 mt->toRank = mstr[4] - '1' + (gs->ranks>9);
2008 mt->fromFile = gs->files/2;
2009 mt->toFile = gs->files-2;
2010 if (gs->onMove == WHITE) {
2014 mt->fromRank = gs->ranks-1;
2015 mt->toRank = gs->ranks-1;
2019 // [HGM] castle: for now always assume Fischer-type castling (of which normal castling is a special case).
2020 mt->fromFile = ALG_CASTLE;
2021 mt->toFile = gs->files-2;
2022 mt->fromRank = gs->onMove == WHITE ? gs->wkrmoved : gs->bkrmoved;
2023 mt->toRank = mt->toFile-1; // R next to K
2027 mt->fromFile = gs->files/2;
2029 if (gs->onMove == WHITE) {
2033 mt->fromRank = gs->ranks-1;
2034 mt->toRank = gs->ranks-1;
2038 mt->fromFile = ALG_CASTLE;
2040 mt->fromRank = gs->onMove == WHITE ? gs->wqrmoved : gs->bqrmoved;
2041 mt->toRank = mt->toFile+1;
2044 /* Fills in the mt structure */
2045 if ((result = alg_parse_move(mstr, gs, mt)) != MOVE_OK)
2049 return MOVE_ILLEGAL;
2052 if((mt->fromRank >= gs->ranks || mt->fromRank < 0 || mt->fromFile >= gs->files) &&
2053 mt->fromFile != ALG_DROP && mt->fromFile != ALG_CASTLE
2054 || mt->toRank < 0 || mt->toRank >= gs->ranks || mt->toFile >= gs->files)
2055 return MOVE_ILLEGAL; // [HGM] make sure move stays on board
2057 if (!(result = legal_move(gs, mt->fromFile, mt->fromRank, mt->toFile, mt->toRank)))
2058 return MOVE_ILLEGAL;
2060 if(result == 2) { // [HGM] castle: orthodox castling was given as King move; convert it to new format
2061 int ff=mt->fromFile, tf=mt->toFile;
2062 if(piecetype(gs->board[tf][mt->toRank]) == KING) { // [HGM] RxK notation
2063 mt->fromFile = tf; mt->toFile = ff > tf ? gs->files-2 : 2; // correct to coventional
2064 flipflag = -1; // kludge: flip gated piece
2066 if(mt->fromFile - mt->toFile > 1) { // Q-side
2068 mt->toRank = mt->toFile+1;
2069 } else if(mt->toFile - mt->fromFile > 1) { // K-side
2070 mt->fromRank = gs->files-1;
2071 mt->toRank = mt->toFile-1;
2073 mt->fromFile = ALG_CASTLE;
2076 if (mt->piecePromotionTo != NOPIECE) {
2077 promote = piecetype(mt->piecePromotionTo);
2078 } else if (promote != NOPIECE) { // [HGM] promotion on long algebraic move; correct ambiguous types for variant
2079 if(gs->promoType == 3 && promote == MASTODON) promote = GOLD;
2080 if(gs->drops == 2 && promote == EMPRESS) promote = SELEPHANT;
2081 if(gs->drops == 2 && promote == DRAGONHORSE) promote = HAWK;
2084 return move_calculate(gs, mt, promote*flipflag);
2087 /* Returns MOVE_OK, MOVE_NOMATERIAL, MOVE_CHECKMATE, or MOVE_STALEMATE */
2088 /* check_game_status prevents recursion */
2089 int execute_move(struct game_state_t * gs, struct move_t * mt, int check_game_status)
2093 int i, j, foobar, wCnt, bCnt, king, rook;
2095 if (mt->fromFile == ALG_DROP) {
2096 movedPiece = mt->fromRank;
2097 tookPiece = NOPIECE;
2098 gs->holding[gs->onMove==WHITE ? 0 : 1][movedPiece-1]--;
2099 gs->board[mt->toFile][mt->toRank] = movedPiece | gs->onMove;
2100 if (gs->gameNum >= 0)
2101 gs->lastIrreversable = game_globals.garray[gs->gameNum].numHalfMoves;
2102 } else if(mt->fromFile == ALG_CASTLE) {
2103 int backRank, fKing;
2104 // [HGM] castle: perform castling
2105 if(gs->onMove == WHITE) {
2107 fKing = gs->wkmoved;
2108 gs->wkmoved = -gs->wkmoved-2;
2110 backRank = gs->ranks-1;
2111 fKing = gs->bkmoved;
2112 gs->bkmoved = -gs->bkmoved-2;
2114 // move Rook & King, in a way that is resistant to ending where they started (for FRC!)
2115 rook = gs->board[mt->fromRank][backRank]; // first remember
2116 king = gs->board[fKing][backRank];
2117 gs->board[mt->fromRank][backRank] = NOPIECE; // then erase
2118 gs->board[fKing][backRank] = NOPIECE;
2119 gs->board[mt->toRank][backRank] = rook; // then put back
2120 gs->board[mt->toFile][backRank] = king;
2121 if(gs->drops == 2 && mt->piecePromotionTo != NOPIECE) { // [HGM] Seirawan-style gating
2122 if(mt->piecePromotionTo > 0)
2123 gs->board[fKing][backRank] = mt->piecePromotionTo | gs->onMove; // gate on King square
2125 gs->board[mt->fromRank][backRank] = -mt->piecePromotionTo | gs->onMove; // gate on Rook square
2126 gs->holding[gs->onMove==WHITE ? 0 : 1][abs(mt->piecePromotionTo)-1]--; // remove gated piece from holdings
2129 movedPiece = gs->board[mt->fromFile][mt->fromRank];
2130 tookPiece = gs->board[mt->toFile][mt->toRank];
2131 if(gs->drops == 2 && mt->piecePromotionTo != NOPIECE && piecetype(movedPiece) != PAWN) { // [HGM] Seirawan-style gating
2132 gs->board[mt->toFile][mt->toRank] = gs->board[mt->fromFile][mt->fromRank];
2133 gs->board[mt->fromFile][mt->fromRank] = mt->piecePromotionTo | gs->onMove;;
2134 gs->holding[gs->onMove==WHITE ? 0 : 1][mt->piecePromotionTo-1]--; // remove gated piece from holdings
2136 if (mt->piecePromotionTo == NOPIECE) {
2137 gs->board[mt->toFile][mt->toRank] = gs->board[mt->fromFile][mt->fromRank];
2139 gs->board[mt->toFile][mt->toRank] = mt->piecePromotionTo | gs->onMove;
2140 if(gs->promoType == 2) gs->holding[gs->onMove][mt->piecePromotionTo-1]--;
2142 gs->board[mt->fromFile][mt->fromRank] = NOPIECE;
2144 /* Check if irreversable */
2145 if ((piecetype(movedPiece) == PAWN) && (mt->fromRank != mt->toRank) // [HGM] XQ: sideway Pawn move reversible!
2146 || (tookPiece != NOPIECE)) {
2147 if (gs->gameNum >= 0)
2148 gs->lastIrreversable = game_globals.garray[gs->gameNum].numHalfMoves;
2150 /* Check if this move is en-passant */
2151 if ((piecetype(movedPiece) == PAWN) && (mt->fromFile != mt->toFile) &&
2152 (tookPiece == NOPIECE) && !gs->palace) { // [HGM] XQ: no e.p. in sideway xiangqi moves
2153 if (gs->onMove == WHITE) {
2154 mt->pieceCaptured = B_PAWN;
2156 mt->pieceCaptured = W_PAWN;
2158 if (mt->fromFile > mt->toFile) {
2163 gs->board[mt->toFile][mt->fromRank] = NOPIECE;
2165 /* Check en-passant flags for next moves */
2166 for (i = 0; i < gs->files; i++) {
2167 gs->ep_possible[0][i] = 0;
2168 gs->ep_possible[1][i] = 0;
2170 /* Added by Sparky 3/16/95
2172 From soso@Viktoria.drp.fmph.uniba.sk Thu Mar 16 13:08:51 1995
2173 Subject: Re: To DAV: enpassant prob. again
2174 To: chess@caissa.onenet.net (ICS)
2175 Date: Thu, 16 Mar 1995 20:06:20 +0100 (MET)
2178 There was bug in other part of code:
2180 movecheck.c , line about 800:
2182 if (gs->onMove == WHITE) {
2183 if ((mt->toFile+1 < 7 ) .... should be : (mt->toFile < 7 ) }
2186 if ((piecetype(movedPiece) == PAWN) &&
2187 ((mt->fromRank == mt->toRank + 2) || (mt->fromRank + 2 == mt->toRank))) {
2188 /* Should turn on enpassent flag if possible */
2189 if (gs->onMove == WHITE) {
2190 if ((mt->toFile < gs->files-1) && gs->board[mt->toFile + 1][mt->toRank] == B_PAWN) {
2191 gs->ep_possible[1][mt->toFile + 1] = -1;
2193 if ((mt->toFile - 1 >= 0) && gs->board[mt->toFile - 1][mt->toRank] == B_PAWN) {
2194 gs->ep_possible[1][mt->toFile - 1] = 1;
2197 if ((mt->toFile < gs->files-1) && gs->board[mt->toFile + 1][mt->toRank] == W_PAWN) {
2198 gs->ep_possible[0][mt->toFile + 1] = -1;
2200 if ((mt->toFile - 1 >= 0) && gs->board[mt->toFile - 1][mt->toRank] == W_PAWN) {
2201 gs->ep_possible[0][mt->toFile - 1] = 1;
2205 if ((piecetype(movedPiece) == ROOK) && (mt->fromRank == 0) && (gs->onMove == WHITE)) {
2206 if (mt->fromFile == gs->wqrmoved) // [HGM] castle: flip w.r.t. -1 to remember original
2207 gs->wqrmoved = -gs->wqrmoved-2;
2208 if (mt->fromFile == gs->wkrmoved)
2209 gs->wkrmoved = -gs->wkrmoved-2;
2211 if ((piecetype(movedPiece) == ROOK) && (mt->fromRank == gs->ranks-1) && (gs->onMove == BLACK)) {
2212 if (mt->fromFile == gs->bqrmoved)
2213 gs->bqrmoved = -gs->bqrmoved-2;
2214 if (mt->fromFile == gs->bkrmoved)
2215 gs->bkrmoved = -gs->bkrmoved-2;
2217 if (piecetype(movedPiece) == KING) {
2218 if ((gs->onMove == WHITE) && (mt->fromFile == gs->wkmoved))
2219 gs->wkmoved = -gs->wkmoved-2;
2220 if ((gs->onMove == BLACK) && (mt->fromFile == gs->bkmoved))
2221 gs->bkmoved = -gs->bkmoved-2;
2224 if ((piecetype(movedPiece) == KING) &&
2225 ((mt->fromFile == gs->files/2) && (mt->toFile == gs->files-2)) &&
2226 mt->fromRank == mt->toRank) { /* Check for KS castling */
2227 gs->board[gs->files-3][mt->toRank] = gs->board[gs->files-1][mt->toRank];
2228 gs->board[gs->files-1][mt->toRank] = NOPIECE;
2230 if ((piecetype(movedPiece) == KING) &&
2231 ((mt->fromFile == gs->files/2) && (mt->toFile == 2)) &&
2232 mt->fromRank == mt->toRank) { /* Check for QS castling */
2233 gs->board[3][mt->toRank] = gs->board[0][mt->toRank];
2234 gs->board[0][mt->toRank] = NOPIECE;
2238 if (gs->onMove == BLACK)
2241 if (check_game_status) {
2242 /* Does this move result in check? */
2244 /* Check for checkmate */
2245 gs->onMove = CToggle(gs->onMove);
2246 if (!has_legal_move(gs))
2247 return MOVE_CHECKMATE;
2249 /* Check for stalemate */
2250 gs->onMove = CToggle(gs->onMove);
2251 if (!has_legal_move(gs))
2252 return gs->stalemate ? MOVE_STALEMATE : MOVE_CHECKMATE; // [HGM] in XQ and shatranj stalemate loses
2254 /* loon: check for insufficient mating material, first try */
2255 foobar = wCnt = bCnt = 0;
2256 for (i=0; i<gs->files; i++) {
2257 for (j=0; j<gs->ranks; j++) {
2258 int p = gs->board[i][j];
2259 switch(piecetype(p)) {
2271 if(p != NOPIECE && iscolor(p, WHITE)) wCnt++;
2272 if(iscolor(p, BLACK)) bCnt++;
2275 if(gs->bareKingLoses) { // [HGM] with bare-King-loses rule only KK is insuff. material
2276 if(gs->onMove == BLACK && wCnt == 1 && bCnt > 1) return MOVE_BARE;
2277 if(gs->onMove == WHITE && bCnt == 1 && wCnt > 1) return MOVE_BARE;
2278 if(bCnt == 1 && wCnt == 1) return MOVE_NOMATERIAL;
2279 } else if (foobar < 2)
2280 return MOVE_NOMATERIAL;
2282 gs->onMove = CToggle(gs->onMove);
2288 int backup_move(int g, int mode)
2290 struct game_state_t *gs;
2291 struct move_t *m, *m1;
2294 if (game_globals.garray[g].link >= 0) /*IanO: not implemented for bughouse yet */
2295 return MOVE_ILLEGAL;
2296 if (game_globals.garray[g].numHalfMoves < 1)
2297 return MOVE_ILLEGAL;
2298 gs = &game_globals.garray[g].game_state;
2299 m = (mode==REL_GAME) ? &game_globals.garray[g].moveList[game_globals.garray[g].numHalfMoves - 1] :
2300 &game_globals.garray[g].examMoveList[game_globals.garray[g].numHalfMoves - 1];
2301 if (m->toFile < 0) {
2302 return MOVE_ILLEGAL;
2304 if(m->fromFile == ALG_CASTLE) {
2305 // [HGM] castling in new format. Derive K and R moves
2306 int rank, kingFromFile;
2307 if(m->color == WHITE) {
2309 kingFromFile = -gs->wkmoved-2;
2310 if(kingFromFile<0) kingFromFile = -kingFromFile-2; // safety catch; should never happen?
2311 gs->wkmoved = kingFromFile;
2312 if(m->toRank > m->toFile) gs->wqrmoved = m->fromRank;
2313 else gs->wkrmoved = m->fromRank;
2316 kingFromFile = -gs->bkmoved-2;
2317 if(kingFromFile<0) kingFromFile = -kingFromFile-2; // safety catch; should never happen?
2318 gs->bkmoved = kingFromFile;
2319 if(m->toRank > m->toFile) gs->bqrmoved = m->fromRank;
2320 else gs->bkrmoved = m->fromRank;
2322 // remove first, as one might come back to a square the other left
2323 gs->board[m->toFile ][rank] = NOPIECE; // King toSqr
2324 gs->board[m->toRank ][rank] = NOPIECE; // Rook toSqr
2325 if(gs->board[m->fromRank][rank] != NOPIECE)
2326 gs->holding[gs->onMove==WHITE ? 1 : 0][piecetype(gs->board[m->fromRank][rank])-1]++; // put back in holdings (onMove not flipped yet!)
2327 if(gs->board[kingFromFile][rank] != NOPIECE)
2328 gs->holding[gs->onMove==WHITE ? 1 : 0][piecetype(gs->board[kingFromFile][rank])-1]++; // put back in holdings (onMove not flipped yet!)
2329 gs->board[m->fromRank][rank] = ROOK | m->color; // Rook fromSqr
2330 gs->board[kingFromFile][rank] = KING | m->color; // King fromSquare
2333 piece = gs->board[m->toFile][m->toRank];
2334 if(gs->board[m->fromFile][m->fromRank] != NOPIECE) { // [HGM] from-square occupied; move must have been Seirawan-style gating
2335 gs->holding[gs->onMove==WHITE ? 1 : 0][piecetype(gs->board[m->fromFile][m->fromRank])-1]++; // put back in holdings (onMove not flipped yet!)
2337 if (m->piecePromotionTo != NOPIECE) { // it is a real promotion
2338 switch(piecetype(m->piecePromotionTo)) { // Spartan pieces came from Hoplite, Shogi is problematic
2343 case GENERAL: piece = HOPLITE; break;
2344 case DRAGONHORSE: piece = BISHOP; break;
2345 case DRAGONKING: piece = ROOK; break;
2346 case GOLD: // TODO: figure out what original was
2347 default: piece = PAWN;
2349 piece |= colorval(gs->board[m->toFile][m->toRank]);
2351 gs->board[m->fromFile][m->fromRank] = piece;
2353 When takeback a _first_ move of rook, the ??rmoved variable
2354 must be cleared . To check, if the move is first, we should
2356 *******************/
2357 if (piecetype(gs->board[m->fromFile][m->fromRank]) == ROOK) {
2358 if (m->color == WHITE) {
2359 if ((m->fromFile == -gs->wqrmoved-2) && (m->fromRank == 0)) {
2360 for (i = 2; i < game_globals.garray[g].numHalfMoves - 1; i += 2) {
2361 m1 = (mode==REL_GAME) ? &game_globals.garray[g].moveList[i] : &game_globals.garray[g].examMoveList[i];
2362 if ((m1->fromFile == -gs->wqrmoved-2) && (m1->fromRank == 0))
2365 if (i == game_globals.garray[g].numHalfMoves - 1)
2366 gs->wqrmoved = m->fromFile;
2368 if ((m->fromFile == -gs->wkrmoved-2) && (m->fromRank == 0)) {
2369 for (i = 2; i < game_globals.garray[g].numHalfMoves - 1; i += 2) {
2370 m1 = (mode==REL_GAME) ? &game_globals.garray[g].moveList[i] : &game_globals.garray[g].examMoveList[i];
2371 if ((m1->fromFile == -gs->wkrmoved-2) && (m1->fromRank == 0))
2374 if (i == game_globals.garray[g].numHalfMoves - 1)
2375 gs->wkrmoved = m->fromFile;
2378 if ((m->fromFile == -gs->bqrmoved-2) && (m->fromRank == gs->ranks-1)) {
2379 for (i = 3; i < game_globals.garray[g].numHalfMoves - 1; i += 2) {
2380 m1 = (mode==REL_GAME) ? &game_globals.garray[g].moveList[i] : &game_globals.garray[g].examMoveList[i];
2381 if ((m1->fromFile == -gs->bkrmoved-2) && (m1->fromRank == gs->ranks-1))
2384 if (i == game_globals.garray[g].numHalfMoves - 1)
2385 gs->bqrmoved = m->fromFile;
2387 if ((m->fromFile == -gs->bkrmoved-2) && (m->fromRank == gs->ranks-1)) {
2388 for (i = 3; i < game_globals.garray[g].numHalfMoves - 1; i += 2) {
2389 m1 = (mode==REL_GAME) ? &game_globals.garray[g].moveList[i] : &game_globals.garray[g].examMoveList[i];
2390 if ((m1->fromFile == -gs->wkrmoved-2) && (m1->fromRank == gs->ranks-1))
2393 if (i == game_globals.garray[g].numHalfMoves - 1)
2394 gs->bkrmoved = m->fromFile;
2398 if (piecetype(gs->board[m->fromFile][m->fromRank]) == KING) {
2399 gs->board[m->toFile][m->toRank] = m->pieceCaptured;
2401 /* [HGM] castlings are already intercepted due to new format; this code wrecks knightmate! */
2402 if (m->toFile - m->fromFile == 2) {
2403 gs->board[7][m->fromRank] = ROOK |
2404 colorval(gs->board[m->fromFile][m->fromRank]);
2405 gs->board[5][m->fromRank] = NOPIECE;
2408 If takeback a castling, the appropriates ??moved variables
2411 if (m->color == WHITE) {
2420 if (m->fromFile - m->toFile == 2) {
2421 gs->board[0][m->fromRank] = ROOK |
2422 colorval(gs->board[m->fromFile][m->fromRank]);
2423 gs->board[3][m->fromRank] = NOPIECE;
2426 If takeback a castling, the appropriate ??moved variables
2429 if (m->color == WHITE) {
2440 When takeback a _first_ move of king (not the castling),
2441 the ?kmoved variable must be cleared . To check, if the move is first,
2442 we should scan moveList.
2443 *******************/
2445 if (m->color == WHITE) {
2447 if ((m->fromFile == -gs->wkmoved-2) && (m->fromRank == 0)) {
2448 for (i = 2; i < game_globals.garray[g].numHalfMoves - 1; i += 2) {
2449 m1 = (mode==REL_GAME) ? &game_globals.garray[g].moveList[i] : &game_globals.garray[g].examMoveList[i];
2450 if ((m1->fromFile == gs->wkmoved-2) && (m1->fromRank == 0))
2453 if (i == game_globals.garray[g].numHalfMoves - 1)
2454 gs->wkmoved = m->fromFile;
2457 if ((m->fromFile == -gs->bkmoved-2) && (m->fromRank == gs->ranks-1)) {
2458 for (i = 3; i < game_globals.garray[g].numHalfMoves - 1; i += 2) {
2459 m1 = (mode==REL_GAME) ? &game_globals.garray[g].moveList[i] : &game_globals.garray[g].examMoveList[i];
2460 if ((m1->fromFile == -gs->bkmoved-2) && (m1->fromRank == gs->ranks-1))
2463 if (i == game_globals.garray[g].numHalfMoves - 1)
2464 gs->bkmoved = m->fromFile;
2468 if (m->enPassant) { /* Do enPassant */
2469 gs->board[m->toFile][m->fromRank] = PAWN |
2470 (colorval(gs->board[m->fromFile][m->fromRank]) == WHITE ? BLACK : WHITE);
2471 gs->board[m->toFile][m->toRank] = NOPIECE;
2472 /* Should set the enpassant array, but I don't care right now */
2475 gs->board[m->toFile][m->toRank] = m->pieceCaptured;
2477 if (game_globals.garray[g].status != GAME_EXAMINE) {
2478 game_update_time(g);
2480 game_globals.garray[g].numHalfMoves--;
2481 if (game_globals.garray[g].status != GAME_EXAMINE) {
2482 if (game_globals.garray[g].wInitTime) { /* Don't update times in untimed games */
2485 if (m->color == WHITE) {
2486 if (net_globals.con[player_globals.parray[game_globals.garray[g].white].socket]->timeseal) { /* white uses timeseal? */
2487 game_globals.garray[g].wRealTime += (m->tookTime * 100);
2488 game_globals.garray[g].wRealTime -= (game_globals.garray[g].wIncrement * 100);
2489 game_globals.garray[g].wTime = game_globals.garray[g].wRealTime / 100;
2490 if (net_globals.con[player_globals.parray[game_globals.garray[g].black].socket]->timeseal) { /* opp uses timeseal? */
2491 game_globals.garray[g].bTime = game_globals.garray[g].bRealTime / 100;
2492 } else { /* opp has no timeseal */
2493 game_globals.garray[g].bTime += (game_globals.garray[g].lastDecTime - game_globals.garray[g].lastMoveTime);
2495 } else { /* white has no timeseal */
2496 game_globals.garray[g].wTime += m->tookTime;
2497 game_globals.garray[g].wTime -= game_globals.garray[g].wIncrement;
2498 if (net_globals.con[player_globals.parray[game_globals.garray[g].black].socket]->timeseal) { /* opp uses timeseal? */
2499 game_globals.garray[g].bTime = game_globals.garray[g].bRealTime / 100;
2500 } else { /* opp has no timeseal */
2501 game_globals.garray[g].bTime += (game_globals.garray[g].lastDecTime - game_globals.garray[g].lastMoveTime);
2505 if (net_globals.con[player_globals.parray[game_globals.garray[g].black].socket]->timeseal) { /* black uses timeseal? */
2506 game_globals.garray[g].bRealTime += (m->tookTime * 100);
2507 game_globals.garray[g].bRealTime -= (game_globals.garray[g].wIncrement * 100);
2508 game_globals.garray[g].bTime = game_globals.garray[g].bRealTime / 100;
2509 if (net_globals.con[player_globals.parray[game_globals.garray[g].white].socket]->timeseal) { /* opp uses timeseal? */
2510 game_globals.garray[g].wTime = game_globals.garray[g].wRealTime / 100;
2511 } else { /* opp has no timeseal */
2512 game_globals.garray[g].wTime += (game_globals.garray[g].lastDecTime - game_globals.garray[g].lastMoveTime);
2514 } else { /* black has no timeseal */
2515 game_globals.garray[g].bTime += m->tookTime;
2516 if (!game_globals.garray[g].bIncrement)
2517 game_globals.garray[g].bTime -= game_globals.garray[g].wIncrement;
2519 game_globals.garray[g].bTime -= game_globals.garray[g].bIncrement;
2520 if (net_globals.con[player_globals.parray[game_globals.garray[g].white].socket]->timeseal) { /* opp uses timeseal? */
2521 game_globals.garray[g].wTime = game_globals.garray[g].wRealTime / 100;
2522 } else { /* opp has no timeseal */
2523 game_globals.garray[g].wTime += (game_globals.garray[g].lastDecTime - game_globals.garray[g].lastMoveTime);
2528 if (game_globals.garray[g].numHalfMoves == 0)
2529 game_globals.garray[g].timeOfStart = now;
2530 game_globals.garray[g].lastMoveTime = now;
2531 game_globals.garray[g].lastDecTime = now;
2534 if (gs->onMove == BLACK)
2541 /******* Here begins the patch : ********************************
2542 Takeback of last move is done already, it's time to update enpassant
2543 array. (patch from Soso, added by Sparky 3/17/95)
2546 if (game_globals.garray[g].numHalfMoves > 0) {
2547 m1 = (mode==REL_GAME) ? &game_globals.garray[g].moveList[game_globals.garray[g].numHalfMoves - 1] :
2548 &game_globals.garray[g].examMoveList[game_globals.garray[g].numHalfMoves - 1];
2549 if (piecetype(gs->board[m1->toFile][m1->toRank]) == PAWN) {
2550 if ((m1->toRank - m1->fromRank) == 2) {
2551 if ((m1->toFile < gs->files-1) && gs->board[m1->toFile + 1][m1->toRank] == B_PAWN) {
2552 gs->ep_possible[1][m1->toFile + 1] = -1;
2554 if ((m1->toFile - 1 >= 0) && gs->board[m1->toFile - 1][m1->toRank] == B_PAWN) {
2555 gs->ep_possible[1][m1->toFile - 1] = 1;
2558 if ((m1->toRank - m1->fromRank) == -2) {
2559 if ((m1->toFile < gs->files-1) && gs->board[m1->toFile + 1][m1->toRank] == W_PAWN) {
2560 gs->ep_possible[0][m1->toFile + 1] = -1;
2562 if ((m1->toFile - 1 >= 0) && gs->board[m1->toFile - 1][m1->toRank] == W_PAWN) {
2563 gs->ep_possible[0][m1->toFile - 1] = 1;
2568 /************** and here's the end **************/