diff options
author | Markus Uhlin <markus@nifty-networks.net> | 2025-09-15 18:50:32 +0200 |
---|---|---|
committer | Markus Uhlin <markus@nifty-networks.net> | 2025-09-15 18:50:32 +0200 |
commit | c3eee8e333866d92e5fd94ae83cef618758c11bb (patch) | |
tree | 234a06fd90bd61a6668490a0cbf8870e6c674b81 /FICS/movecheck.c |
FICS RPBLC v1.4.61.4.6
Diffstat (limited to 'FICS/movecheck.c')
-rw-r--r-- | FICS/movecheck.c | 1608 |
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; +} |