From 556a5094b9add8a0930ff74ab884972982303d4f Mon Sep 17 00:00:00 2001 From: Ben Kibbey Date: Thu, 13 Apr 2006 12:38:21 -0400 Subject: [PATCH] libchess: Changes to the move validator. Handles ambiguities alot better now. Renamed pgn_get_valid_moves() to pgn_find_valid_moves(). Namespace cleanups. Need to fix the check and draw tests. --- libchess/chess.h | 22 +- libchess/move.c | 2297 +++++++++++++++++++++++++++--------------------------- libchess/move.h | 16 +- libchess/pgn.c | 344 +------- libchess/pgn.h | 1 + src/cboard.c | 10 +- 6 files changed, 1159 insertions(+), 1531 deletions(-) rewrite libchess/move.c (72%) diff --git a/libchess/chess.h b/libchess/chess.h index bf1db8f..65c04f3 100644 --- a/libchess/chess.h +++ b/libchess/chess.h @@ -26,12 +26,18 @@ #define VALIDRANK VALIDFILE #define VALIDFILE(f) (f >= 1 && f <= 8) +#define RANKTOBOARD ROWTOBOARD +#define FILETOBOARD COLTOBOARD +#define RANKTOINT ROWTOINT +#define FILETOINT COLTOINT #define ROWTOBOARD(r) (8 - r) #define COLTOBOARD(c) (c - 1) #define ROWTOINT(r) (r - '0') #define COLTOINT(c) (c - ('a' - 1)) #define VALIDROW(r) (r >= '1' && r <= '8') #define VALIDCOL(c) (c >= 'a' && c <= 'h') +#define INTTORANK(r) (r + '0') +#define INTTOFILE(f) (f + ('a' - 1)) #define SET_FLAG(var, f) (var |= f) #define CLEAR_FLAG(var, f) (var &= ~(f)) @@ -115,8 +121,7 @@ typedef struct games { unsigned short hindex; // Current move in *hp. unsigned moveclock; // Move clock. FIXME unsigned short flags; // Game flags. - unsigned char castle: 2, // The current move is a castling move. FIXME - side: 1, // This playing side. BLACK or WHITE. + unsigned char side: 1, // This playing side. BLACK or WHITE. turn: 1, // BLACK or WHITE. mode: 2; // MODE_[HISTORY/EDIT/PLAY] unsigned short ply; // Move count. @@ -281,15 +286,15 @@ void pgn_board_init(BOARD b); * Valididate move 'mp' against the game state 'g' and game board 'b' and * update board 'b'. 'mp' is updated to SAN format for moves which aren't * (a2a4 or e8Q for example). Returns E_PGN_PARSE if there was a move text - * parsing error, E_PGN_INVALID if the move is invalid or E_PGN_OK if - * successful. + * parsing error, E_PGN_INVALID if the move is invalid, E_PGN_AMBIGUOUS if the + * move is invalid with ambiguities or E_PGN_OK if * successful. */ -int pgn_validate_move(GAME *g, BOARD b, char **mp); +int pgn_parse_move(GAME *g, BOARD b, char **mp); /* - * Like pgn_validate_move() but don't modify game flags in 'g' or board 'b'. + * Like pgn_parse_move() but don't modify game flags in 'g' or board 'b'. */ -int pgn_validate_only(GAME *g, BOARD b, char **mp); +int pgn_validate_move(GAME *g, BOARD b, char **mp); /* * Returns the total number of moves in 'h' or 0 if there are none. @@ -368,7 +373,7 @@ void pgn_reset_valid_moves(BOARD b); * Sets valid moves from game 'g' using board 'b'. The valid moves are for the * piece on the board 'b' at 'rank' and 'file'. Returns nothing. */ -void pgn_get_valid_moves(GAME *g, BOARD b, int rank, int file); +void pgn_find_valid_moves(GAME g, BOARD b, int rank, int file); /* * Errors returned from the above functions. @@ -377,6 +382,7 @@ typedef enum { E_PGN_ERR = -1, E_PGN_OK, E_PGN_PARSE, + E_PGN_AMBIGUOUS, E_PGN_INVALID, E_MAX_PGN_ERRORS } pgn_error; diff --git a/libchess/move.c b/libchess/move.c dissimilarity index 72% index 67c3a26..25c2929 100644 --- a/libchess/move.c +++ b/libchess/move.c @@ -1,1163 +1,1134 @@ -/* vim:tw=78:ts=8:sw=4:set ft=c: */ -/* - Copyright (C) 2002-2006 Ben Kibbey - - 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. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ -#include -#include -#include -#include - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include "chess.h" -#include "pgn.h" -#include "move.h" - -#ifdef DEBUG -#include "debug.h" -#endif - -#ifdef WITH_DMALLOC -#include -#endif - -int _piece_side(GAME g, int c) -{ - if (c == pgn_int_to_piece(g.turn, OPEN_SQUARE)) - return -1; - - if (c < 'A') - c = pgn_int_to_piece(g.turn, c); - - return (isupper(c)) ? WHITE : BLACK; -} - -int _val_piece_side(char turn, int c) -{ - if ((isupper(c) && turn == WHITE) || - (islower(c) && turn == BLACK)) - return 1; - - return 0; -} - -/* - * Get the source row and column for a given piece. - * - * The following two functions find 'piece' from the given square 'col' and - * 'row' and store the resulting column or row in 'c' and 'r'. The return - * value is the number of 'piece' found (on the current g.side) or zero. - * Search for 'piece' stops when a non-empty square is found. - */ -int _count_by_diag(GAME g, BOARD b, int piece, int row, int col) -{ - int count = 0; - int ul = 0, ur = 0, dl = 0, dr = 0; - int i; - - for (i = 1; VALIDFILE(i); i++) { - unsigned char n; - int r, c; - - r = row + i; - c = col - i; - - if (!ul && VALIDRANK(r) && VALIDFILE(c)) { - n = b[ROWTOBOARD(r)][COLTOBOARD(c)].icon; - - if (pgn_piece_to_int(n) != OPEN_SQUARE) { - if (pgn_piece_to_int(n) == piece && _val_piece_side(g.turn, n)) - count++; - - ul++; - } - } - - r = row + i; - c = col + i; - - if (!ur && VALIDRANK(r) && VALIDFILE(c)) { - n = b[ROWTOBOARD(r)][COLTOBOARD(c)].icon; - - if (pgn_piece_to_int(n) != OPEN_SQUARE) { - if (pgn_piece_to_int(n) == piece && _val_piece_side(g.turn, n)) - count++; - - ur++; - } - } - - r = row - i; - c = col - i; - - if (!dl && VALIDRANK(r) && VALIDFILE(c)) { - n = b[ROWTOBOARD(r)][COLTOBOARD(c)].icon; - - if (pgn_piece_to_int(n) != OPEN_SQUARE) { - if (pgn_piece_to_int(n) == piece && _val_piece_side(g.turn, n)) - count++; - - dl++; - } - } - - r = row - i; - c = col + i; - - if (!dr && VALIDRANK(r) && VALIDFILE(c)) { - n = b[ROWTOBOARD(r)][COLTOBOARD(c)].icon; - - if (pgn_piece_to_int(n) != OPEN_SQUARE) { - if (pgn_piece_to_int(n) == piece && _val_piece_side(g.turn, n)) - count++; - - dr++; - } - } - } - - return count; -} - -int _count_knight(GAME g, BOARD b, int row, int col) -{ - int r, c; - unsigned char p; - int count = 0; - - r = row - 2; - c = col - 1; - - if (VALIDFILE(r) && VALIDFILE(c)) { - p = b[ROWTOBOARD(r)][COLTOBOARD(c)].icon; - - if (pgn_piece_to_int(p) == KNIGHT && _val_piece_side(g.turn, p)) - count++; - } - - r = row - 2; - c = col + 1; - - if (VALIDFILE(r) && VALIDFILE(c)) { - p = b[ROWTOBOARD(r)][COLTOBOARD(c)].icon; - - if (pgn_piece_to_int(p) == KNIGHT && _val_piece_side(g.turn, p)) - count++; - } - - r = row - 1; - c = col - 2; - - if (VALIDFILE(r) && VALIDFILE(c)) { - p = b[ROWTOBOARD(r)][COLTOBOARD(c)].icon; - - if (pgn_piece_to_int(p) == KNIGHT && _val_piece_side(g.turn, p)) - count++; - } - r = row - 1; - c = col + 2; - - if (VALIDFILE(r) && VALIDFILE(c)) { - p = b[ROWTOBOARD(r)][COLTOBOARD(c)].icon; - - if (pgn_piece_to_int(p) == KNIGHT && _val_piece_side(g.turn, p)) - count++; - } - - r = row + 2; - c = col - 1; - - if (VALIDFILE(r) && VALIDFILE(c)) { - p = b[ROWTOBOARD(r)][COLTOBOARD(c)].icon; - - if (pgn_piece_to_int(p) == KNIGHT && _val_piece_side(g.turn, p)) - count++; - } - - r = row + 2; - c = col + 1; - - if (VALIDFILE(r) && VALIDFILE(c)) { - p = b[ROWTOBOARD(r)][COLTOBOARD(c)].icon; - - if (pgn_piece_to_int(p) == KNIGHT && _val_piece_side(g.turn, p)) - count++; - } - - r = row + 1; - c = col - 2; - - if (VALIDFILE(r) && VALIDFILE(c)) { - p = b[ROWTOBOARD(r)][COLTOBOARD(c)].icon; - - if (pgn_piece_to_int(p) == KNIGHT && _val_piece_side(g.turn, p)) - count++; - } - - r = row + 1; - c = col + 2; - - if (VALIDFILE(r) && VALIDFILE(c)) { - p = b[ROWTOBOARD(r)][COLTOBOARD(c)].icon; - - if (pgn_piece_to_int(p) == KNIGHT && _val_piece_side(g.turn, p)) - count++; - } - - return count; -} - -int _count_by_rank(GAME g, BOARD b, int piece, int row, int col) -{ - int i; - int count = 0; - int u = 0, d = 0; - - for (i = 1; VALIDRANK(i); i++) { - unsigned char n; - - if (!u && VALIDRANK(row + i)) { - n = b[ROWTOBOARD(row + i)][COLTOBOARD(col)].icon; - - if (pgn_piece_to_int(n) != OPEN_SQUARE) { - if (pgn_piece_to_int(n) == piece && _val_piece_side(g.turn, n)) - count++; - - u++; - } - } - - if (!d && VALIDRANK(row - i)) { - n = b[ROWTOBOARD(row - i)][COLTOBOARD(col)].icon; - - if (pgn_piece_to_int(n) != OPEN_SQUARE) { - if (pgn_piece_to_int(n) == piece && _val_piece_side(g.turn, n)) - count++; - - d++; - } - } - } - - return count; -} - -int _count_by_file(GAME g, BOARD b, int piece, int row, int col) -{ - int i; - int count = 0; - int l = 0, r = 0; - - for (i = 1; VALIDFILE(i); i++) { - unsigned char n; - - if (!r && VALIDFILE(col + i)) { - n = b[ROWTOBOARD(row)][COLTOBOARD(col + i)].icon; - - if (pgn_piece_to_int(n) != OPEN_SQUARE) { - if (pgn_piece_to_int(n) == piece && _val_piece_side(g.turn, n)) - count++; - - r++; - } - } - - if (!l && VALIDFILE(col - i)) { - n = b[ROWTOBOARD(row)][COLTOBOARD(col - i)].icon; - - if (pgn_piece_to_int(n) != OPEN_SQUARE) { - if (pgn_piece_to_int(n) == piece && _val_piece_side(g.turn, n)) - count++; - - l++; - } - } - } - - return count; -} - -int _count_by_rank_file(GAME g, BOARD b, int piece, int row, int col) -{ - int count; - - count = _count_by_rank(g, b, piece, row, col); - return count + _count_by_file(g, b, piece, row, col); -} - -int _piece_count(GAME g, BOARD b, int piece, int row, int col, int *r, int *c) -{ - int count = 0; - - switch (piece) { - case PAWN: - case ROOK: - count = _count_by_rank_file(g, b, piece, row, col); - break; - case BISHOP: - count = _count_by_diag(g, b, piece, row, col); - break; - case QUEEN: - case KING: - count = _count_by_rank_file(g, b, piece, row, col); - count += _count_by_diag(g, b, piece, row, col); - break; - case KNIGHT: - count = _count_knight(g, b, row, col); - break; - } - - return count; -} - -int _piece_by_col(GAME g, BOARD b, int piece, int row, int col, int *r, int *c) -{ - int i; - int count = 0; - - for (i = col - 1; VALIDFILE(i); i--) { - int n = b[ROWTOBOARD(row)][COLTOBOARD(i)].icon; - - if (pgn_piece_to_int(n) != OPEN_SQUARE) { - if (pgn_piece_to_int(n) == piece && _val_piece_side(g.turn, n)) { - *c = i; - *r = row; - count++; - } - - break; - } - } - - for (i = col + 1; VALIDFILE(i); i++) { - int n = b[ROWTOBOARD(row)][COLTOBOARD(i)].icon; - - if (pgn_piece_to_int(n) != OPEN_SQUARE) { - if (pgn_piece_to_int(n) == piece && _val_piece_side(g.turn, n)) { - *c = i; - *r = row; - count++; - } - - break; - } - } - - return count; -} - -int _piece_by_row(GAME g, BOARD b, int piece, int row, int col, int *r, int *c) -{ - int i; - int count = 0; - - for (i = row + 1; VALIDFILE(i); i++) { - int n = b[ROWTOBOARD(i)][COLTOBOARD(col)].icon; - - if (pgn_piece_to_int(n) != OPEN_SQUARE) { - if (pgn_piece_to_int(n) == piece && _val_piece_side(g.turn, n)) { - *r = i; - *c = col; - count++; - } - - break; - } - } - - for (i = row - 1; VALIDFILE(i); i--) { - int n = b[ROWTOBOARD(i)][COLTOBOARD(col)].icon; - - if (pgn_piece_to_int(n) != OPEN_SQUARE) { - if (pgn_piece_to_int(n) == piece && _val_piece_side(g.turn, n)) { - *r = i; - *c = col; - count++; - } - - break; - } - } - - return count; -} - -int _piece_by_xy(GAME g, BOARD b, int piece, int row, int col, int *srow, int *scol) -{ - int count = 0; - - count = _piece_by_row(g, b, piece, row, col, srow, scol); - count += _piece_by_col(g, b, piece, row, col, srow, scol); - return (count != 1) ? 0 : 1; -} - -int _piece_test(GAME g, BOARD b, int piece, int row, int col, int *dstr, int *dstc) -{ - int p; - - if (!VALIDFILE(row) || !VALIDFILE(col)) - return 2; - - p = b[ROWTOBOARD(row)][COLTOBOARD(col)].icon; - - if (pgn_piece_to_int(p) != OPEN_SQUARE) { - if (pgn_piece_to_int(p) == piece && _val_piece_side(g.turn, p)) { - *dstr = row; - *dstc = col; - return 1; - } - - return 2; - } - - return 0; -} - -int _piece_by_diag(GAME g, BOARD b, int piece, int row, int col, int *srow, int *scol) -{ - int i, n; - int ul = 1, ur = 1, dl = 1, dr = 1; - int count = 0; - - for (i = 1; VALIDFILE(i); i++) { - if (dr) { - n = _piece_test(g, b, piece, row - i, col + i, srow, scol); - - if (n == 1 && count++) - return 1; - else if (n == 2) - dr = 0; - } - - if (dl) { - n = _piece_test(g, b, piece, row - i, col - i, srow, scol); - - if (n == 1 && count++) - return 1; - else if (n == 2) - dl = 0; - } - - if (ur) { - n = _piece_test(g, b, piece, row + i, col + i, srow, scol); - - if (n == 1 && count++) - return 1; - else if (n == 2) - ur = 0; - } - - if (ul) { - n = _piece_test(g, b, piece, row + i, col - i, srow, scol); - - if (n == 1 && count++) - return 1; - else if (n == 2) - ul = 0; - } - } - - return (count) ? 1 : 0; -} - -int _valid_move(GAME g, BOARD b, int row, int col, int srow, int scol) -{ - int p1, p2; - - if (!VALIDFILE(srow) || !VALIDFILE(scol)) - return 0; - - if (row == srow && col == scol) - return 0; - - p1 = b[ROWTOBOARD(srow)][COLTOBOARD(scol)].icon; - p2 = b[ROWTOBOARD(row)][COLTOBOARD(col)].icon; - - if (pgn_piece_to_int(p1) == OPEN_SQUARE) - return 0; - - if (_piece_side(g, p1) == _piece_side(g, p2)) - return 0; - - if (pgn_piece_to_int(p1) == PAWN && scol == col && - pgn_piece_to_int(p2) != OPEN_SQUARE) - return 0; - - return 1; -} - -int _castle_move(GAME *g, BOARD b, char which) -{ - int row; - int n; - int p, p2, p3, p4; - - row = (g->turn == WHITE) ? 1 : 8; - n = COLTOINT('e'); - - if (which == KINGSIDE) { - if ((g->turn == WHITE && (!TEST_FLAG(g->flags, GF_WK_CASTLE))) || - (g->turn == BLACK && (!TEST_FLAG(g->flags, GF_BK_CASTLE)))) - return 1; - - p = b[ROWTOBOARD(row)][COLTOBOARD((n + 1))].icon; - p2 = b[ROWTOBOARD(row)][COLTOBOARD((n + 2))].icon; - p3 = b[ROWTOBOARD(row)][COLTOBOARD((n + 3))].icon; - - if (pgn_piece_to_int(p) != OPEN_SQUARE || pgn_piece_to_int(p2) != OPEN_SQUARE - || (pgn_piece_to_int(p3) != ROOK && _val_piece_side(g->turn, p3))) - return 1; - - if (!validate) { - b[ROWTOBOARD(row)][COLTOBOARD(COLTOINT('e'))].icon = - pgn_int_to_piece(g->turn, OPEN_SQUARE); - b[ROWTOBOARD(row)][COLTOBOARD(6)].icon = pgn_int_to_piece(g->turn, ROOK); - b[ROWTOBOARD(row)][COLTOBOARD(7)].icon = pgn_int_to_piece(g->turn, KING); - b[ROWTOBOARD(row)][COLTOBOARD(8)].icon = pgn_int_to_piece(g->turn, OPEN_SQUARE); - - if (g->turn == WHITE) - CLEAR_FLAG(g->flags, GF_WK_CASTLE); - else if (g->turn == BLACK) - CLEAR_FLAG(g->flags, GF_BK_CASTLE); - } - } - else { - if ((g->turn == WHITE && (!TEST_FLAG(g->flags, GF_WQ_CASTLE))) || - (g->turn == BLACK && (!TEST_FLAG(g->flags, GF_BQ_CASTLE)))) - return 1; - - p = b[ROWTOBOARD(row)][COLTOBOARD((n - 1))].icon; - p2 = b[ROWTOBOARD(row)][COLTOBOARD((n - 2))].icon; - p3 = b[ROWTOBOARD(row)][COLTOBOARD((n - 3))].icon; - p4 = b[ROWTOBOARD(row)][COLTOBOARD((n - 4))].icon; - - if (pgn_piece_to_int(p) != OPEN_SQUARE || - pgn_piece_to_int(p2) != OPEN_SQUARE || - pgn_piece_to_int(p3) != OPEN_SQUARE || - (pgn_piece_to_int(p4) != ROOK && _val_piece_side(g->turn, p4))) - return 1; - - if (!validate) { - b[ROWTOBOARD(row)][COLTOBOARD(1)].icon = pgn_int_to_piece(g->turn, OPEN_SQUARE); - b[ROWTOBOARD(row)][COLTOBOARD(COLTOINT('e'))].icon = - pgn_int_to_piece(g->turn, OPEN_SQUARE); - b[ROWTOBOARD(row)][COLTOBOARD(3)].icon = pgn_int_to_piece(g->turn, KING); - b[ROWTOBOARD(row)][COLTOBOARD(4)].icon = pgn_int_to_piece(g->turn, ROOK); - - if (g->turn == WHITE) - CLEAR_FLAG(g->flags, GF_WQ_CASTLE); - else - CLEAR_FLAG(g->flags, GF_BQ_CASTLE); - } - } - - return 0; -} - -int _get_source_yx(GAME *g, BOARD b, int piece, int row, int col, int *srow, int *scol) -{ - int p = 0; - int count = 0; - int r, c; - int i; - int dist = 0; - - /* FIXME valid move ambiguities. */ - switch (piece) { - case PAWN: - if (*srow == 0 && *scol == col) { - i = (g->turn == WHITE) ? -1 : 1; - - /* Find the first pawn in the current column. */ - for (r = row + i, dist = 0; VALIDFILE(r); r += i, dist++) { - int n = b[ROWTOBOARD(r)][COLTOBOARD(col)].icon; - - p = pgn_piece_to_int(n); - - if (p == PAWN && _val_piece_side(g->turn, n)) - break; - } - - if (p != PAWN || dist > 2) - return 1; - - *srow = r; - dist = abs(*srow - row); - - if (g->turn == WHITE) { - if ((*srow == 2 && dist > 2) || (*srow > 2 && dist > 1)) - return 1; - } - else { - if ((*srow == 7 && dist > 2) || (*srow < 7 && dist > 1)) - return 1; - } - - if (dist == 2) { - p = pgn_piece_to_int(b[ROWTOBOARD(*srow + i)][COLTOBOARD(col)].icon); - if (p != OPEN_SQUARE) - return 1; - } - } - else if (*scol != col) { - if (abs(*scol - col) != 1) - return 1; - - *srow = (g->turn == WHITE) ? row - 1 : row + 1; - - if (pgn_piece_to_int(b[ROWTOBOARD(*srow)][COLTOBOARD(*scol)].icon) - != PAWN) - return 1; - - piece = pgn_piece_to_int(b[ROWTOBOARD(row)][COLTOBOARD(col)].icon); - - /* En Passant. */ - if (piece == OPEN_SQUARE) { - /* Previous move was not 2 squares and a pawn. */ - if (!TEST_FLAG(g->flags, GF_ENPASSANT)) - return 1; - - if (!b[ROWTOBOARD(row)][COLTOBOARD(col)].enpassant) - return 1; - - r = (g->turn == WHITE) ? 6 : 3; - - if (row != r) - return 1; - - r = (g->turn == WHITE) ? row - 1 : row + 1; - piece = b[ROWTOBOARD(r)][COLTOBOARD(col)].icon; - - if (pgn_piece_to_int(piece) != PAWN) - return 1; - - if (!validate) - b[ROWTOBOARD(r)][COLTOBOARD(col)].icon = - pgn_int_to_piece(g->turn, OPEN_SQUARE); - } - } - break; - case ROOK: - if (_piece_by_xy(*g, b, ROOK, row, col, srow, scol) == 0) - return 1; - - if (!validate && *scol == 1) { - if (g->turn == WHITE) - CLEAR_FLAG(g->flags, GF_WQ_CASTLE); - else - CLEAR_FLAG(g->flags, GF_BQ_CASTLE); - } - else if (!validate && *scol == 8) { - if (g->turn == WHITE) - CLEAR_FLAG(g->flags, GF_WK_CASTLE); - else - CLEAR_FLAG(g->flags, GF_BK_CASTLE); - } - break; - case KNIGHT: - r = row - 2; - c = col - 1; - - if (VALIDFILE(r) && VALIDFILE(c)) { - p = b[ROWTOBOARD(r)][COLTOBOARD(c)].icon; - - if (pgn_piece_to_int(p) == KNIGHT && _val_piece_side(g->turn, p)) { - *srow = r; - *scol = c; - count++; - - if ((*srow && *srow == row) || (*scol && *scol == col)) - break; - } - } - - r = row - 2; - c = col + 1; - - if (VALIDFILE(r) && VALIDFILE(c)) { - p = b[ROWTOBOARD(r)][COLTOBOARD(c)].icon; - - if (pgn_piece_to_int(p) == KNIGHT && _val_piece_side(g->turn, p)) { - *srow = r; - *scol = c; - count++; - - if ((*srow && *srow == row) || (*scol && *scol == col)) - break; - } - } - - r = row + 2; - c = col - 1; - - if (VALIDFILE(r) && VALIDFILE(c)) { - p = b[ROWTOBOARD(r)][COLTOBOARD(c)].icon; - - if (pgn_piece_to_int(p) == KNIGHT && _val_piece_side(g->turn, p)) { - *srow = r; - *scol = c; - count++; - - if ((*srow && *srow == row) || (*scol && *scol == col)) - break; - } - } - - r = row + 2; - c = col + 1; - - if (VALIDFILE(r) && VALIDFILE(c)) { - p = b[ROWTOBOARD(r)][COLTOBOARD(c)].icon; - - if (pgn_piece_to_int(p) == KNIGHT && _val_piece_side(g->turn, p)) { - *srow = r; - *scol = c; - count++; - - if ((*srow && *srow == row) || (*scol && *scol == col)) - break; - } - } - - r = row - 1; - c = col - 2; - - if (VALIDFILE(r) && VALIDFILE(c)) { - p = b[ROWTOBOARD(r)][COLTOBOARD(c)].icon; - - if (pgn_piece_to_int(p) == KNIGHT && _val_piece_side(g->turn, p)) { - *srow = r; - *scol = c; - count++; - - if ((*srow && *srow == row) || (*scol && *scol == col)) - break; - } - } - - r = row - 1; - c = col + 2; - - if (VALIDFILE(r) && VALIDFILE(c)) { - p = b[ROWTOBOARD(r)][COLTOBOARD(c)].icon; - - if (pgn_piece_to_int(p) == KNIGHT && _val_piece_side(g->turn, p)) { - *srow = r; - *scol = c; - count++; - - if ((*srow && *srow == row) || (*scol && *scol == col)) - break; - } - } - - r = row + 1; - c = col + 2; - - if (VALIDFILE(r) && VALIDFILE(c)) { - p = b[ROWTOBOARD(r)][COLTOBOARD(c)].icon; - - if (pgn_piece_to_int(p) == KNIGHT && _val_piece_side(g->turn, p)) { - *srow = r; - *scol = c; - count++; - - if ((*srow && *srow == row) || (*scol && *scol == col)) - break; - } - } - - r = row + 1; - c = col - 2; - - if (VALIDFILE(r) && VALIDFILE(c)) { - p = b[ROWTOBOARD(r)][COLTOBOARD(c)].icon; - - if (pgn_piece_to_int(p) == KNIGHT && _val_piece_side(g->turn, p)) { - *srow = r; - *scol = c; - count++; - - if ((*srow && *srow == row) || (*scol && *scol == col)) - break; - } - } - - if ((count != 1 && !validate) || (validate && !count)) - return 1; - - break; - case BISHOP: - if (_piece_by_diag(*g, b, BISHOP, row, col, srow, scol) == 0) - return 1; - break; - case QUEEN: - if (_piece_by_xy(*g, b, QUEEN, row, col, srow, scol) == 0) { - if (_piece_by_diag(*g, b, QUEEN, row, col, srow, scol) == 0) - return 1; - } - break; - case KING: - if (_piece_by_xy(*g, b, KING, row, col, srow, scol) == 0) { - if (_piece_by_diag(*g, b, KING, row, col, srow, scol) == 0) - return 1; - } - - if (abs(*srow - row) > 1) - return 1; - - dist = abs(*scol - col); - - if (*scol == COLTOINT('e')) { - if (dist > 2) - return 1; - - if (validate) { - if (dist == 2) { - if (col == 3) { - if (_castle_move(g, b, QUEENSIDE)) - return 1; - } - else if (col == 7) { - if (_castle_move(g, b, KINGSIDE)) - return 1; - } - else - return 1; - - break; - } - - if (dist > 1) - return 1; - - break; - } - } - - if (dist > 1) - return 1; - - break; - default: - return 1; - } - - if (_valid_move(*g, b, row, col, *srow, *scol) == 0) - return 1; - - if (piece == KING) { - if (!validate) { - if (g->turn == WHITE) { - CLEAR_FLAG(g->flags, GF_WK_CASTLE); - CLEAR_FLAG(g->flags, GF_WQ_CASTLE); - } - else { - CLEAR_FLAG(g->flags, GF_BK_CASTLE); - CLEAR_FLAG(g->flags, GF_BQ_CASTLE); - } - } - } - - return 0; -} - -/* This function converts a2a4 formatted moves to SAN format. Minimal checks - * are performed here. The real checks are in validate_move() after the - * conversion. - */ -char *_a2a4tosan(GAME *g, BOARD b, char *m) -{ - static char buf[MAX_SAN_MOVE_LEN + 1] = {0}, *cp = buf; - char *p = m; - int scol, srow, col, row; - int piece, piecei, spiece; - int trow, tcol; - int rowc, colc; - int promo = 0; - int tenpassant = 0; - int n; - - // Not in a2a4 format. Probably already in SAN format. - if (!VALIDCOL(*p) || !VALIDROW(*(p + 1)) || !VALIDCOL(*(p + 2)) - || !VALIDROW(*(p + 3))) - return m; - - scol = COLTOINT(*p); - srow = ROWTOINT(*(p + 1)); - col = COLTOINT(*(p + 2)); - row = ROWTOINT(*(p + 3)); - - if (p[4]) { - if ((promo = pgn_piece_to_int(p[4])) == -1) - return NULL; - } - - piece = b[ROWTOBOARD(srow)][COLTOBOARD(scol)].icon; - - if ((piecei = pgn_piece_to_int(piece)) == -1 || piecei == OPEN_SQUARE) - return NULL; - - spiece = piecei; - cp = buf; - colc = abs(scol - col); - - if (srow == row && (row == 1 || row == 8) && scol == COLTOINT('e') - && colc > 1 && piecei == KING) { - if (scol - col < 0) - return "O-O"; - else if (scol - col > 0) - return "O-O-O"; - - return NULL; - } - - if (piecei != PAWN) - *cp++ = toupper(piece); - else { - /* En Passant. */ - if (scol != col && pgn_piece_to_int(b[row][col].icon) == OPEN_SQUARE) - tenpassant = 1; - } - - colc = _piece_by_col(*g, b, piecei, row, col, &trow, &tcol); - rowc = _piece_by_row(*g, b, piecei, row, col, &trow, &tcol); - n = colc + rowc; - - if (piecei == KNIGHT) { - if (_get_source_yx(g, b, KNIGHT, row, col, &trow, &tcol) == 1) - *cp++ = INTTOCOL(scol); - } - else if (n > 1 && piecei != PAWN) { - if (colc > 1 && rowc > 1) { - *cp++ = INTTOCOL(scol); - *cp++ = INTTOROW(srow); - } - else if (colc >= 1) - *cp++ = INTTOCOL(scol); - else if (rowc >= 1) - *cp++ = INTTOROW(srow); - } - - piece = b[ROWTOBOARD(row)][COLTOBOARD(col)].icon; - - if ((piecei = pgn_piece_to_int(piece)) != OPEN_SQUARE || tenpassant) { - if (tenpassant || spiece == PAWN) - *cp++ = INTTOCOL(scol); - - *cp++ = 'x'; - } - - *cp++ = INTTOCOL(col); - *cp++ = INTTOROW(row); - - if (promo) { - *cp++ = '='; - *cp++ = toupper(pgn_int_to_piece(g->turn, promo)); - } - - *cp = '\0'; - return buf; -} - -void _kingsquare(GAME g, BOARD b, int *kr, int *kc, int *okr, int *okc) -{ - int row, col; - - for (row = 1; VALIDFILE(row); row++) { - for (col = 1; VALIDFILE(col); col++) { - int p = b[ROWTOBOARD(row)][COLTOBOARD(col)].icon; - - if (pgn_piece_to_int(p) == KING) { - if (_val_piece_side(g.turn, p)) { - *kr = row; - *kc = col; - } - else { - /* Opponent. */ - *okr = row; - *okc = col; - } - } - } - } -} - -int _checktest(GAME *g, BOARD b, int kr, int kc, int okr, int okc, int matetest) -{ - int row, col; - - pgn_switch_turn(g); - - /* See if the move would put our opponent in check. */ - for (row = 1; VALIDFILE(row); row++) { - for (col = 1; VALIDFILE(col); col++) { - int srow = 0, scol = 0; - int p = b[ROWTOBOARD(row)][COLTOBOARD(col)].icon; - int pi = pgn_piece_to_int(p); - - if (pi == OPEN_SQUARE) - continue; - - if (pi == PAWN) - scol = col; - - /* See if the move would leave ourselves in check. */ - if (!matetest) { - pgn_switch_turn(g); - - if (_get_source_yx(g, b, pi, kr, kc, &srow, &scol) == 0) - return -1; - - pgn_switch_turn(g); - } - - if (_get_source_yx(g, b, pi, okr, okc, &srow, &scol) == 0) { - pgn_switch_turn(g); - return 1; - } - } - } - - pgn_switch_turn(g); - return 0; -} - -int _checkmate_pawn_test(GAME *g, BOARD b, int row, int col, int *srow, int *scol) -{ - int r, c; - - r = 0; - c = col; - - if (!_get_source_yx(g, b, PAWN, row, col, &r, &c)) - return 0; - - c = col - 1; - - if (!_get_source_yx(g, b, PAWN, row, col, &r, &c)) - return 0; - - c = col + 1; - - if (!_get_source_yx(g, b, PAWN, row, col, &r, &c)) - return 0; - - return 1; -} - -int _checkmatetest(GAME *g, BOARD b, int kr, int kc, int okr, int okc) -{ - int row, col; - int srow, scol; - int check; - - /* For each square on the board see if each peace has a valid move, and if - * so, see if it would leave ourselves or the opponent in check. - */ - for (row = 1; VALIDFILE(row); row++) { - for (col = 1; VALIDFILE(col); col++) { - int n; - - for (n = 0; n < MAX_PIECES; n++) { - int p; - int nkr = kr, nkc = kc, nokr = okr, nokc = okc; - BOARD oldboard; - - srow = scol = 0; - - if (n == PAWN) { - if (_checkmate_pawn_test(g, b, row, col, &srow, &scol)) - continue; - } - else { - if (_get_source_yx(g, b, n, row, col, &srow, &scol)) - continue; - } - - /* Valid move. */ - memcpy(oldboard, b, sizeof(BOARD)); - p = b[ROWTOBOARD(srow)][COLTOBOARD(scol)].icon; - b[ROWTOBOARD(row)][COLTOBOARD(col)].icon = p; - b[ROWTOBOARD(srow)][COLTOBOARD(scol)].icon = - pgn_int_to_piece(g->turn, OPEN_SQUARE); - - if (pgn_piece_to_int(p) == KING) { - if (_piece_side(*g, p) == g->turn) { - nokr = row; - nokc = col; - } - else { - nkr = row; - nkc = col; - } - } - - check = _checktest(g, b, nkr, nkc, nokr, nokc, 1); - memcpy(b, oldboard, sizeof(BOARD)); - - if (check == 0) - goto done; - } - } - } - - check = 1; - - if (g->turn == WHITE) - result = BLACKWINS; - else - result = WHITEWINS; - -done: - return (check != 0) ? 1 : 0; -} - -/* FIXME */ -int _drawtest(GAME *g, BOARD b) -{ - int row, col; - int other = 0; - - if (pgn_config.fmd && g->ply >= 50) - return 1; - - for (row = 1; VALIDFILE(row); row++) { - for (col = 1; VALIDFILE(col); col++) { - int p = b[ROWTOBOARD(row)][COLTOBOARD(col)].icon; - int piece = pgn_piece_to_int(p); - - switch (piece) { - case PAWN: - return 0; - break; - case KING: - break; - default: - other++; - break; - } - } - } - - if (!other) - return 1; - - return 0; -} +/* vim:tw=78:ts=8:sw=4:set ft=c: */ +/* + Copyright (C) 2002-2006 Ben Kibbey + + 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. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +#include +#include +#include +#include + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "chess.h" +#include "pgn.h" +#include "move.h" + +#ifdef DEBUG +#include "debug.h" +#endif + +#ifdef WITH_DMALLOC +#include +#endif + +static int piece_side(GAME g, int c) +{ + if (c == pgn_int_to_piece(g.turn, OPEN_SQUARE)) + return -1; + + if (c < 'A') + c = pgn_int_to_piece(g.turn, c); + + return (isupper(c)) ? WHITE : BLACK; +} + +static int val_piece_side(char turn, int c) +{ + if ((isupper(c) && turn == WHITE) || + (islower(c) && turn == BLACK)) + return 1; + + return 0; +} + +static int count_piece(GAME g, BOARD b, int piece, int sfile, int srank, int + file, int rank, int *count) +{ + int n = b[RANKTOBOARD(rank)][FILETOBOARD(file)].icon; + + if (!VALIDRANK(rank) || !VALIDFILE(file)) + return 0; + + if (pgn_piece_to_int(n) != OPEN_SQUARE) { + if (pgn_piece_to_int(n) == piece && val_piece_side(g.turn, n)) { + if (sfile && file == sfile) { + if (!srank || (srank && rank == srank)) + (*count)++; + } + else if (srank && rank == srank) { + if (!sfile) + (*count)++; + } + else if (!sfile && !srank) + (*count)++; + } + + return 1; + } + + return 0; +} + +/* + * Get the source row and column for a given piece. + * + * The following two functions find 'piece' from the given square 'col' and + * 'row' and store the resulting column or row in 'c' and 'r'. The return + * value is the number of 'piece' found (on the current g.side) or zero. + * Search for 'piece' stops when a non-empty square is found. + */ +static int count_by_diag(GAME g, BOARD b, int piece, int sfile, int srank, + int file, int rank) +{ + int count = 0; + int ul = 0, ur = 0, dl = 0, dr = 0; + int i; + int f, r; + + for (i = 1; VALIDFILE(i); i++) { + r = rank + i; + f = file - i; + + if (!ul && VALIDRANK(r) && VALIDFILE(f)) + ul = count_piece(g, b, piece, sfile, srank, f, r, &count); + + r = rank + i; + f = file + i; + + if (!ur && VALIDRANK(r) && VALIDFILE(f)) + ur = count_piece(g, b, piece, sfile, srank, f, r, &count); + + r = rank - i; + f = file - i; + + if (!dl && VALIDRANK(r) && VALIDFILE(f)) + dl = count_piece(g, b, piece, sfile, srank, f, r, &count); + + r = rank - i; + f = file + i; + + if (!dr && VALIDRANK(r) && VALIDFILE(f)) + dr = count_piece(g, b, piece, sfile, srank, f, r, &count); + } + + return count; +} + +static int count_knight(GAME g, BOARD b, int piece, int sfile, int srank, + int file, int rank) +{ + int count = 0; + + count_piece(g, b, piece, sfile, srank, file - 1, rank + 2, &count); + count_piece(g, b, piece, sfile, srank, file + 1, rank + 2, &count); + count_piece(g, b, piece, sfile, srank, file + 2, rank + 1, &count); + count_piece(g, b, piece, sfile, srank, file - 2, rank + 1, &count); + count_piece(g, b, piece, sfile, srank, file + 1, rank - 2, &count); + count_piece(g, b, piece, sfile, srank, file - 1, rank - 2, &count); + count_piece(g, b, piece, sfile, srank, file + 2, rank - 1, &count); + count_piece(g, b, piece, sfile, srank, file - 2, rank - 1, &count); + return count; +} + +static int count_by_rank(GAME g, BOARD b, int piece, int sfile, int srank, + int file, int rank) +{ + int i; + int count = 0; + int u = 0, d = 0; + + for (i = 1; VALIDRANK(i); i++) { + if (!u && VALIDRANK((rank + i))) { + if (count_piece(g, b, piece, sfile, srank, file, rank + i, &count)) + u++; + } + + if (!d && VALIDRANK((rank - i))) { + if (count_piece(g, b, piece, sfile, srank, file, rank - i, &count)) + d++; + } + } + + return count; +} + +static int count_by_file(GAME g, BOARD b, int piece, int sfile, int srank, + int file, int rank) +{ + int i; + int count = 0; + int l = 0, r = 0; + + for (i = 1; VALIDFILE(i); i++) { + if (!r && VALIDFILE((file + i))) { + if (count_piece(g, b, piece, sfile, srank, file + i, rank, &count)) + r++; + } + + if (!l && VALIDFILE((file - i))) { + if (count_piece(g, b, piece, sfile, srank, file - i, rank, &count)) + l++; + } + } + + return count; +} + +static int count_by_rank_file(GAME g, BOARD b, int piece, int sfile, int srank, + int file, int rank) +{ + int count; + + count = count_by_rank(g, b, piece, sfile, srank, file, rank); + return count + count_by_file(g, b, piece, sfile, srank, file, rank); +} + +static int valid_move(GAME g, BOARD b, int row, int col, int srow, int scol) +{ + int p1, p2; + + if (!VALIDFILE(srow) || !VALIDFILE(scol)) + return 0; + + if (row == srow && col == scol) + return 0; + + p1 = b[ROWTOBOARD(srow)][COLTOBOARD(scol)].icon; + p2 = b[ROWTOBOARD(row)][COLTOBOARD(col)].icon; + + if (pgn_piece_to_int(p1) == OPEN_SQUARE) + return 0; + + if (piece_side(g, p1) == piece_side(g, p2)) + return 0; + + if (pgn_piece_to_int(p1) == PAWN && scol == col && + pgn_piece_to_int(p2) != OPEN_SQUARE) + return 0; + + return 1; +} + +/* + * Returns the number of pieces of type 'p' that can move to the destination + * square located at 'file' and 'rank'. The source square 'sfile' and 'srank' + * (if available in the move text) should be determined before calling this + * function or set to 0 if unknown. Returns 0 if the move is impossible for + * the piece 'p'. + */ +static int find_ambiguous(GAME g, BOARD b, int p, int sfile, int srank, + int file, int rank) +{ + int count = 0; + + switch (p) { + case PAWN: + count = 1; + break; + case ROOK: + count = count_by_rank_file(g, b, p, sfile, srank, file, rank); + break; + case KNIGHT: + count = count_knight(g, b, p, sfile, srank, file, rank); + break; + case BISHOP: + count = count_by_diag(g, b, p, sfile, srank, file, rank); + break; + case QUEEN: + count = count_by_rank_file(g, b, p, sfile, srank, file, rank); + count += count_by_diag(g, b, p, sfile, srank, file, rank); + break; + case KING: + count = count_by_rank_file(g, b, p, sfile, srank, file, rank); + count += count_by_diag(g, b, p, sfile, srank, file, rank); + break; + default: + break; + } + + return count; +} + +static void find_king_squares(GAME g, BOARD b, int *file, int *rank, int *ofile, + int *orank) +{ + int f, r; + + for (r = 1; VALIDRANK(r); r++) { + for (f = 1; VALIDFILE(f); f++) { + int p = b[RANKTOBOARD(r)][FILETOBOARD(f)].icon; + + if (pgn_piece_to_int(p) != KING) + continue; + + if (val_piece_side(g.turn, p)) + *file = f, *rank = r; + else + *ofile = f, *orank = r; + } + } +} + +// FIXME in-check test +static int validate_castle_move(GAME g, BOARD b, int side, int sfile, + int srank, int file, int rank) +{ + if (side == KINGSIDE) { + if ((g.turn == WHITE && !TEST_FLAG(g.flags, GF_WK_CASTLE)) || + (g.turn == BLACK && !TEST_FLAG(g.flags, GF_BK_CASTLE))) + return E_PGN_INVALID; + } + else { + if ((g.turn == WHITE && !TEST_FLAG(g.flags, GF_WQ_CASTLE)) || + (g.turn == BLACK && !TEST_FLAG(g.flags, GF_BQ_CASTLE))) + return E_PGN_INVALID; + } + + if (file > FILETOINT('e')) { + if (b[RANKTOBOARD(srank)][FILETOBOARD((sfile + 1))].icon + != pgn_int_to_piece(g.turn, OPEN_SQUARE) || + b[RANKTOBOARD(srank)][FILETOBOARD((sfile + 2))].icon + != pgn_int_to_piece(g.turn, OPEN_SQUARE)) + return E_PGN_INVALID; + } + else { + if (b[RANKTOBOARD(srank)][FILETOBOARD((sfile - 1))].icon != + pgn_int_to_piece(g.turn, OPEN_SQUARE) || + b[RANKTOBOARD(srank)][FILETOBOARD((sfile - 2))].icon != + pgn_int_to_piece(g.turn, OPEN_SQUARE) || + b[RANKTOBOARD(srank)][FILETOBOARD((sfile - 3))].icon != + pgn_int_to_piece(g.turn, OPEN_SQUARE)) + return E_PGN_INVALID; + } + + return E_PGN_OK; +} + +static int parse_castle_move(GAME g, BOARD b, int side, int *sfile, + int *srank, int *file, int *rank) +{ + *srank = *rank = (g.turn == WHITE) ? 1 : 8; + *sfile = FILETOINT('e'); + + if (side == KINGSIDE) + *file = FILETOINT('g'); + else + *file = FILETOINT('c'); + + return validate_castle_move(g, b, side, *sfile, *srank, *file, *rank); +} + +static int find_source_square(GAME g, BOARD b, int piece, int *sfile, + int *srank, int file, int rank) +{ + int p = 0; + int r, f; + int i; + int dist = 0; + int count = 0; + + if (piece == PAWN) { + if (!*srank && *sfile == file) { + /* Find the first pawn in the current column. */ + i = (g.turn == WHITE) ? -1 : 1; + + for (r = rank + i, dist = 0; VALIDFILE(r); r += i, dist++) { + int n = b[RANKTOBOARD(r)][FILETOBOARD(file)].icon; + + p = pgn_piece_to_int(n); + + if (p == PAWN && val_piece_side(g.turn, n)) + break; + } + + if (p != PAWN || dist > 2) + return 0; + + *srank = r; + dist = abs(*srank - rank); + + if (g.turn == WHITE) { + if ((*srank == 2 && dist > 2) || (*srank > 2 && dist > 1)) + return 0; + } + else { + if ((*srank == 7 && dist > 2) || (*srank < 7 && dist > 1)) + return 0; + } + + p = b[RANKTOBOARD(rank)][FILETOBOARD(file)].icon; + + if (pgn_piece_to_int(p) != OPEN_SQUARE) + return 0; + } + else if (*sfile != file) { + if (abs(*sfile - file) != 1) + return 0; + + *srank = (g.turn == WHITE) ? rank - 1 : rank + 1; + p = b[RANKTOBOARD(*srank)][FILETOBOARD(*sfile)].icon; + + if (!val_piece_side(g.turn, p)) + return 0; + + if (pgn_piece_to_int(p) != PAWN || abs(*srank - rank) != 1) + return 0; + + p = b[RANKTOBOARD(rank)][FILETOBOARD(file)].icon; + + /* En Passant. */ + if (pgn_piece_to_int(p) == OPEN_SQUARE) { + /* Previous move was not 2 squares and a pawn. */ + if (!TEST_FLAG(g.flags, GF_ENPASSANT)) + return 0; + + if (!b[RANKTOBOARD(rank)][FILETOBOARD(file)].enpassant) + return 0; + + r = (g.turn == WHITE) ? 6 : 3; + + if (rank != r) + return 0; + + r = (g.turn == WHITE) ? rank - 1 : rank + 1; + p = b[RANKTOBOARD(r)][FILETOBOARD(file)].icon; + + if (pgn_piece_to_int(p) != PAWN) + return 0; + } + + if (val_piece_side(g.turn, p)) + return 0; + } + + return 1; + } + + for (r = 1; VALIDRANK(r); r++) { + for (f = 1; VALIDFILE(f); f++) { + int n; + + if ((*sfile && f != *sfile) || (*srank && r != *srank)) + continue; + + n = find_ambiguous(g, b, piece, f, r, file, rank); + + if (n) { + count += n; + *sfile = f; + *srank = r; + } + } + } + + if (count != 1) + return count; + + /* FIXME shouldn't be here. + if (valid_move(g, b, rank, file, *srank, *sfile) == 0) + return 0; + */ + + if (piece == KING) { + if (abs(*srank - rank) > 1 || abs(*sfile - file) > 2) + return 0; + + if (abs(*sfile - file) == 2) { + if (*sfile != FILETOINT('e')) + return 0; + else { + if (validate_castle_move(g, b, (file > FILETOINT('e')) ? + KINGSIDE : QUEENSIDE, *sfile, *srank, file, rank) + != E_PGN_OK) + return 0; + } + } + } + + return count; +} + +static int finalize_move(GAME *g, BOARD b, int promo, int sfile, int srank, + int file, int rank) +{ + int p, pi; + + p = b[RANKTOBOARD(rank)][FILETOBOARD(file)].icon; + pi = pgn_piece_to_int(p); + + if (pi != OPEN_SQUARE) { + if (val_piece_side(g->turn, p)) + return E_PGN_INVALID; + } + + if (!validate) { + if (p == PAWN || capture) + g->ply = 0; + else + g->ply++; + + pgn_reset_enpassant(b); + p = b[RANKTOBOARD(srank)][FILETOBOARD(sfile)].icon; + pi = pgn_piece_to_int(p); + + if (pi == PAWN) { + if (sfile != file) { + p = (g->turn == WHITE) ? rank - 1 : rank + 1; + b[RANKTOBOARD(p)][FILETOBOARD(file)].icon = + pgn_int_to_piece(g->turn, OPEN_SQUARE); + } + + if (abs(srank - rank) > 1) { + SET_FLAG(g->flags, GF_ENPASSANT); + b[RANKTOBOARD(((g->turn == WHITE) ? rank - 1 : rank + 1))][FILETOBOARD(file)].enpassant = 1; + } + else + CLEAR_FLAG(g->flags, GF_ENPASSANT); + } + else if (pi == ROOK) { + if (g->turn == WHITE) + CLEAR_FLAG(g->flags, (file > FILETOINT('e')) ? GF_WK_CASTLE : + GF_WQ_CASTLE); + else + CLEAR_FLAG(g->flags, (file > FILETOINT('e')) ? GF_BK_CASTLE : + GF_BQ_CASTLE); + } + + if (pi == KING && !castle) { + if (g->turn == WHITE) + CLEAR_FLAG(g->flags, GF_WK_CASTLE|GF_WQ_CASTLE); + else + CLEAR_FLAG(g->flags, GF_BK_CASTLE|GF_BQ_CASTLE); + } + + if (castle) { + p = b[RANKTOBOARD(srank)][FILETOBOARD(sfile)].icon; + b[RANKTOBOARD(srank)][FILETOBOARD(sfile)].icon = + pgn_int_to_piece(g->turn, OPEN_SQUARE); + b[RANKTOBOARD(srank)][FILETOBOARD( + (file > FILETOINT('e') ? 8 : 1))].icon = + pgn_int_to_piece(g->turn, OPEN_SQUARE); + b[RANKTOBOARD(rank)][FILETOBOARD(file)].icon = p; + + if (file > FILETOINT('e')) + b[RANKTOBOARD(rank)][FILETOBOARD((file - 1))].icon = + pgn_int_to_piece(g->turn, ROOK); + else + b[RANKTOBOARD(rank)][FILETOBOARD((file + 1))].icon = + pgn_int_to_piece(g->turn, ROOK); + + if (g->turn == WHITE) + CLEAR_FLAG(g->flags, (file > FILETOINT('e')) ? GF_WK_CASTLE : + GF_WQ_CASTLE); + else + CLEAR_FLAG(g->flags, (file > FILETOINT('e')) ? GF_BK_CASTLE : + GF_BQ_CASTLE); + + castle = 0; + } + else { + if (promo) + p = pgn_int_to_piece(g->turn, promo); + else + p = b[RANKTOBOARD(srank)][FILETOBOARD(sfile)].icon; + + b[RANKTOBOARD(srank)][FILETOBOARD(sfile)].icon = + pgn_int_to_piece(g->turn, OPEN_SQUARE); + b[RANKTOBOARD(rank)][FILETOBOARD(file)].icon = p; + } + } + + return E_PGN_OK; +#if 0 + _kingsquare(*g, b, &kr, &kc, &okr, &okc); + pgn_switch_turn(g); + + if (g->castle) { + p = m + strlen(m); + g->castle = 0; + } + + CLEAR_FLAG(g->flags, GF_GAMEOVER); + i = validate; + validate = 1; + + if (_drawtest(g, b)) { + pgn_tag_add(&g->tag, "Result", "1/2-1/2"); + SET_FLAG(g->flags, GF_GAMEOVER); + } + else { + switch (_checktest(g, b, kr, kc, okr, okc, 0)) { + case 0: + break; + case -1: + validate = i; + pgn_switch_turn(g); + return E_PGN_INVALID; + default: + if (_checkmatetest(g, b, kr, kc, okr, okc)) { + *p++ = '#'; + + if (result == WHITEWINS) + pgn_tag_add(&g->tag, "Result", "1-0"); + else if (result == BLACKWINS) + pgn_tag_add(&g->tag, "Result", "1-0"); + + SET_FLAG(g->flags, GF_GAMEOVER); + } + else + *p++ = '+'; + + *p = '\0'; + break; + } + } + validate = i; +#endif + +// pgn_switch_turn(g); + + return E_PGN_OK; +} + +/* + * Converts a2a3 formatted moves to SAN format. The promotion piece should be + * appended (a7a8q). + */ +static int frfrtosan(GAME *g, BOARD b, char **m) +{ + char *bp = *m; + int icon, p, dp, promo = 0; + int sfile, srank, file, rank; + int n; + int fc, rc; + + sfile = FILETOINT(bp[0]); + srank = RANKTOINT(bp[1]); + file = FILETOINT(bp[2]); + rank = RANKTOINT(bp[3]); + + if (bp[4]) { + if ((promo = pgn_piece_to_int(bp[4])) == -1) + return E_PGN_PARSE; + } + + icon = b[RANKTOBOARD(srank)][FILETOBOARD(sfile)].icon; + + if ((p = pgn_piece_to_int(icon)) == -1 || p == OPEN_SQUARE) + return 0; + + if (p != PAWN && promo) + return E_PGN_INVALID; + + if (p == PAWN) { + if (find_source_square(*g, b, p, &sfile, &srank, file, rank) != 1) + return E_PGN_INVALID; + + goto capture; + } + + if (p == KING && abs(sfile - file) > 1) { + if (abs(sfile - file) > 2) + return E_PGN_INVALID; + + if (validate_castle_move(*g, b, (file > FILETOINT('e')) ? + KINGSIDE : QUEENSIDE, sfile, srank, file, rank) != E_PGN_OK) + return E_PGN_INVALID; + + castle = 1; + strcpy(bp, (file > FILETOINT('e')) ? "O-O" : "O-O-O"); + return finalize_move(g, b, promo, sfile, srank, file, rank); + } + + *bp++ = toupper(icon); + n = find_ambiguous(*g, b, p, 0, 0, file, rank); + + if (!n) + return E_PGN_INVALID; + else if (n > 1) { + fc = find_ambiguous(*g, b, p, sfile, 0, file, rank); + rc = find_ambiguous(*g, b, p, 0, srank, file, rank); + + if (fc == 1) + *bp++ = INTTOFILE(sfile); + else if (!fc && rc) + *bp++ = INTTORANK(srank); + else if (fc && rc) { + *bp++ = INTTOFILE(sfile); + *bp++ = INTTORANK(srank); + } + else + return E_PGN_PARSE; // FIXME probable a bug in the parser. + } + +capture: + icon = b[RANKTOBOARD(rank)][FILETOBOARD(file)].icon; + + if ((dp = pgn_piece_to_int(icon)) == -1) + return E_PGN_PARSE; + + /* + * [Pf][fr]x + */ + if (dp != OPEN_SQUARE || (dp == OPEN_SQUARE && p == PAWN && sfile != file)) { + if (p == PAWN) + *bp++ = INTTOFILE(sfile); + + *bp++ = 'x'; + } + + /* + * [Pf][fr][x]fr + */ + + *bp++ = INTTOFILE(file); + *bp++ = INTTORANK(rank); + + /* + * [Pf][fr][x]fr[=P] + */ + if (promo) { + if (p != PAWN || (g->turn == WHITE && (srank != 7 || rank != 8)) || + (g->turn == BLACK && (srank != 2 || rank != 1))) + return E_PGN_INVALID; + + *bp++ = '='; + *bp++ = pgn_int_to_piece(g->turn, promo); + } + + *bp = 0; + return finalize_move(g, b, promo, sfile, srank, file, rank); +} + +/* + * Valididate move 'mp' against the game state 'g' and game board 'b' and + * update board 'b'. 'mp' is updated to SAN format for moves which aren't + * (frfr or e8Q for example). Returns E_PGN_PARSE if there was a move text + * parsing error, E_PGN_INVALID if the move is invalid or E_PGN_OK if + * successful. + */ +int pgn_parse_move(GAME *g, BOARD b, char **mp) +{ + char *p; + int piece; + int i = -1; + int srank = 0, sfile = 0, rank, file; + int promo = -1; + char *m = *mp; + + capture = 0; + srank = rank = file = sfile = promo = piece = 0; + + if (VALIDCOL(*m) && VALIDROW(*(m + 1)) && VALIDCOL(*(m + 2)) && + VALIDROW(*(m + 3))) + return frfrtosan(g, b, mp); + else if (strcmp(m, "O-O") == 0) + i = KINGSIDE; + else if (strcmp(m, "O-O-O") == 0) + i = QUEENSIDE; + + if (i >= 0) { + if (parse_castle_move(*g, b, i, &sfile, &srank, &file, &rank) != + E_PGN_OK) + return E_PGN_INVALID; + + *m++ = INTTOFILE(sfile); + *m++ = INTTORANK(srank); + *m++ = INTTOFILE(file); + *m++ = INTTORANK(rank); + *m = 0; + return frfrtosan(g, b, mp); + } + +again: + if (strlen(m) < 2) + return E_PGN_PARSE; + + p = (m) + strlen(m); + + while (!isdigit(*--p) && *p != 'O') { + if (*p == '=') { + promo = pgn_piece_to_int(i); + i = 0; + break; + } + + i = *p; + *p = '\0'; + } + + // Old promotion text (e8Q). Convert to SAN. + if (pgn_piece_to_int(i) != -1) { + p = (m) + strlen(m); + *p++ = '='; + *p++ = i; + *p = '\0'; + goto again; + } + + p = m; + + /* Skip 'P' (pawn). */ + if (pgn_piece_to_int(*p) == PAWN) + p++; + + /* Pawn. */ + if (VALIDCOL(*p)) { + for (i = 0; *p; i++) { + if (VALIDCOL(*p)) { + if (i > 0) + file = FILETOINT(*p++); + else + file = sfile = FILETOINT(*p++); + } + else if (VALIDROW(*p)) { + if (1 > 1) + rank = RANKTOINT(*p++); + else + rank = RANKTOINT(*p++); + } + else if (*p == 'x') { + file = FILETOINT(*++p); + rank = RANKTOINT(*++p); + capture++; + } + else if (*p == '=') { + if (promo == -1 || promo == KING || promo == PAWN) + return E_PGN_PARSE; + + *p++ = '='; + *p++ = toupper(pgn_int_to_piece(g->turn, promo)); + *p = '\0'; + break; + } + else { +#ifdef DEBUG + DUMP("Pawn (move: '%s'): %c\n", m, *p++); +#else + p++; +#endif + } + } + + if (find_source_square(*g, b, PAWN, &sfile, &srank, file, rank) != 1) + return E_PGN_INVALID; + } + /* Not a pawn. */ + else { + if (strcmp(m, "O-O") == 0) + castle = KINGSIDE; + else if (strcmp(m, "O-O-O") == 0) + castle = QUEENSIDE; + else { + p = m; + + /* + * P[fr][x]fr + * + * The first character is the piece but only if not a pawn. + */ + if ((piece = pgn_piece_to_int(*p++)) == -1) + return E_PGN_PARSE; + + /* + * [fr]fr + */ + if (strlen(m) > 3) { + /* + * rfr + */ + if (isdigit(*p)) + srank = RANKTOINT(*p++); + /* + * f[r]fr + */ + else if (VALIDCOL(*p)) { + sfile = FILETOINT(*p++); + + /* + * frfr + */ + if (isdigit(*p)) + srank = RANKTOINT(*p++); + } + + /* + * xfr + */ + if (*p == 'x') { + capture++; + p++; + } + } + + /* + * fr + * + * The destination square. + */ + file = FILETOINT(*p++); + rank = RANKTOINT(*p++); + + if (*p == '=') + promo == *++p; + + if ((i = find_ambiguous(*g, b, piece, sfile, srank, file, rank)) + != 1) + return (i == 0) ? E_PGN_INVALID : E_PGN_AMBIGUOUS; + + /* + * The move is a valid one. Find the source file and rank so we + * can later update the board positions. + */ + if (find_source_square(*g, b, piece, &sfile, &srank, file, rank) != 1) + return E_PGN_INVALID; + } + } + + *mp = m; + return finalize_move(g, b, promo, sfile, srank, file, rank); +} + +/* + * Like pgn_parse_move() but don't modify game flags in 'g' or board 'b'. + */ +int pgn_validate_move(GAME *g, BOARD b, char **m) +{ + int ret; + + validate = 1; + ret = pgn_parse_move(g, b, m); + validate = 0; + return ret; +} + +/* + * Sets valid moves from game 'g' using board 'b'. The valid moves are for the + * piece on the board 'b' at 'rank' and 'file'. Returns nothing. + */ +void pgn_find_valid_moves(GAME g, BOARD b, int file, int rank) +{ + int p = pgn_piece_to_int(b[RANKTOBOARD(rank)][FILETOBOARD(file)].icon); + int r, f; + + validate = 1; + + for (r = 1; VALIDRANK(r); r++) { + for (f = 1; VALIDFILE(f); f++) { + int n = 0; + + if (val_piece_side(g.turn, b[RANKTOBOARD(r)][FILETOBOARD(f)].icon)) + continue; + + if (find_source_square(g, b, p, &file, (p == PAWN) ? &n : &rank, f, + r) != 0) + b[RANKTOBOARD(r)][FILETOBOARD(f)].valid = 1; + } + } + + validate = 0; +} + +#if 0 +void _kingsquare(GAME g, BOARD b, int *kr, int *kc, int *okr, int *okc) +{ + int row, col; + + for (row = 1; VALIDFILE(row); row++) { + for (col = 1; VALIDFILE(col); col++) { + int p = b[ROWTOBOARD(row)][COLTOBOARD(col)].icon; + + if (pgn_piece_to_int(p) == KING) { + if (val_piece_side(g.turn, p)) { + *kr = row; + *kc = col; + } + else { + /* Opponent. */ + *okr = row; + *okc = col; + } + } + } + } +} + +int _checktest(GAME *g, BOARD b, int kr, int kc, int okr, int okc, int matetest) +{ + int row, col; + + pgn_switch_turn(g); + + /* See if the move would put our opponent in check. */ + for (row = 1; VALIDFILE(row); row++) { + for (col = 1; VALIDFILE(col); col++) { + int srow = 0, scol = 0; + int p = b[ROWTOBOARD(row)][COLTOBOARD(col)].icon; + int pi = pgn_piece_to_int(p); + + if (pi == OPEN_SQUARE) + continue; + + if (pi == PAWN) + scol = col; + + /* See if the move would leave ourselves in check. */ + if (!matetest) { + pgn_switch_turn(g); + + if (find_source_square(g, b, pi, kr, kc, &srow, &scol) == 0) + return -1; + + pgn_switch_turn(g); + } + + if (find_source_square(g, b, pi, okr, okc, &srow, &scol) == 0) { + pgn_switch_turn(g); + return 1; + } + } + } + + pgn_switch_turn(g); + return 0; +} + +int _checkmate_pawn_test(GAME *g, BOARD b, int row, int col, int *srow, int *scol) +{ + int r, c; + + r = 0; + c = col; + + if (!_get_source_square(g, b, PAWN, row, col, &r, &c)) + return 0; + + c = col - 1; + + if (!_get_source_square(g, b, PAWN, row, col, &r, &c)) + return 0; + + c = col + 1; + + if (!_get_source_square(g, b, PAWN, row, col, &r, &c)) + return 0; + + return 1; +} + +int _checkmatetest(GAME *g, BOARD b, int kr, int kc, int okr, int okc) +{ + int row, col; + int srow, scol; + int check; + + /* For each square on the board see if each peace has a valid move, and if + * so, see if it would leave ourselves or the opponent in check. + */ + for (row = 1; VALIDFILE(row); row++) { + for (col = 1; VALIDFILE(col); col++) { + int n; + + for (n = 0; n < MAX_PIECES; n++) { + int p; + int nkr = kr, nkc = kc, nokr = okr, nokc = okc; + BOARD oldboard; + + srow = scol = 0; + + if (n == PAWN) { + if (_checkmate_pawn_test(g, b, row, col, &srow, &scol)) + continue; + } + else { + if (_get_source_square(g, b, n, row, col, &srow, &scol)) + continue; + } + + /* Valid move. */ + memcpy(oldboard, b, sizeof(BOARD)); + p = b[ROWTOBOARD(srow)][COLTOBOARD(scol)].icon; + b[ROWTOBOARD(row)][COLTOBOARD(col)].icon = p; + b[ROWTOBOARD(srow)][COLTOBOARD(scol)].icon = + pgn_int_to_piece(g->turn, OPEN_SQUARE); + + if (pgn_piece_to_int(p) == KING) { + if (piece_side(*g, p) == g->turn) { + nokr = row; + nokc = col; + } + else { + nkr = row; + nkc = col; + } + } + + check = _checktest(g, b, nkr, nkc, nokr, nokc, 1); + memcpy(b, oldboard, sizeof(BOARD)); + + if (check == 0) + goto done; + } + } + } + + check = 1; + + if (g->turn == WHITE) + result = BLACKWINS; + else + result = WHITEWINS; + +done: + return (check != 0) ? 1 : 0; +} + +/* FIXME */ +int _drawtest(GAME *g, BOARD b) +{ + int row, col; + int other = 0; + + if (pgn_config.fmd && g->ply >= 50) + return 1; + + for (row = 1; VALIDFILE(row); row++) { + for (col = 1; VALIDFILE(col); col++) { + int p = b[ROWTOBOARD(row)][COLTOBOARD(col)].icon; + int piece = pgn_piece_to_int(p); + + switch (piece) { + case PAWN: + return 0; + break; + case KING: + break; + default: + other++; + break; + } + } + } + + if (!other) + return 1; + + return 0; +} +#endif diff --git a/libchess/move.h b/libchess/move.h index 6f77820..c20a26c 100644 --- a/libchess/move.h +++ b/libchess/move.h @@ -19,11 +19,8 @@ #ifndef MOVE_H #define MOVE_H -#define INTTOROW(r) (r + '0') -#define INTTOCOL(c) (c + ('a' - 1)) - -int result; -int capture; +static int capture; +static int castle; enum { WHITEWINS, BLACKWINS, DRAW @@ -33,13 +30,4 @@ enum { KINGSIDE = 1, QUEENSIDE }; -int _get_source_yx(GAME *, BOARD, int, int, int, int *, int *); -char *_a2a4tosan(GAME *, BOARD, char *); -int _val_piece_side(char, int); -int _castle_move(GAME *, BOARD, char); -void _kingsquare(GAME, BOARD, int *, int *, int *, int *); -int _drawtest(GAME *, BOARD); -int _checktest(GAME *, BOARD, int, int, int, int, int); -int _checkmatetest(GAME *, BOARD, int, int, int, int); - #endif diff --git a/libchess/pgn.c b/libchess/pgn.c index 490303e..34e273d 100644 --- a/libchess/pgn.c +++ b/libchess/pgn.c @@ -40,7 +40,6 @@ #endif #include "chess.h" -#include "move.h" #include "pgn.h" #ifdef DEBUG @@ -88,48 +87,6 @@ void pgn_reset_valid_moves(BOARD b) } } -/* - * Sets valid moves from game 'g' using board 'b'. The valid moves are for the - * piece on the board 'b' at 'rank' and 'file'. Returns nothing. - */ -void pgn_get_valid_moves(GAME *g, BOARD b, int r, int f) -{ - int row, col; - int p = pgn_piece_to_int(b[ROWTOBOARD(r)][COLTOBOARD(f)].icon); - - /* - * Don't update board 'b'. Only check for valid moves. - */ - validate = 1; - - for (row = 1; VALIDFILE(row); row++) { - for (col = 1; VALIDFILE(col); col++) { - int sr = 0, sc = 0; - - if (_get_source_yx(g, b, p, row, col, &sr, &sc)) { - sr = 0; - sc = f; - - if (_get_source_yx(g, b, p, row, col, &sr, &sc)) { - sc = 0; - sr = r; - - if (_get_source_yx(g, b, p, row, col, &sr, &sc)) { - continue; - } - } - } - - if (sr != r || sc != f) - continue; - - b[ROWTOBOARD(row)][COLTOBOARD(col)].valid = 1; - } - } - - validate = 0; -} - /* * Toggles g->turn. Returns nothing. */ @@ -402,7 +359,7 @@ int pgn_board_update(GAME *g, BOARD b, int n) p = h->move; - if ((ret = pgn_validate_move(g, tb, &p)) != E_PGN_OK) + if ((ret = pgn_parse_move(g, tb, &p)) != E_PGN_OK) break; pgn_switch_turn(g); @@ -872,7 +829,7 @@ static int move_text(GAME *g, FILE *fp) p = m; - if (pgn_validate_move(g, g->b, &p)) { + if (pgn_parse_move(g, g->b, &p)) { pgn_switch_turn(g); return 1; } @@ -2162,300 +2119,3 @@ void pgn_reset_enpassant(BOARD b) b[r][c].enpassant = 0; } } - -/* - * Valididate move 'mp' against the game state 'g' and game board 'b' and - * update board 'b'. 'mp' is updated to SAN format for moves which aren't - * (a2a4 or e8Q for example). Returns E_PGN_PARSE if there was a move text - * parsing error, E_PGN_INVALID if the move is invalid or E_PGN_OK if - * successful. - */ -int pgn_validate_move(GAME *g, BOARD b, char **mp) -{ - char *p; - int piece, dstpiece; - int i = 0; - int srow = 0, scol = 0, row, col; - int dist = 0; - int promo = -1; - int kr = 0, kc = 0, okr = 0, okc = 0; - char *m = *mp; - - if ((m = _a2a4tosan(g, b, m)) == NULL) - return E_PGN_PARSE; - - if (strlen(m) < 2) - return E_PGN_PARSE; - - capture = 0; - srow = row = col = scol = promo = piece = 0; -again: - p = (m) + strlen(m); - - while (!isdigit(*--p) && *p != 'O') { - if (*p == '=') { - promo = pgn_piece_to_int(i); - i = 0; - break; - } - - i = *p; - *p = '\0'; - } - - // Old promotion text (e8Q). Convert to SAN. - if (pgn_piece_to_int(i) != -1) { - p = (m) + strlen(m); - *p++ = '='; - *p++ = i; - *p = '\0'; - goto again; - } - - if (strlen(m) < 2) - return E_PGN_PARSE; - - p = m; - - /* Skip 'P'. */ - if (pgn_piece_to_int(*p) == PAWN) - p++; - /* Pawn. */ - if (VALIDCOL(*p)) { - for (i = 0; *p; i++) { - if (VALIDCOL(*p)) { - if (i > 0) - col = COLTOINT(*p++); - else - col = scol = COLTOINT(*p++); - } - else if (VALIDROW(*p)) { - if (1 > 1) - row = ROWTOINT(*p++); - else - row = ROWTOINT(*p++); - } - else if (*p == 'x') { - col = COLTOINT(*++p); - row = ROWTOINT(*++p); - capture++; - } - else if (*p == '=') { - if (promo == -1 || promo == KING || promo == PAWN) - return E_PGN_PARSE; - - *p++ = '='; - *p++ = toupper(pgn_int_to_piece(g->turn, promo)); - *p = '\0'; - break; - } - else { -#ifdef DEBUG - DUMP("Pawn (move: '%s'): %c\n", m, *p++); -#else - p++; -#endif - } - } - - if (_get_source_yx(g, b, PAWN, row, col, &srow, &scol)) - return E_PGN_INVALID; - } - /* Not a pawn. */ - else { - if (strcmp(m, "O-O") == 0) - g->castle = KINGSIDE; - else if (strcmp(m, "O-O-O") == 0) - g->castle = QUEENSIDE; - else { - p = m; - - if ((piece = pgn_piece_to_int(*p++)) == -1) - return E_PGN_PARSE; - - if (strlen(m) > 3) { - if (isdigit(*p)) - srow = ROWTOINT(*p++); - else if (VALIDCOL(*p)) - scol = COLTOINT(*p++); - - if (*p == 'x') { - capture++; - p++; - } - } - - col = COLTOINT(*p++); - row = ROWTOINT(*p++); - - /* Get the source row and column. */ - if (srow == 0) { - if (scol > 0) { - for (i = 1; VALIDFILE(i); i++) { - int fpiece = b[ROWTOBOARD(i)][COLTOBOARD(scol)].icon; - - if (piece == pgn_piece_to_int(fpiece) && - _val_piece_side(g->turn, fpiece)) { - srow = i; - break; - } - } - - if (srow == 0) - return E_PGN_INVALID; - } - else { - if (_get_source_yx(g, b, piece, row, col, &srow, &scol)) - return E_PGN_INVALID; - } - } - else if (scol == 0) { - if (srow > 0) { - for (i = 1; VALIDFILE(i); i++) { - int fpiece = pgn_piece_to_int(b[ROWTOBOARD(srow)][COLTOBOARD(i)].icon); - - if (piece == fpiece) { - scol = i; - break; - } - } - - if (scol == 0) - return E_PGN_INVALID; - } - else { - if (_get_source_yx(g, b, piece, row, col, &srow, &scol)) - return E_PGN_INVALID; - } - } - } - } - - piece = pgn_piece_to_int(b[ROWTOBOARD(srow)][COLTOBOARD(scol)].icon); - dist = abs(srow - row); - - if (!validate) { - if (piece == PAWN || capture) - g->ply = 0; - else - g->ply++; - - pgn_reset_enpassant(b); - - if (piece == PAWN && dist == 2) { - if (g->turn == WHITE) - b[ROWTOBOARD(srow - 1)][COLTOBOARD(scol)].enpassant = 1; - else - b[ROWTOBOARD(srow + 1)][COLTOBOARD(scol)].enpassant = 1; - - SET_FLAG(g->flags, GF_ENPASSANT); - } - else { - CLEAR_FLAG(g->flags, GF_ENPASSANT); - } - } - - dstpiece = piece = b[ROWTOBOARD(row)][COLTOBOARD(col)].icon; - - if (g->castle) { - if (_castle_move(g, b, g->castle)) { - g->castle = 0; - return E_PGN_INVALID; - } - - goto done; - } - - if (pgn_piece_to_int(piece) != OPEN_SQUARE) { - if (_val_piece_side(g->turn, piece)) - return E_PGN_INVALID; - } - - if (!validate) { - if (promo) - piece = pgn_int_to_piece(g->turn, promo); - else - piece = b[ROWTOBOARD(srow)][COLTOBOARD(scol)].icon; - } - else - piece = b[ROWTOBOARD(srow)][COLTOBOARD(scol)].icon; - - if (!validate) { - b[ROWTOBOARD(srow)][COLTOBOARD(scol)].icon = - pgn_int_to_piece(g->turn, OPEN_SQUARE); - b[ROWTOBOARD(row)][COLTOBOARD(col)].icon = piece; - } - -done: - if (!validate && capture && pgn_piece_to_int(dstpiece) == ROOK) { - if (row == 1 && col == 1) - CLEAR_FLAG(g->flags, GF_WQ_CASTLE); - else if (row == 8 && col == 1) - CLEAR_FLAG(g->flags, GF_BQ_CASTLE); - else if (row == 1 && col == 8) - CLEAR_FLAG(g->flags, GF_WK_CASTLE); - else if (row == 8 && col == 8) - CLEAR_FLAG(g->flags, GF_BK_CASTLE); - } - - _kingsquare(*g, b, &kr, &kc, &okr, &okc); - pgn_switch_turn(g); - - if (g->castle) { - p = m + strlen(m); - g->castle = 0; - } - - CLEAR_FLAG(g->flags, GF_GAMEOVER); - i = validate; - validate = 1; - - if (_drawtest(g, b)) { - pgn_tag_add(&g->tag, "Result", "1/2-1/2"); - SET_FLAG(g->flags, GF_GAMEOVER); - } - else { - switch (_checktest(g, b, kr, kc, okr, okc, 0)) { - case 0: - break; - case -1: - validate = i; - pgn_switch_turn(g); - return E_PGN_INVALID; - default: - if (_checkmatetest(g, b, kr, kc, okr, okc)) { - *p++ = '#'; - - if (result == WHITEWINS) - pgn_tag_add(&g->tag, "Result", "1-0"); - else if (result == BLACKWINS) - pgn_tag_add(&g->tag, "Result", "1-0"); - - SET_FLAG(g->flags, GF_GAMEOVER); - } - else - *p++ = '+'; - - *p = '\0'; - break; - } - } - - pgn_switch_turn(g); - validate = i; - *mp = m; - return E_PGN_OK; -} - -/* - * Like pgn_validate_move() but don't modify game flags in 'g' or board 'b'. - */ -int pgn_validate_only(GAME *g, BOARD b, char **m) -{ - int ret; - - validate = 1; - ret = pgn_validate_move(g, b, m); - validate = 0; - return ret; -} diff --git a/libchess/pgn.h b/libchess/pgn.h index 4778cd9..e71f8b2 100644 --- a/libchess/pgn.h +++ b/libchess/pgn.h @@ -43,5 +43,6 @@ int pgn_write_turn; int pgn_mpl; int pgn_lastc; int pgn_isfile; +int pgn_count; #endif diff --git a/src/cboard.c b/src/cboard.c index a64a424..c700a8c 100644 --- a/src/cboard.c +++ b/src/cboard.c @@ -1641,7 +1641,7 @@ static char *board_to_san(GAME *g, BOARD b) p = str; if (TEST_FLAG(d->flags, CF_HUMAN)) { - if (pgn_validate_move(g, b, &p)) { + if (pgn_parse_move(g, b, &p)) { invalid_move(gindex + 1, p); return NULL; } @@ -1649,7 +1649,7 @@ static char *board_to_san(GAME *g, BOARD b) return p; } - if (pgn_validate_only(g, b, &p)) { + if (pgn_validate_move(g, b, &p)) { invalid_move(gindex + 1, p); return NULL; } @@ -2511,8 +2511,8 @@ static int playmode_keys(chtype c) sp.col = c_col; if (!editmode && config.validmoves) - pgn_get_valid_moves(&game[gindex], game[gindex].b, sp.row, - sp.col); + pgn_find_valid_moves(game[gindex], game[gindex].b, sp.col, + sp.row); paused = 0; break; @@ -2863,6 +2863,7 @@ static int globalkeys(chtype c) return 1; } + /* if (!TEST_FLAG(d->flags, CF_HUMAN) && (!d->engine || d->engine->status == ENGINE_OFFLINE)) { if (start_chess_engine(&game[gindex]) < 0) @@ -2874,6 +2875,7 @@ static int globalkeys(chtype c) pushkey = 'h'; return 1; } + */ pushkey = 0; oldhistorytotal = pgn_history_total(game[gindex].hp); -- 2.11.4.GIT