aboutsummaryrefslogtreecommitdiffstats
path: root/FICS/movecheck.c
diff options
context:
space:
mode:
Diffstat (limited to 'FICS/movecheck.c')
-rw-r--r--FICS/movecheck.c1608
1 files changed, 1608 insertions, 0 deletions
diff --git a/FICS/movecheck.c b/FICS/movecheck.c
new file mode 100644
index 0000000..5f607de
--- /dev/null
+++ b/FICS/movecheck.c
@@ -0,0 +1,1608 @@
+/* movecheck.c
+ *
+ */
+
+/*
+ fics - An internet chess server.
+ Copyright (C) 1993 Richard V. Nash
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+/* Revision history:
+ name email yy/mm/dd Change
+ Richard Nash 93/10/22 Created
+ Markus Uhlin 23/12/14 Fixed compiler warnings
+ Markus Uhlin 23/12/17 Fixed compiler warnings
+ Markus Uhlin 23/12/24 Fixed dead assignment
+ Markus Uhlin 24/05/05 Refactored and reformatted all
+ functions.
+ Markus Uhlin 25/03/21 Fixed out-of-bounds array access
+ in has_legal_move().
+*/
+
+#include "stdinclude.h"
+
+#include "algcheck.h"
+#include "board.h"
+#include "common.h"
+#include "gamedb.h"
+#include "movecheck.h"
+#include "network.h"
+#include "playerdb.h"
+#include "utils.h"
+
+#if __linux__
+#include <bsd/string.h>
+#endif
+
+/*
+ * Simply tests if the input string is a move or not. If it matches
+ * patterns below.
+ *
+ * Add to this list as you improve the move parser:
+ * MS_COMP e2e4
+ * MS_COMPDASH e2-e4
+ * MS_CASTLE o-o, o-o-o
+ * Not done yet:
+ * MS_ALG e4, Nd5 Ncd5
+ */
+PUBLIC int
+is_move(char *mstr)
+{
+ int len = strlen(mstr);
+
+ if ((len > 3) && (mstr[len - 2] == '='))
+ len -= 2;
+
+ if (len == 4) { // Test for e2e4
+ if (isfile(mstr[0]) && isrank(mstr[1]) &&
+ isfile(mstr[2]) && isrank(mstr[3])) {
+ return MS_COMP;
+ }
+ }
+
+ if (len == 5) { // Test for e2-e4
+ if (isfile(mstr[0]) &&
+ isrank(mstr[1]) &&
+ (mstr[2] == '-') &&
+ isfile(mstr[3]) &&
+ isrank(mstr[4]))
+ return MS_COMPDASH;
+ }
+
+ if (len == 3) { // Test for o-o
+ if ((mstr[0] == 'o') && (mstr[1] == '-') && (mstr[2] == 'o'))
+ return MS_KCASTLE;
+ if ((mstr[0] == 'O') && (mstr[1] == '-') && (mstr[2] == 'O'))
+ return MS_KCASTLE;
+ if ((mstr[0] == '0') && (mstr[1] == '-') && (mstr[2] == '0'))
+ return MS_KCASTLE;
+ }
+
+ if (len == 2) { // Test for oo
+ if ((mstr[0] == 'o') && (mstr[1] == 'o'))
+ return MS_KCASTLE;
+ if ((mstr[0] == 'O') && (mstr[1] == 'O'))
+ return MS_KCASTLE;
+ if ((mstr[0] == '0') && (mstr[1] == '0'))
+ return MS_KCASTLE;
+ }
+
+ if (len == 5) { // Test for o-o-o
+ if ((mstr[0] == 'o') && (mstr[1] == '-') && (mstr[2] == 'o') &&
+ (mstr[3] == '-') && (mstr[4] == 'o'))
+ return MS_QCASTLE;
+ if ((mstr[0] == 'O') && (mstr[1] == '-') && (mstr[2] == 'O') &&
+ (mstr[3] == '-') && (mstr[4] == 'O'))
+ return MS_QCASTLE;
+ if ((mstr[0] == '0') && (mstr[1] == '-') && (mstr[2] == '0') &&
+ (mstr[3] == '-') && (mstr[4] == '0'))
+ return MS_QCASTLE;
+ }
+
+ if (len == 3) { // Test for ooo
+ if ((mstr[0] == 'o') && (mstr[1] == 'o') && (mstr[2] == 'o'))
+ return MS_QCASTLE;
+ if ((mstr[0] == 'O') && (mstr[1] == 'O') && (mstr[2] == 'O'))
+ return MS_QCASTLE;
+ if ((mstr[0] == '0') && (mstr[1] == '0') && (mstr[2] == '0'))
+ return MS_QCASTLE;
+ }
+
+ return alg_is_move(mstr);
+}
+
+PUBLIC int
+NextPieceLoop(board_t b, int *f, int *r, int color)
+{
+ while (1) {
+ (*r) = (*r) + 1;
+
+ if (*r > 7) {
+ *r = 0;
+ *f = *f + 1;
+
+ if (*f > 7)
+ break;
+ }
+
+ if ((b[*f][*r] != NOPIECE) && iscolor(b[*f][*r], color))
+ return 1;
+ }
+
+ return 0;
+}
+
+PUBLIC int
+InitPieceLoop(board_t b, int *f, int *r, int color)
+{
+ *f = 0;
+ *r = -1;
+ return 1;
+}
+
+PRIVATE int
+legal_pawn_move(game_state_t *gs, int ff, int fr, int tf, int tr)
+{
+ if (ff == tf) {
+ if (gs->board[tf][tr] != NOPIECE)
+ return 0;
+
+ if (gs->onMove == WHITE) {
+ if (tr - fr == 1)
+ return 1;
+ if ((fr == 1) && (tr - fr == 2) &&
+ gs->board[ff][2] == NOPIECE)
+ return 1;
+ } else {
+ if (fr - tr == 1)
+ return 1;
+ if ((fr == 6) && (fr - tr == 2) &&
+ gs->board[ff][5] == NOPIECE)
+ return 1;
+ }
+
+ return 0;
+ }
+
+ if (ff != tf) { /* Capture ? */
+ if ((ff - tf != 1) && (tf - ff != 1))
+ return 0;
+ if ((fr - tr != 1) && (tr - fr != 1))
+ return 0;
+
+ if (gs->onMove == WHITE) {
+ if (fr > tr)
+ return 0;
+ if ((gs->board[tf][tr] != NOPIECE) &&
+ iscolor(gs->board[tf][tr], BLACK))
+ return 1;
+ if (gs->ep_possible[0][ff] == 1) {
+ if ((tf == ff + 1) &&
+ (gs->board[ff + 1][fr] == B_PAWN))
+ return 1;
+ } else if (gs->ep_possible[0][ff] == -1) {
+ if ((tf == ff - 1) &&
+ (gs->board[ff - 1][fr] == B_PAWN))
+ return 1;
+ }
+ } else {
+ if (tr > fr)
+ return 0;
+ if ((gs->board[tf][tr] != NOPIECE) &&
+ iscolor(gs->board[tf][tr], WHITE))
+ return 1;
+ if (gs->ep_possible[1][ff] == 1) {
+ if ((tf == ff + 1) &&
+ (gs->board[ff + 1][fr] == W_PAWN))
+ return 1;
+ } else if (gs->ep_possible[1][ff] == -1) {
+ if ((tf == ff - 1) &&
+ (gs->board[ff - 1][fr] == W_PAWN))
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+PRIVATE int
+legal_knight_move(game_state_t *gs, int ff, int fr, int tf, int tr)
+{
+ int dx, dy;
+
+ dx = ff - tf;
+ dy = fr - tr;
+
+ if ((dx == 2) || (dx == -2)) {
+ if ((dy == -1) || (dy == 1))
+ return 1;
+ }
+ if ((dy == 2) || (dy == -2)) {
+ if ((dx == -1) || (dx == 1))
+ return 1;
+ }
+ return 0;
+}
+
+PRIVATE int
+legal_bishop_move(game_state_t *gs, int ff, int fr, int tf, int tr)
+{
+ int count;
+ int dx, dy, x, y;
+ int incx, incy;
+ int startx, starty;
+
+ if (ff > tf) {
+ dx = ff - tf;
+ incx = -1;
+ } else {
+ dx = tf - ff;
+ incx = 1;
+ }
+ startx = ff + incx;
+ if (fr > tr) {
+ dy = fr - tr;
+ incy = -1;
+ } else {
+ dy = tr - fr;
+ incy = 1;
+ }
+ starty = fr + incy;
+ if (dx != dy)
+ return 0; // Not diagonal
+ if (dx == 1)
+ return 1; // One square, ok
+ count = dx - 1;
+ for (x = startx, y = starty;
+ count;
+ x += incx, y += incy, count--) {
+ if (gs->board[x][y] != NOPIECE)
+ return 0;
+ }
+ return 1;
+}
+
+PRIVATE int
+legal_rook_move(game_state_t *gs, int ff, int fr, int tf, int tr)
+{
+ int i;
+ int start, stop;
+
+ if (ff == tf) {
+ if (((fr - tr) == 1) || ((tr - fr) == 1))
+ return 1;
+ if (fr < tr) {
+ start = fr + 1;
+ stop = tr - 1;
+ } else {
+ start = tr + 1;
+ stop = fr - 1;
+ }
+ for (i = start; i <= stop; i++) {
+ if (gs->board[ff][i] != NOPIECE)
+ return 0;
+ }
+ return 1;
+ } else if (fr == tr) {
+ if (((ff - tf) == 1) || ((tf - ff) == 1))
+ return 1;
+ if (ff < tf) {
+ start = ff + 1;
+ stop = tf - 1;
+ } else {
+ start = tf + 1;
+ stop = ff - 1;
+ }
+ for (i = start; i <= stop; i++) {
+ if (gs->board[i][fr] != NOPIECE)
+ return 0;
+ }
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+PRIVATE int
+legal_queen_move(game_state_t *gs, int ff, int fr, int tf, int tr)
+{
+ return (legal_rook_move(gs, ff, fr, tf, tr) ||
+ legal_bishop_move(gs, ff, fr, tf, tr));
+}
+
+/*
+ * New one from soso
+ */
+PRIVATE int
+is_square_attacked(game_state_t *gs, int kf, int kr)
+{
+ game_state_t fakeMove;
+
+ fakeMove = *gs;
+ fakeMove.board[4][kr] = NOPIECE;
+ fakeMove.board[kf][kr] = KING | fakeMove.onMove;
+ fakeMove.onMove = CToggle(fakeMove.onMove);
+
+ if (in_check(&fakeMove))
+ return 1;
+ else
+ return 0;
+}
+
+#if 0
+PRIVATE int
+is_square_attacked(game_state_t *gs, int kf, int kr)
+{
+ int f, r;
+
+ gs->onMove = CToggle(gs->onMove);
+
+ for (InitPieceLoop(gs->board, &f, &r, gs->onMove);
+ NextPieceLoop(gs->board, &f, &r, gs->onMove);) {
+ if (legal_move(gs, f, r, kf, kr)) {
+ gs->onMove = CToggle(gs->onMove);
+ return 1;
+ }
+ }
+
+ gs->onMove = CToggle(gs->onMove);
+ return 0;
+}
+#endif
+
+PRIVATE int
+legal_king_move(game_state_t *gs, int ff, int fr, int tf, int tr)
+{
+ if (gs->onMove == WHITE) {
+ /* King side castling */
+ if ((fr == 0) && (tr == 0) && (ff == 4) && (tf == 6) &&
+ (!gs->wkmoved) &&
+ (!gs->wkrmoved) &&
+ (gs->board[5][0] == NOPIECE) &&
+ (gs->board[6][0] == NOPIECE) &&
+ (gs->board[7][0] == W_ROOK) &&
+ (!is_square_attacked(gs, 4, 0)) &&
+ (!is_square_attacked(gs, 5, 0))) {
+ return 1;
+ }
+
+ /* Queen side castling */
+ if ((fr == 0) && (tr == 0) && (ff == 4) && (tf == 2) &&
+ (!gs->wkmoved) &&
+ (!gs->wqrmoved) &&
+ (gs->board[3][0] == NOPIECE) &&
+ (gs->board[2][0] == NOPIECE) &&
+ (gs->board[1][0] == NOPIECE) &&
+ (gs->board[0][0] == W_ROOK) &&
+ (!is_square_attacked(gs, 4, 0)) &&
+ (!is_square_attacked(gs, 3, 0))) {
+ return 1;
+ }
+ } else { /* Black */
+ /* King side castling */
+ if ((fr == 7) && (tr == 7) && (ff == 4) && (tf == 6) &&
+ (!gs->bkmoved) &&
+ (!gs->bkrmoved) &&
+ (gs->board[5][7] == NOPIECE) &&
+ (gs->board[6][7] == NOPIECE) &&
+ (gs->board[7][7] == B_ROOK) &&
+ (!is_square_attacked(gs, 4, 7)) &&
+ (!is_square_attacked(gs, 5, 7))) {
+ return 1;
+ }
+
+ /* Queen side castling */
+ if ((fr == 7) && (tr == 7) && (ff == 4) && (tf == 2) &&
+ (!gs->bkmoved) &&
+ (!gs->bqrmoved) &&
+ (gs->board[3][7] == NOPIECE) &&
+ (gs->board[2][7] == NOPIECE) &&
+ (gs->board[1][7] == NOPIECE) &&
+ (gs->board[0][7] == B_ROOK) &&
+ (!is_square_attacked(gs, 4, 7)) &&
+ (!is_square_attacked(gs, 3, 7))) {
+ return 1;
+ }
+ }
+
+ if (((ff - tf) > 1) || ((tf - ff) > 1))
+ return 0;
+ if (((fr - tr) > 1) || ((tr - fr) > 1))
+ return 0;
+ return 1;
+}
+
+PRIVATE void
+add_pos(int tof, int tor, int *posf, int *posr, int *numpos)
+{
+ posf[*numpos] = tof;
+ posr[*numpos] = tor;
+ (*numpos)++;
+}
+
+PRIVATE void
+possible_pawn_moves(game_state_t *gs,
+ int onf, int onr,
+ int *posf, int *posr,
+ int *numpos)
+{
+ if (gs->onMove == WHITE) {
+ if (gs->board[onf][onr + 1] == NOPIECE) {
+ add_pos(onf, onr + 1, posf, posr, numpos);
+ if ((onr == 1) && (gs->board[onf][onr + 2] == NOPIECE))
+ add_pos(onf, onr + 2, posf, posr, numpos);
+ }
+ if ((onf > 0) &&
+ (gs->board[onf - 1][onr + 1] != NOPIECE) &&
+ (iscolor(gs->board[onf - 1][onr + 1], BLACK)))
+ add_pos(onf - 1, onr + 1, posf, posr, numpos);
+ if ((onf < 7) &&
+ (gs->board[onf + 1][onr + 1] != NOPIECE) &&
+ (iscolor(gs->board[onf + 1][onr + 1], BLACK)))
+ add_pos(onf + 1, onr + 1, posf, posr, numpos);
+ if (gs->ep_possible[0][onf] == -1)
+ add_pos(onf - 1, onr + 1, posf, posr, numpos);
+ if (gs->ep_possible[0][onf] == 1)
+ add_pos(onf + 1, onr + 1, posf, posr, numpos);
+ } else {
+ if (gs->board[onf][onr - 1] == NOPIECE) {
+ add_pos(onf, onr - 1, posf, posr, numpos);
+ if ((onr == 6) && (gs->board[onf][onr - 2] == NOPIECE))
+ add_pos(onf, onr - 2, posf, posr, numpos);
+ }
+ if ((onf > 0) &&
+ (gs->board[onf - 1][onr - 1] != NOPIECE) &&
+ (iscolor(gs->board[onf - 1][onr - 1], WHITE)))
+ add_pos(onf - 1, onr - 1, posf, posr, numpos);
+ if ((onf < 7) &&
+ (gs->board[onf + 1][onr - 1] != NOPIECE) &&
+ (iscolor(gs->board[onf + 1][onr - 1], WHITE)))
+ add_pos(onf + 1, onr - 1, posf, posr, numpos);
+ if (gs->ep_possible[1][onf] == -1)
+ add_pos(onf - 1, onr - 1, posf, posr, numpos);
+ if (gs->ep_possible[1][onf] == 1)
+ add_pos(onf + 1, onr - 1, posf, posr, numpos);
+ }
+}
+
+PRIVATE void
+possible_knight_moves(game_state_t *gs,
+ int onf, int onr,
+ int *posf, int *posr,
+ int *numpos)
+{
+ int f, r;
+ int j;
+ static int knightJumps[8][2] = {
+ {-1, 2},
+ {1, 2},
+ {2, -1},
+ {2, 1},
+ {-1, -2},
+ {1, -2},
+ {-2, 1},
+ {-2, -1}
+ };
+
+ for (j = 0; j < 8; j++) {
+ f = knightJumps[j][0] + onf;
+ r = knightJumps[j][1] + onr;
+
+ if ((f < 0) || (f > 7))
+ continue;
+ if ((r < 0) || (r > 7))
+ continue;
+ if ((gs->board[f][r] == NOPIECE) ||
+ (iscolor(gs->board[f][r], CToggle(gs->onMove))))
+ add_pos(f, r, posf, posr, numpos);
+ }
+}
+
+PRIVATE void
+possible_bishop_moves(game_state_t *gs,
+ int onf, int onr,
+ int *posf, int *posr,
+ int *numpos)
+{
+ int f, r;
+
+ /* Up Left */
+ f = onf;
+ r = onr;
+ while (1) {
+ f--;
+ r++;
+ if ((f < 0) || (f > 7))
+ break;
+ if ((r < 0) || (r > 7))
+ break;
+ if ((gs->board[f][r] != NOPIECE) &&
+ (iscolor(gs->board[f][r], gs->onMove)))
+ break;
+ add_pos(f, r, posf, posr, numpos);
+ if (gs->board[f][r] != NOPIECE)
+ break;
+ }
+
+ /* Up Right */
+ f = onf;
+ r = onr;
+ while (1) {
+ f++;
+ r++;
+ if ((f < 0) || (f > 7))
+ break;
+ if ((r < 0) || (r > 7))
+ break;
+ if ((gs->board[f][r] != NOPIECE) &&
+ (iscolor(gs->board[f][r], gs->onMove)))
+ break;
+ add_pos(f, r, posf, posr, numpos);
+ if (gs->board[f][r] != NOPIECE)
+ break;
+ }
+
+ /* Down Left */
+ f = onf;
+ r = onr;
+ while (1) {
+ f--;
+ r--;
+ if ((f < 0) || (f > 7))
+ break;
+ if ((r < 0) || (r > 7))
+ break;
+ if ((gs->board[f][r] != NOPIECE) &&
+ (iscolor(gs->board[f][r], gs->onMove)))
+ break;
+ add_pos(f, r, posf, posr, numpos);
+ if (gs->board[f][r] != NOPIECE)
+ break;
+ }
+
+ /* Down Right */
+ f = onf;
+ r = onr;
+ while (1) {
+ f++;
+ r--;
+ if ((f < 0) || (f > 7))
+ break;
+ if ((r < 0) || (r > 7))
+ break;
+ if ((gs->board[f][r] != NOPIECE) &&
+ (iscolor(gs->board[f][r], gs->onMove)))
+ break;
+ add_pos(f, r, posf, posr, numpos);
+ if (gs->board[f][r] != NOPIECE)
+ break;
+ }
+}
+
+PRIVATE void
+possible_rook_moves(game_state_t *gs,
+ int onf, int onr,
+ int *posf, int *posr,
+ int *numpos)
+{
+ int f, r;
+
+ /* Left */
+ f = onf;
+ r = onr;
+ while (1) {
+ f--;
+ if ((f < 0) || (f > 7))
+ break;
+ if ((r < 0) || (r > 7))
+ break;
+ if ((gs->board[f][r] != NOPIECE) &&
+ (iscolor(gs->board[f][r], gs->onMove)))
+ break;
+ add_pos(f, r, posf, posr, numpos);
+ if (gs->board[f][r] != NOPIECE)
+ break;
+ }
+
+ /* Right */
+ f = onf;
+ r = onr;
+ while (1) {
+ f++;
+ if ((f < 0) || (f > 7))
+ break;
+ if ((r < 0) || (r > 7))
+ break;
+ if ((gs->board[f][r] != NOPIECE) &&
+ (iscolor(gs->board[f][r], gs->onMove)))
+ break;
+ add_pos(f, r, posf, posr, numpos);
+ if (gs->board[f][r] != NOPIECE)
+ break;
+ }
+
+ /* Up */
+ f = onf;
+ r = onr;
+ while (1) {
+ r++;
+ if ((f < 0) || (f > 7))
+ break;
+ if ((r < 0) || (r > 7))
+ break;
+ if ((gs->board[f][r] != NOPIECE) &&
+ (iscolor(gs->board[f][r], gs->onMove)))
+ break;
+ add_pos(f, r, posf, posr, numpos);
+ if (gs->board[f][r] != NOPIECE)
+ break;
+ }
+
+ /* Down */
+ f = onf;
+ r = onr;
+ while (1) {
+ r--;
+ if ((f < 0) || (f > 7))
+ break;
+ if ((r < 0) || (r > 7))
+ break;
+ if ((gs->board[f][r] != NOPIECE) &&
+ (iscolor(gs->board[f][r], gs->onMove)))
+ break;
+ add_pos(f, r, posf, posr, numpos);
+ if (gs->board[f][r] != NOPIECE)
+ break;
+ }
+}
+
+PRIVATE void
+possible_queen_moves(game_state_t *gs,
+ int onf, int onr,
+ int *posf, int *posr,
+ int *numpos)
+{
+ possible_rook_moves(gs, onf, onr, posf, posr, numpos);
+ possible_bishop_moves(gs, onf, onr, posf, posr, numpos);
+}
+
+PRIVATE void
+possible_king_moves(game_state_t *gs,
+ int onf, int onr,
+ int *posf, int *posr,
+ int *numpos)
+{
+ int f, r;
+ int j;
+ static int kingJumps[8][2] = {
+ {-1, -1},
+ {0, -1},
+ {1, -1},
+ {-1, 1},
+ {0, 1},
+ {1, 1},
+ {-1, 0},
+ {1, 0}
+ };
+
+ for (j = 0; j < 8; j++) {
+ f = kingJumps[j][0] + onf;
+ r = kingJumps[j][1] + onr;
+
+ if ((f < 0) || (f > 7))
+ continue;
+ if ((r < 0) || (r > 7))
+ continue;
+ if ((gs->board[f][r] == NOPIECE) ||
+ (iscolor(gs->board[f][r], CToggle(gs->onMove))))
+ add_pos(f, r, posf, posr, numpos);
+ }
+}
+
+/* Doesn't check for check */
+PUBLIC int
+legal_move(game_state_t *gs,
+ int fFile, int fRank,
+ int tFile, int tRank)
+{
+ int legal;
+ int move_piece;
+
+ if (fFile == ALG_DROP) {
+ move_piece = fRank;
+
+ if (move_piece == KING)
+ return 0;
+ if (gs->holding[gs->onMove == WHITE ? 0 : 1][move_piece - 1] == 0)
+ return 0;
+ if (gs->board[tFile][tRank] != NOPIECE)
+ return 0;
+ if (move_piece == PAWN && (tRank == 0 || tRank == 7))
+ return 0;
+
+ return 1;
+ } else {
+ move_piece = piecetype(gs->board[fFile][fRank]);
+ }
+
+ if (gs->board[fFile][fRank] == NOPIECE)
+ return 0;
+ if (!iscolor(gs->board[fFile][fRank], gs->onMove)) // Wrong color
+ return 0;
+ if ((gs->board[tFile][tRank] != NOPIECE) &&
+ iscolor(gs->board[tFile][tRank], gs->onMove)) // Can't capture own
+ return 0;
+ if ((fFile == tFile) && (fRank == tRank)) // Same square
+ return 0;
+
+ switch (move_piece) {
+ case PAWN:
+ legal = legal_pawn_move(gs, fFile, fRank, tFile, tRank);
+ break;
+ case KNIGHT:
+ legal = legal_knight_move(gs, fFile, fRank, tFile, tRank);
+ break;
+ case BISHOP:
+ legal = legal_bishop_move(gs, fFile, fRank, tFile, tRank);
+ break;
+ case ROOK:
+ legal = legal_rook_move(gs, fFile, fRank, tFile, tRank);
+ break;
+ case QUEEN:
+ legal = legal_queen_move(gs, fFile, fRank, tFile, tRank);
+ break;
+ case KING:
+ legal = legal_king_move(gs, fFile, fRank, tFile, tRank);
+ break;
+ default:
+ return 0;
+ break;
+ }
+
+ return legal;
+}
+
+/*
+ * This fills in the rest of the mt structure once it is determined
+ * that. (Returns 'MOVE_ILLEGAL' if move leaves you in check.)
+ */
+PRIVATE int
+move_calculate(game_state_t *gs, move_t *mt, int promote)
+{
+ game_state_t fakeMove;
+ int ret, too_long;
+
+ mt->pieceCaptured = gs->board[mt->toFile][mt->toRank];
+ mt->enPassant = 0; // Don't know yet,
+ // let execute move take care of it
+
+ if (mt->fromFile == ALG_DROP) {
+ mt->piecePromotionTo = NOPIECE;
+ ret = snprintf(mt->moveString, sizeof mt->moveString,
+ "%s/%c%c-%c%d",
+ wpstring[mt->fromRank],
+ DROP_CHAR,
+ DROP_CHAR,
+ (mt->toFile + 'a'),
+ (mt->toRank + 1));
+
+ too_long = (ret < 0 || (size_t)ret >= sizeof mt->moveString);
+
+ if (too_long) { /* XXX */
+ fprintf(stderr, "FICS: %s: warning: "
+ "snprintf truncated\n", __func__);
+ }
+ } else {
+ if (piecetype(gs->board[mt->fromFile][mt->fromRank]) == PAWN &&
+ (mt->toRank == 0 || mt->toRank == 7)) {
+ mt->piecePromotionTo = (promote |
+ colorval(gs->board[mt->fromFile][mt->fromRank]));
+ } else {
+ mt->piecePromotionTo = NOPIECE;
+ }
+
+ if (piecetype(gs->board[mt->fromFile][mt->fromRank]) == PAWN &&
+ (mt->fromRank - mt->toRank == 2 ||
+ mt->toRank - mt->fromRank == 2)) {
+ mt->doublePawn = mt->fromFile;
+ } else {
+ mt->doublePawn = -1;
+ }
+
+ if (piecetype(gs->board[mt->fromFile][mt->fromRank]) == KING &&
+ mt->fromFile == 4 &&
+ mt->toFile == 2) {
+ strlcpy(mt->moveString, "o-o-o", sizeof mt->moveString);
+ } else if (piecetype(gs->board[mt->fromFile][mt->fromRank]) == KING &&
+ mt->fromFile == 4 &&
+ mt->toFile == 6) {
+ strlcpy(mt->moveString, "o-o", sizeof mt->moveString);
+ } else {
+ ret = snprintf(mt->moveString, sizeof mt->moveString,
+ "%s/%c%d-%c%d",
+ wpstring[piecetype(gs->board[mt->fromFile][mt->fromRank])],
+ (mt->fromFile + 'a'),
+ (mt->fromRank + 1),
+ (mt->toFile + 'a'),
+ (mt->toRank + 1));
+
+ too_long = (ret < 0 || (size_t)ret >= sizeof mt->moveString);
+
+ if (too_long) { /* XXX */
+ fprintf(stderr, "FICS: %s: warning: "
+ "snprintf truncated\n", __func__);
+ }
+ }
+ }
+
+ // Replace this with an algabraic de-parser
+ snprintf(mt->algString, sizeof mt->algString, "%s",
+ alg_unparse(gs, mt));
+ fakeMove = *gs;
+ execute_move(&fakeMove, mt, 0); // Calculates enPassant also
+
+ // Does making this move leave ME in check?
+ if (in_check(&fakeMove))
+ return MOVE_ILLEGAL;
+ // IanO: bughouse variants: drop cannot be check/checkmate
+
+ return MOVE_OK;
+}
+
+PUBLIC int
+legal_andcheck_move(game_state_t *gs,
+ int fFile, int fRank,
+ int tFile, int tRank)
+{
+ move_t mt;
+
+ if (!legal_move(gs, fFile, fRank, tFile, tRank))
+ return 0;
+
+ mt.color = gs->onMove;
+ mt.fromFile = fFile;
+ mt.fromRank = fRank;
+ mt.toFile = tFile;
+ mt.toRank = tRank;
+
+ /*
+ * This should take into account a pawn promoting to another
+ * piece.
+ */
+ if (move_calculate(gs, &mt, QUEEN) == MOVE_OK)
+ return 1;
+ else
+ return 0;
+}
+
+PUBLIC int
+in_check(game_state_t *gs)
+{
+ int f, r;
+ int kf = -1, kr = -1;
+
+ /* Find the king */
+ if (gs->onMove == WHITE) {
+ for (f = 0; f < 8 && kf < 0; f++) {
+ for (r = 0; r < 8 && kf < 0; r++) {
+ if (gs->board[f][r] == B_KING) {
+ kf = f;
+ kr = r;
+ }
+ }
+ }
+ } else {
+ for (f = 0; f < 8 && kf < 0; f++) {
+ for (r = 0; r < 8 && kf < 0; r++) {
+ if (gs->board[f][r] == W_KING) {
+ kf = f;
+ kr = r;
+ }
+ }
+ }
+ }
+
+ if (kf < 0) {
+ fprintf(stderr, "FICS: Error game with no king!\n");
+ return 0;
+ }
+
+ for (InitPieceLoop(gs->board, &f, &r, gs->onMove);
+ NextPieceLoop(gs->board, &f, &r, gs->onMove);) {
+ if (legal_move(gs, f, r, kf, kr)) // In Check?
+ return 1;
+ }
+
+ return 0;
+}
+
+PRIVATE int
+has_legal_move(game_state_t *gs)
+{
+ int f, r;
+ int i;
+ int kf = 0, kr = -1;
+ int kf_and_kr_set = 0;
+ int numpossible = 0;
+ int possiblef[500];
+ int possibler[500];
+
+ for (InitPieceLoop(gs->board, &f, &r, gs->onMove);
+ NextPieceLoop(gs->board, &f, &r, gs->onMove);) {
+ switch (piecetype(gs->board[f][r])) {
+ case PAWN:
+ possible_pawn_moves(gs, f, r, possiblef, possibler,
+ &numpossible);
+ break;
+ case KNIGHT:
+ possible_knight_moves(gs, f, r, possiblef, possibler,
+ &numpossible);
+ break;
+ case BISHOP:
+ possible_bishop_moves(gs, f, r, possiblef, possibler,
+ &numpossible);
+ break;
+ case ROOK:
+ possible_rook_moves(gs, f, r, possiblef, possibler,
+ &numpossible);
+ break;
+ case QUEEN:
+ possible_queen_moves(gs, f, r, possiblef, possibler,
+ &numpossible);
+ break;
+ case KING:
+ kf = f;
+ kr = r;
+ kf_and_kr_set = 1;
+ possible_king_moves(gs, f, r, possiblef, possibler,
+ &numpossible);
+ break;
+ }
+ if (numpossible >= 500) {
+ fprintf(stderr, "FICS: Possible move overrun\n");
+ return 0;
+ }
+ for (i = 0; i < numpossible; i++) {
+ if (legal_andcheck_move(gs, f, r, possiblef[i],
+ possibler[i]))
+ return 1;
+ }
+ }
+
+ if (!kf_and_kr_set && kf == 0 && kr == -1) {
+ fprintf(stderr, "FICS: %s: 'kf_and_kr_set' is 0\n", __func__);
+ return 0;
+ }
+
+ // IanO: if we got here, then kf and kr must be set
+ if (gs->gameNum >= 0 && garray[gs->gameNum].link >= 0) {
+ // bughouse: potential drops as check interpositions
+ gs->holding[gs->onMove == WHITE ? 0 : 1][QUEEN - 1]++;
+ for (f = kf - 1; f <= kf + 1; f++) {
+ for (r = kr - 1; r <= kr + 1; r++) {
+ if (f >= 0 &&
+ f < 8 &&
+ r >= 0 &&
+ r < 8 &&
+ gs->board[f][r] == NOPIECE) {
+ // try a drop next to the king
+ if (legal_andcheck_move(gs, ALG_DROP,
+ QUEEN, f, r)) {
+ gs->holding[gs->onMove == WHITE
+ ? 0 : 1][QUEEN - 1]--;
+ return 1;
+ }
+ }
+ }
+ }
+
+ gs->holding[gs->onMove == WHITE ? 0 : 1][QUEEN - 1]--;
+ }
+
+ fprintf(stderr, "FICS: NO LEGAL MOVE!\n");
+ return 0;
+}
+
+/* This will end up being a very complicated function */
+PUBLIC int
+parse_move(char *mstr, game_state_t *gs, move_t *mt, int promote)
+{
+ int result;
+ int type = is_move(mstr);
+
+ mt->color = gs->onMove;
+
+ switch (type) {
+ case MS_NOTMOVE:
+ return MOVE_ILLEGAL;
+ break;
+ case MS_COMP:
+ mt->fromFile = mstr[0] - 'a';
+ mt->fromRank = mstr[1] - '1';
+ mt->toFile = mstr[2] - 'a';
+ mt->toRank = mstr[3] - '1';
+ break;
+ case MS_COMPDASH:
+ mt->fromFile = mstr[0] - 'a';
+ mt->fromRank = mstr[1] - '1';
+ mt->toFile = mstr[3] - 'a';
+ mt->toRank = mstr[4] - '1';
+ break;
+ case MS_KCASTLE:
+ mt->fromFile = 4;
+ mt->toFile = 6;
+ if (gs->onMove == WHITE) {
+ mt->fromRank = 0;
+ mt->toRank = 0;
+ } else {
+ mt->fromRank = 7;
+ mt->toRank = 7;
+ }
+ break;
+ case MS_QCASTLE:
+ mt->fromFile = 4;
+ mt->toFile = 2;
+ if (gs->onMove == WHITE) {
+ mt->fromRank = 0;
+ mt->toRank = 0;
+ } else {
+ mt->fromRank = 7;
+ mt->toRank = 7;
+ }
+ break;
+ case MS_ALG:
+ // Fills in the mt structure
+ if ((result = alg_parse_move(mstr, gs, mt)) != MOVE_OK)
+ return result;
+ break;
+ default:
+ return MOVE_ILLEGAL;
+ break;
+ }
+
+ if (!legal_move(gs, mt->fromFile, mt->fromRank, mt->toFile, mt->toRank))
+ return MOVE_ILLEGAL;
+ return move_calculate(gs, mt, promote);
+}
+
+/*
+ * Returns 'MOVE_OK', 'MOVE_NOMATERIAL', 'MOVE_CHECKMATE', or
+ * 'MOVE_STALEMATE'.
+ *
+ * ('check_game_status' prevents recursion.)
+ */
+PUBLIC int
+execute_move(game_state_t *gs, move_t *mt, int check_game_status)
+{
+ int i, j, foobar;
+ int movedPiece;
+ int tookPiece;
+
+ if (mt->fromFile == ALG_DROP) {
+ movedPiece = mt->fromRank;
+// tookPiece = NOPIECE;
+
+ gs->holding[gs->onMove == WHITE ? 0 : 1][movedPiece-1]--;
+ gs->board[mt->toFile][mt->toRank] = (movedPiece | gs->onMove);
+
+ if (gs->gameNum >= 0)
+ gs->lastIrreversable = garray[gs->gameNum].numHalfMoves;
+ } else {
+ movedPiece = gs->board[mt->fromFile][mt->fromRank];
+ tookPiece = gs->board[mt->toFile][mt->toRank];
+
+ if (mt->piecePromotionTo == NOPIECE) {
+ gs->board[mt->toFile][mt->toRank] =
+ gs->board[mt->fromFile][mt->fromRank];
+ } else {
+ gs->board[mt->toFile][mt->toRank] =
+ (mt->piecePromotionTo | gs->onMove);
+ }
+
+ gs->board[mt->fromFile][mt->fromRank] = NOPIECE;
+
+ /*
+ * Check if irreversable
+ */
+ if (piecetype(movedPiece) == PAWN || tookPiece != NOPIECE) {
+ if (gs->gameNum >= 0) {
+ gs->lastIrreversable =
+ garray[gs->gameNum].numHalfMoves;
+ }
+ }
+
+ /*
+ * Check if this move is en-passant
+ */
+ if (piecetype(movedPiece) == PAWN &&
+ mt->fromFile != mt->toFile &&
+ tookPiece == NOPIECE) {
+ if (gs->onMove == WHITE)
+ mt->pieceCaptured = B_PAWN;
+ else
+ mt->pieceCaptured = W_PAWN;
+
+ if (mt->fromFile > mt->toFile)
+ mt->enPassant = -1;
+ else
+ mt->enPassant = 1;
+ gs->board[mt->toFile][mt->fromRank] = NOPIECE;
+ }
+
+ /*
+ * Check en-passant flags for next moves
+ */
+ for (i = 0; i < 8; i++) {
+ gs->ep_possible[0][i] = 0;
+ gs->ep_possible[1][i] = 0;
+ }
+
+ if (piecetype(movedPiece) == PAWN &&
+ (mt->fromRank == (mt->toRank + 2) ||
+ (mt->fromRank + 2) == mt->toRank)) {
+ /*
+ * Should turn on enpassent flag if possible.
+ */
+
+ if (gs->onMove == WHITE) {
+ if (mt->toFile < 7 &&
+ gs->board[mt->toFile + 1][3] == B_PAWN)
+ gs->ep_possible[1][mt->toFile + 1] = -1;
+ if ((mt->toFile - 1) >= 0 &&
+ gs->board[mt->toFile - 1][3] == B_PAWN)
+ gs->ep_possible[1][mt->toFile - 1] = 1;
+ } else {
+ if (mt->toFile < 7 &&
+ gs->board[mt->toFile + 1][4] == W_PAWN)
+ gs->ep_possible[0][mt->toFile + 1] = -1;
+ if ((mt->toFile - 1) >= 0 &&
+ gs->board[mt->toFile - 1][4] == W_PAWN)
+ gs->ep_possible[0][mt->toFile - 1] = 1;
+ }
+ }
+
+ if (piecetype(movedPiece) == ROOK && mt->fromFile == 0) {
+ if (mt->fromRank == 0 && gs->onMove == WHITE)
+ gs->wqrmoved = 1;
+ if (mt->fromRank == 7 && gs->onMove == BLACK)
+ gs->bqrmoved = 1;
+ }
+
+ if (piecetype(movedPiece) == ROOK && mt->fromFile == 7) {
+ if (mt->fromRank == 0 && gs->onMove == WHITE)
+ gs->wkrmoved = 1;
+ if (mt->fromRank == 7 && gs->onMove == BLACK)
+ gs->bkrmoved = 1;
+ }
+
+ if (piecetype(movedPiece) == KING) {
+ if (gs->onMove == WHITE)
+ gs->wkmoved = 1;
+ else
+ gs->bkmoved = 1;
+ }
+
+ if (piecetype(movedPiece) == KING &&
+ (mt->fromFile == 4 && mt->toFile == 6)) { // Check for KS
+ // castling
+ gs->board[5][mt->toRank] = gs->board[7][mt->toRank];
+ gs->board[7][mt->toRank] = NOPIECE;
+ }
+
+ if (piecetype(movedPiece) == KING &&
+ (mt->fromFile == 4 && mt->toFile == 2)) { // Check for QS
+ // castling
+ gs->board[3][mt->toRank] = gs->board[0][mt->toRank];
+ gs->board[0][mt->toRank] = NOPIECE;
+ }
+ }
+
+ if (gs->onMove == BLACK)
+ gs->moveNum++;
+
+ if (check_game_status) {
+ /*
+ * Does this move result in check?
+ */
+
+ if (in_check(gs)) {
+ /*
+ * Check for checkmate
+ */
+
+ gs->onMove = CToggle(gs->onMove);
+
+ if (!has_legal_move(gs))
+ return MOVE_CHECKMATE;
+ } else {
+ /*
+ * Check for stalemate
+ */
+
+ gs->onMove = CToggle(gs->onMove);
+
+ if (!has_legal_move(gs))
+ return MOVE_STALEMATE;
+
+ /*
+ * loon: check for insufficient mating material
+ */
+
+ foobar = 0;
+
+ for (i = 0; i < 8; i++) {
+ for (j = 0; j < 8; j++) {
+ switch (piecetype(gs->board[i][j])) {
+ case KNIGHT:
+ case BISHOP:
+ foobar++;
+ break;
+ case KING:
+ case NOPIECE:
+ break;
+ default:
+ foobar = 2;
+ break;
+ }
+ }
+ }
+
+ if (foobar < 2)
+ return MOVE_NOMATERIAL;
+ }
+ } else {
+ gs->onMove = CToggle(gs->onMove);
+ }
+
+ return MOVE_OK;
+}
+
+#ifdef TIMESEAL
+static void
+backup_move_timeseal(int g, move_t *m)
+{
+ if (m->color == WHITE) {
+ if (con[parray[garray[g].white].socket].timeseal) {
+ garray[g].wRealTime += (m->tookTime * 100);
+ garray[g].wRealTime -= (garray[g].wIncrement * 100);
+ garray[g].wTime = garray[g].wRealTime / 100;
+
+ if (con[parray[garray[g].black].socket].timeseal) {
+ garray[g].bTime = garray[g].bRealTime / 100;
+ } else {
+ garray[g].bTime += (garray[g].lastDecTime -
+ garray[g].lastMoveTime);
+ }
+ } else { // white has no timeseal
+ garray[g].wTime += m->tookTime;
+ garray[g].wTime -= garray[g].wIncrement;
+
+ if (con[parray[garray[g].black].socket].timeseal) {
+ garray[g].bTime = garray[g].bRealTime / 100;
+ } else {
+ garray[g].bTime += (garray[g].lastDecTime -
+ garray[g].lastMoveTime);
+ }
+ }
+ } else {
+ if (con[parray[garray[g].black].socket].timeseal) {
+ garray[g].bRealTime += (m->tookTime * 100);
+ garray[g].bRealTime -= (garray[g].wIncrement * 100);
+ garray[g].bTime = garray[g].bRealTime / 100;
+
+ if (con[parray[garray[g].white].socket].timeseal) {
+ garray[g].wTime = garray[g].wRealTime / 100;
+ } else {
+ garray[g].wTime += (garray[g].lastDecTime -
+ garray[g].lastMoveTime);
+ }
+ } else { // black has no timeseal
+ garray[g].bTime += m->tookTime;
+
+ if (!garray[g].bIncrement)
+ garray[g].bTime -= garray[g].wIncrement;
+ else
+ garray[g].bTime -= garray[g].bIncrement;
+ if (con[parray[garray[g].white].socket].timeseal) {
+ garray[g].wTime = garray[g].wRealTime / 100;
+ } else {
+ garray[g].wTime += (garray[g].lastDecTime -
+ garray[g].lastMoveTime);
+ }
+ }
+ }
+}
+#endif // TIMESEAL
+
+PUBLIC int
+backup_move(int g, int mode)
+{
+ game_state_t *gs;
+ int now, i;
+ move_t *m, *m1;
+
+ if (garray[g].link >= 0) // IanO: not implemented for bughouse yet
+ return MOVE_ILLEGAL;
+ if (garray[g].numHalfMoves < 1)
+ return MOVE_ILLEGAL;
+ gs = &garray[g].game_state;
+ m = (mode == REL_GAME)
+ ? &garray[g].moveList[garray[g].numHalfMoves - 1]
+ : &garray[g].examMoveList[garray[g].numHalfMoves - 1];
+ if (m->toFile < 0)
+ return MOVE_ILLEGAL;
+
+ gs->board[m->fromFile][m->fromRank] = gs->board[m->toFile][m->toRank];
+
+ if (m->piecePromotionTo != NOPIECE) {
+ gs->board[m->fromFile][m->fromRank] = PAWN |
+ colorval(gs->board[m->fromFile][m->fromRank]);
+ }
+
+ /*
+ * When takeback a _first_ move of rook, the ??rmoved variable
+ * must be cleared.
+ * To check, if the move is first, we should scan moveList.
+ */
+ if (piecetype(gs->board[m->fromFile][m->fromRank]) == ROOK) {
+ if (m->color == WHITE) {
+ if ((m->fromFile == 0) && (m->fromRank == 0)) {
+ for (i = 2; i < garray[g].numHalfMoves - 1;
+ i += 2) {
+ m1 = (mode == REL_GAME)
+ ? &garray[g].moveList[i]
+ : &garray[g].examMoveList[i];
+
+ if ((m1->fromFile == 0) &&
+ (m1->fromRank == 0))
+ break;
+ }
+
+ if (i == garray[g].numHalfMoves - 1)
+ gs->wqrmoved = 0;
+ }
+
+ if ((m->fromFile == 7) && (m->fromRank == 0)) {
+ for (i = 2; i < garray[g].numHalfMoves - 1;
+ i += 2) {
+ m1 = (mode == REL_GAME)
+ ? &garray[g].moveList[i]
+ : &garray[g].examMoveList[i];
+
+ if ((m1->fromFile == 7) &&
+ (m1->fromRank == 0))
+ break;
+ }
+
+ if (i == garray[g].numHalfMoves - 1)
+ gs->wkrmoved = 0;
+ }
+ } else {
+ if ((m->fromFile == 0) && (m->fromRank == 7)) {
+ for (i = 3; i < garray[g].numHalfMoves - 1;
+ i += 2) {
+ m1 = (mode == REL_GAME)
+ ? &garray[g].moveList[i]
+ : &garray[g].examMoveList[i];
+
+ if ((m1->fromFile == 0) &&
+ (m1->fromRank == 0))
+ break;
+ }
+
+ if (i == garray[g].numHalfMoves - 1)
+ gs->bqrmoved = 0;
+ }
+
+ if ((m->fromFile == 7) && (m->fromRank == 7)) {
+ for (i = 3; i < garray[g].numHalfMoves - 1;
+ i += 2) {
+ m1 = (mode == REL_GAME)
+ ? &garray[g].moveList[i]
+ : &garray[g].examMoveList[i];
+
+ if ((m1->fromFile == 7) &&
+ (m1->fromRank == 0))
+ break;
+ }
+
+ if (i == garray[g].numHalfMoves - 1)
+ gs->bkrmoved = 0;
+ }
+ }
+ }
+
+ if (piecetype(gs->board[m->fromFile][m->fromRank]) == KING) {
+ gs->board[m->toFile][m->toRank] = m->pieceCaptured;
+
+ if (m->toFile - m->fromFile == 2) {
+ gs->board[7][m->fromRank] = ROOK |
+ colorval(gs->board[m->fromFile][m->fromRank]);
+ gs->board[5][m->fromRank] = NOPIECE;
+
+ /*
+ * If takeback a castling, the appropriates
+ * ??moved variables must be cleared.
+ */
+ if (m->color == WHITE) {
+ gs->wkmoved = 0;
+ gs->wkrmoved = 0;
+ } else {
+ gs->bkmoved = 0;
+ gs->bkrmoved = 0;
+ }
+ goto cleanupMove;
+ }
+
+ if (m->fromFile - m->toFile == 2) {
+ gs->board[0][m->fromRank] = ROOK |
+ colorval(gs->board[m->fromFile][m->fromRank]);
+ gs->board[3][m->fromRank] = NOPIECE;
+
+ /*
+ * If takeback a castling, the appropriate
+ * ??moved variables must be cleared.
+ */
+ if (m->color == WHITE) {
+ gs->wkmoved = 0;
+ gs->wqrmoved = 0;
+ } else {
+ gs->bkmoved = 0;
+ gs->bqrmoved = 0;
+ }
+ goto cleanupMove;
+ }
+
+ /*
+ * When takeback a _first_ move of king (not the castling),
+ * the ?kmoved variable must be cleared.
+ * To check, if the move is first, we should scan moveList.
+ */
+ if (m->color == WHITE) {
+ if ((m->fromFile == 4) && (m->fromRank == 0)) {
+ for (i = 2; i < garray[g].numHalfMoves - 1;
+ i += 2) {
+ m1 = (mode == REL_GAME)
+ ? &garray[g].moveList[i]
+ : &garray[g].examMoveList[i];
+
+ if ((m1->fromFile == 4) &&
+ (m1->fromRank == 0))
+ break;
+ }
+
+ if (i == garray[g].numHalfMoves - 1)
+ gs->wkmoved = 0;
+ }
+ } else {
+ if ((m->fromFile == 4) && (m->fromRank == 7)) {
+ for (i = 3; i < garray[g].numHalfMoves - 1;
+ i += 2) {
+ m1 = (mode == REL_GAME)
+ ? &garray[g].moveList[i]
+ : &garray[g].examMoveList[i];
+
+ if ((m1->fromFile == 4) &&
+ (m1->fromRank == 7))
+ break;
+ }
+
+ if (i == garray[g].numHalfMoves - 1)
+ gs->bkmoved = 0;
+ }
+ }
+ }
+
+ if (m->enPassant) { // Do enPassant
+ gs->board[m->toFile][m->fromRank] = PAWN |
+ (colorval(gs->board[m->fromFile][m->fromRank]) == WHITE
+ ? BLACK
+ : WHITE);
+ gs->board[m->toFile][m->toRank] = NOPIECE;
+ /*
+ * Should set the enpassant array, but I don't care
+ * right now.
+ */
+ goto cleanupMove;
+ }
+
+ gs->board[m->toFile][m->toRank] = m->pieceCaptured;
+
+ cleanupMove:
+
+ if (garray[g].status != GAME_EXAMINE)
+ game_update_time(g);
+
+ garray[g].numHalfMoves--;
+
+ if (garray[g].status != GAME_EXAMINE) {
+ if (garray[g].wInitTime) { // Don't update times in untimed
+ // games
+ now = tenth_secs();
+
+#ifdef TIMESEAL
+ backup_move_timeseal(g, m);
+#else
+ if (m->color == WHITE) {
+ garray[g].wTime += m->tookTime;
+ garray[g].wTime = (garray[g].wTime -
+ garray[g].wIncrement);
+ garray[g].bTime += (garray[g].lastDecTime -
+ garray[g].lastMoveTime);
+ } else {
+ garray[g].bTime += m->tookTime;
+
+ if (!garray[g].bIncrement) {
+ garray[g].bTime = (garray[g].bTime -
+ garray[g].wIncrement);
+ } else {
+ garray[g].bTime = (garray[g].bTime -
+ garray[g].bIncrement);
+ }
+
+ garray[g].wTime += (garray[g].lastDecTime -
+ garray[g].lastMoveTime);
+ }
+#endif
+
+ if (garray[g].numHalfMoves == 0)
+ garray[g].timeOfStart = now;
+ garray[g].lastMoveTime = now;
+ garray[g].lastDecTime = now;
+ }
+ }
+
+ if (gs->onMove == BLACK) {
+ gs->onMove = WHITE;
+ } else {
+ gs->onMove = BLACK;
+ gs->moveNum--;
+ }
+
+ /*
+ * Takeback of last move is done already, it's time to update
+ * enpassant array.
+ * (Patch from Soso, added by Sparky 3/17/95)
+ */
+ if (garray[g].numHalfMoves > 0) {
+ m1 = (mode == REL_GAME)
+ ? &garray[g].moveList[garray[g].numHalfMoves - 1]
+ : &garray[g].examMoveList[garray[g].numHalfMoves - 1];
+
+ if (piecetype(gs->board[m1->toFile][m1->toRank]) == PAWN) {
+ if ((m1->toRank - m1->fromRank) == 2) {
+ if ((m1->toFile < 7) &&
+ gs->board[m1->toFile + 1][3] == B_PAWN) {
+ gs->ep_possible[1][m1->toFile + 1] = -1;
+ }
+ if ((m1->toFile - 1 >= 0) &&
+ gs->board[m1->toFile - 1][3] == B_PAWN) {
+ gs->ep_possible[1][m1->toFile - 1] = 1;
+ }
+ }
+ if ((m1->toRank - m1->fromRank) == -2) {
+ if ((m1->toFile < 7) &&
+ gs->board[m1->toFile + 1][4] == W_PAWN) {
+ gs->ep_possible[0][m1->toFile + 1] = -1;
+ }
+ if ((m1->toFile - 1 >= 0) &&
+ gs->board[m1->toFile - 1][4] == W_PAWN) {
+ gs->ep_possible[0][m1->toFile - 1] = 1;
+ }
+ }
+ }
+ }
+
+ return MOVE_OK;
+}