#include "chess.h"
#include "data.h"
/* last modified 02/24/14 */
/*
*******************************************************************************
* *
* InputMove() is responsible for converting a move from a text string to *
* the internal move format. It allows the so-called "reduced algebraic *
* move format" which makes the origin square optional unless required for *
* clarity. It also accepts as little as required to remove ambiguity from *
* the move, by using GenerateMoves() to produce a set of legal moves that *
* the text can be applied against to eliminate those moves not intended. *
* Hopefully, only one move will remain after the elimination and legality *
* checks. *
* *
*******************************************************************************
*/
int InputMove(TREE * RESTRICT tree, char *text, int ply, int wtm, int silent,
int ponder_list) {
int moves[220], *mv, *mvp, *goodmove = 0;
int piece = -1, capture, promote, give_check;
int ffile, frank, tfile, trank;
int current, i, nleft;
char *goodchar, *tc;
char movetext[128];
static const char pieces[15] =
{ ' ', ' ', 'P', 'p', 'N', 'n', 'B', 'b', 'R', 'r',
'Q', 'q', 'K', 'k', '\0'
};
static const char pro_pieces[15] =
{ ' ', ' ', 'P', 'p', 'N', 'n', 'B', 'b', 'R', 'r', 'Q', 'q',
'K', 'k', '\0'
};
/*
************************************************************
* *
* First, we need to strip off the special characters for *
* check, mate, bad move, good move, and such that might *
* come from a PGN input file. *
* *
************************************************************
*/
*tc = 0;
*tc = 0;
/*
************************************************************
* *
* Check for full coordinate input (f1e1) and handle that *
* if needed. *
* *
************************************************************
*/
return 0;
if ((text[0] >= 'a') && (text[0] <= 'h') && (text[1] >= '1')
&& (text[1] <= '8') && (text[2] >= 'a') && (text[2] <= 'h')
&& (text[3] >= '1') && (text[3] <= '8'))
return InputMoveICS(tree, text, ply, wtm, silent, ponder_list);
/*
************************************************************
* *
* Initialize move structure. If we discover a parsing *
* error, this will cause us to return a move of "0" to *
* indicate some sort of error was detected. *
* *
************************************************************
*/
tree->status[MAXPLY] = tree->status[ply];
strcpy_s(movetext, sizeof (movetext), text); // Pierre-Marie Baty -- use safe version
moves[0] = 0;
piece = 0;
capture = 0;
promote = 0;
give_check = 0;
frank = -1;
ffile = -1;
trank = -1;
tfile = -1;
goodchar
= strchr(movetext
, '#');
if (goodchar)
*goodchar = 0;
/*
************************************************************
* *
* First we look for castling moves which are a special *
* case with an unusual syntax compared to normal moves. *
* *
************************************************************
*/
piece = king;
if (wtm) {
ffile = 4;
frank = 0;
tfile = 6;
trank = 0;
} else {
ffile = 4;
frank = 7;
tfile = 6;
trank = 7;
}
} else if (!strcmp(movetext
, "o-o-o") || !strcmp(movetext
, "o-o-o+")
|| !strcmp(movetext
, "O-O-O") || !strcmp(movetext
, "O-O-O+")
|| !strcmp(movetext
, "0-0-0") || !strcmp(movetext
, "0-0-0+")) {
piece = king;
if (wtm) {
ffile = 4;
frank = 0;
tfile = 2;
trank = 0;
} else {
ffile = 4;
frank = 7;
tfile = 2;
trank = 7;
}
} else {
/*
************************************************************
* *
* OK, it is not a castling move. Check for two "b" *
* characters which might be a piece (bishop) and a file *
* (b-file). The first "b" should be "B" but we allow *
* this to make typing input simpler. *
* *
************************************************************
*/
if ((movetext[0] == 'b') && (movetext[1] == 'b'))
movetext[0] = 'B';
/*
************************************************************
* *
* Check to see if there is a "+" character which means *
* that this move is a check. We can use this to later *
* eliminate all non-checking moves as possibilities. *
* *
************************************************************
*/
give_check = 1;
}
/*
************************************************************
* *
* If this is a promotion, indicated by an "=" in the *
* text, we can pick up the promote-to piece and save it *
* to use later when eliminating moves. *
* *
************************************************************
*/
goodchar
= strchr(movetext
, '=');
goodchar++;
promote
= (strchr(pro_pieces
, *goodchar
) - pro_pieces
) >> 1;
}
/*
************************************************************
* *
* Now for a kludge. ChessBase (and others) can't follow *
* the PGN standard of bxc8=Q for promotion, and instead *
* will produce "bxc8Q" omitting the PGN-standard "=" *
* character. We handle that here so that we can read *
* their non-standard moves. *
* *
************************************************************
*/
else {
char *prom
= strchr(pro_pieces
, movetext
[strlen(movetext
) - 1]);
if (prom) {
promote = (prom - pro_pieces) >> 1;
movetext
[strlen(movetext
) - 1] = 0;
}
}
/*
************************************************************
* *
* Next we extract the last rank/file designators from the *
* text, since the destination is required for all valid *
* non-castling moves. Note that we might not have both a *
* rank and file but we must have at least one. *
* *
************************************************************
*/
current
= strlen(movetext
) - 1;
trank = movetext[current] - '1';
if ((trank >= 0) && (trank <= 7))
movetext[current] = 0;
else
trank = -1;
current
= strlen(movetext
) - 1;
tfile = movetext[current] - 'a';
if ((tfile >= 0) && (tfile <= 7))
movetext[current] = 0;
else
tfile = -1;
/*
************************************************************
* *
* The first character is the moving piece, unless it is a *
* pawn. In this case, the moving piece is omitted and we *
* know what it has to be. *
* *
************************************************************
*/
if (strchr(" PpNnBBRrQqKk", *movetext
)) {
piece
= (strchr(pieces
, movetext
[0]) - pieces
) >> 1;
for (i
= 0; i
< (int) strlen(movetext
); i
++)
movetext[i] = movetext[i + 1];
}
/*
************************************************************
* *
* It is also possible that this move is a capture, which *
* is indicated by a "x" between either the source and *
* destination squares, or between the moving piece and *
* the destination. *
* *
************************************************************
*/
if ((strlen(movetext
)) && (movetext
[strlen(movetext
) - 1] == 'x')) {
capture = 1;
movetext
[strlen(movetext
) - 1] = 0;
} else
capture = 0;
/*
************************************************************
* *
* It is possible to have no source square, but we could *
* have a complete algebraic square designation, or just *
* rank or file, needed to disambiguate the move. *
* *
************************************************************
*/
ffile = movetext[0] - 'a';
if ((ffile < 0) || (ffile > 7)) {
ffile = -1;
frank = movetext[0] - '1';
if ((frank < 0) || (frank > 7))
piece = -1;
} else {
frank = movetext[1] - '1';
if ((frank < 0) || (frank > 7))
piece = -1;
}
}
}
}
}
/*
************************************************************
* *
* Now for the easy part. We first generate all moves if *
* not pondering, or else use a pre-computed list of moves *
* (if pondering) since the board position is not correct *
* for move input analysis. We then loop through the list *
* of moves, using the information we extracted previously *
* , and eliminate all moves that are (a) the wrong piece *
* type; (b) wrong source or destination square; *
* (c) wrong promotion type; (d) should be a capture, *
* check or promotion but is not, or vice-versa. *
* *
************************************************************
*/
if (!piece)
piece = 1;
if (!ponder_list) {
mvp = GenerateCaptures(tree, MAXPLY, wtm, moves);
mvp = GenerateNoncaptures(tree, MAXPLY, wtm, mvp);
} else {
for (i = 0; i < num_ponder_moves; i++)
moves[i] = ponder_moves[i];
mvp = moves + num_ponder_moves;
}
for (mv = &moves[0]; mv < mvp; mv++) {
if (piece && (Piece(*mv) != piece))
*mv = 0;
if ((ffile >= 0) && (File(From(*mv)) != ffile))
*mv = 0;
if (capture && (!Captured(*mv)))
*mv = 0;
if (promote && (Promote(*mv) != promote))
*mv = 0;
if ((frank >= 0) && (Rank(From(*mv)) != frank))
*mv = 0;
if ((tfile >= 0) && (File(To(*mv)) != tfile))
*mv = 0;
if ((trank >= 0) && (Rank(To(*mv)) != trank))
*mv = 0;
if (!ponder_list && *mv) {
MakeMove(tree, MAXPLY, *mv, wtm);
if (Check(wtm) || (give_check && !Check(Flip(wtm)))) {
UnmakeMove(tree, MAXPLY, *mv, wtm);
*mv = 0;
} else
UnmakeMove(tree, MAXPLY, *mv, wtm);
}
}
/*
************************************************************
* *
* Once we have completed eliminating incorrect moves, we *
* hope to have exactly one move left. If none or left, *
* the entered move is illegal. If more than one is left, *
* the move entered is ambiguous. If appropriate, we *
* output some sort of diagnostic message and then return. *
* *
************************************************************
*/
nleft = 0;
for (mv = &moves[0]; mv < mvp; mv++) {
if (*mv) {
nleft++;
goodmove = mv;
}
}
if (nleft == 1)
return *goodmove;
if (!silent) {
if (nleft == 0)
Print(4095, "Illegal move: %s\n", text);
else if (piece < 0)
Print(4095, "Illegal move (unrecognizable): %s\n", text);
else
Print(4095, "Illegal move (ambiguous): %s\n", text);
}
return 0;
}
/* last modified 02/24/14 */
/*
*******************************************************************************
* *
* InputMoveICS() is responsible for converting a move from the ics format *
* [from][to][promote] to the program's internal format. *
* *
*******************************************************************************
*/
int InputMoveICS(TREE * RESTRICT tree, char *text, int ply, int wtm,
int silent, int ponder_list) {
int moves[220], *mv, *mvp, *goodmove = 0;
int piece = -1, promote;
int ffile, frank, tfile, trank;
int i, nleft;
char movetext[128];
static const char pieces[15] =
{ ' ', ' ', 'P', 'p', 'N', 'n', 'B', 'b', 'R', 'r',
'Q', 'q', 'K', 'k', '\0'
};
/*
************************************************************
* *
* Initialize move structure. If we discover a parsing *
* error, this will cause us to return a move of "0" to *
* indicate some sort of error was detected. *
* *
************************************************************
*/
return 0;
tree->status[MAXPLY] = tree->status[ply];
strcpy_s(movetext, sizeof (movetext), text); // Pierre-Marie Baty -- use safe version
moves[0] = 0;
promote = 0;
/*
************************************************************
* *
* First we look for castling moves which are a special *
* case with an unusual syntax compared to normal moves. *
* *
************************************************************
*/
|| !strcmp(movetext
, "0-0")) {
piece = king;
if (wtm) {
ffile = 4;
frank = 0;
tfile = 6;
trank = 0;
} else {
ffile = 4;
frank = 7;
tfile = 6;
trank = 7;
}
} else if (!strcmp(movetext
, "o-o-o") || !strcmp(movetext
, "O-O-O")
|| !strcmp(movetext
, "0-0-0")) {
piece = king;
if (wtm) {
ffile = 4;
frank = 0;
tfile = 2;
trank = 0;
} else {
ffile = 4;
frank = 7;
tfile = 2;
trank = 7;
}
} else {
/*
************************************************************
* *
* Next we extract both rank/file designators from the *
* text, since the destination is required for all valid *
* non-castling moves. *
* *
************************************************************
*/
ffile = movetext[0] - 'a';
frank = movetext[1] - '1';
tfile = movetext[2] - 'a';
trank = movetext[3] - '1';
/*
************************************************************
* *
* If this is a promotion, indicated by an "=" in the *
* text, we can pick up the promote-to piece and save it *
* to use later when eliminating moves. *
* *
************************************************************
*/
if (movetext[4] == '=')
promote
= (strchr(pieces
, movetext
[5]) - pieces
) >> 1;
else if ((movetext[4] != 0) && (movetext[4] != ' '))
promote
= (strchr(pieces
, movetext
[4]) - pieces
) >> 1;
}
/*
************************************************************
* *
* Now for the easy part. We first generate all moves if *
* not pondering, or else use a pre-computed list of moves *
* (if pondering) since the board position is not correct *
* for move input analysis. We then loop through the list *
* of moves, using the information we extracted previously *
* and eliminate all moves that are (a) the wrong piece *
* type; (b) wrong source or destination square; *
* (c) wrong promotion type; (d) should be a capture, *
* check or promotion but is not or vice-versa. *
* *
************************************************************
*/
if (!ponder_list) {
mvp = GenerateCaptures(tree, MAXPLY, wtm, moves);
mvp = GenerateNoncaptures(tree, MAXPLY, wtm, mvp);
} else {
for (i = 0; i < num_ponder_moves; i++)
moves[i] = ponder_moves[i];
mvp = moves + num_ponder_moves;
}
for (mv = &moves[0]; mv < mvp; mv++) {
if (Promote(*mv) != promote)
*mv = 0;
if (Rank(From(*mv)) != frank)
*mv = 0;
if (File(From(*mv)) != ffile)
*mv = 0;
if (Rank(To(*mv)) != trank)
*mv = 0;
if (File(To(*mv)) != tfile)
*mv = 0;
if (!ponder_list && *mv) {
MakeMove(tree, MAXPLY, *mv, wtm);
if (Check(wtm)) {
UnmakeMove(tree, MAXPLY, *mv, wtm);
*mv = 0;
} else
UnmakeMove(tree, MAXPLY, *mv, wtm);
}
}
/*
************************************************************
* *
* Once we have completed eliminating incorrect moves, we *
* hope to have exactly one move left. If none or left, *
* the entered move is illegal. If more than one is left, *
* the move entered is ambiguous. If appropriate, we *
* output some sort of diagnostic message and then return. *
* *
************************************************************
*/
nleft = 0;
for (mv = &moves[0]; mv < mvp; mv++) {
if (*mv) {
nleft++;
goodmove = mv;
}
}
if (nleft == 1)
return *goodmove;
if (!silent) {
if (nleft == 0)
Print(4095, "Illegal move: %s\n", text);
else if (piece < 0)
Print(4095, "Illegal move (unrecognizable): %s\n", text);
else
Print(4095, "Illegal move (ambiguous): %s\n", text);
}
return 0;
}