--- /dev/null
+\r
+// san.c\r
+\r
+// includes\r
+\r
+#include <ctype.h>\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <string.h>\r
+\r
+#include "attack.h"\r
+#include "board.h"\r
+#include "list.h"\r
+#include "move.h"\r
+#include "move_gen.h"\r
+#include "move_legal.h"\r
+#include "piece.h"\r
+#include "san.h"\r
+#include "square.h"\r
+#include "util.h"\r
+\r
+// constants\r
+\r
+static const bool UseSlowDebug = FALSE;\r
+\r
+enum ambiguity_t {\r
+ AMBIGUITY_NONE,\r
+ AMBIGUITY_FILE,\r
+ AMBIGUITY_RANK,\r
+ AMBIGUITY_SQUARE\r
+};\r
+\r
+// functions\r
+\r
+static bool san_to_lan (const char san[], const board_t * board, char string[], int size);\r
+static int move_from_lan (const char string[], const board_t * board);\r
+\r
+static int ambiguity (int move, const board_t * board);\r
+\r
+// move_to_san()\r
+\r
+bool move_to_san(int move, const board_t * board, char string[], int size) {\r
+\r
+ int from, to, piece;\r
+ char tmp_string[256];\r
+\r
+ ASSERT(move_is_ok(move));\r
+ ASSERT(board_is_ok(board));\r
+ ASSERT(string!=NULL);\r
+ ASSERT(size>=8);\r
+\r
+ ASSERT(move_is_legal(move,board));\r
+\r
+ if (size < 8) return FALSE;\r
+\r
+ // init\r
+\r
+ from = move_from(move);\r
+ to = move_to(move);\r
+\r
+ string[0] = '\0';\r
+\r
+ // castle\r
+\r
+ if (move_is_castle(move,board)) {\r
+\r
+ if (to > from) {\r
+ strcat(string,"O-O");\r
+ } else {\r
+ strcat(string,"O-O-O");\r
+ }\r
+\r
+ goto check;\r
+ }\r
+\r
+ // from\r
+\r
+ piece = board->square[from];\r
+\r
+ if (piece_is_pawn(piece)) {\r
+\r
+ // pawn\r
+\r
+ if (move_is_capture(move,board)) {\r
+ sprintf(tmp_string,"%c",file_to_char(square_file(from)));\r
+ strcat(string,tmp_string);\r
+ }\r
+\r
+ } else {\r
+\r
+ // piece\r
+\r
+ sprintf(tmp_string,"%c",toupper(piece_to_char(piece)));\r
+ strcat(string,tmp_string);\r
+\r
+ // ambiguity\r
+\r
+ switch (ambiguity(move,board)) {\r
+ case AMBIGUITY_NONE:\r
+ break;\r
+ case AMBIGUITY_FILE:\r
+ sprintf(tmp_string,"%c",file_to_char(square_file(from)));\r
+ strcat(string,tmp_string);\r
+ break;\r
+ case AMBIGUITY_RANK:\r
+ sprintf(tmp_string,"%c",rank_to_char(square_rank(from)));\r
+ strcat(string,tmp_string);\r
+ break;\r
+ case AMBIGUITY_SQUARE:\r
+ if (!square_to_string(from,tmp_string,256)) return FALSE;\r
+ strcat(string,tmp_string);\r
+ break;\r
+ default:\r
+ ASSERT(FALSE);\r
+ break;\r
+ }\r
+ }\r
+\r
+ // capture\r
+\r
+ if (move_is_capture(move,board)) strcat(string,"x");\r
+\r
+ // to\r
+\r
+ if (!square_to_string(to,tmp_string,256)) return FALSE;\r
+ strcat(string,tmp_string);\r
+\r
+ // promote\r
+\r
+ if (move_is_promote(move)) {\r
+ sprintf(tmp_string,"=%c",toupper(piece_to_char(move_promote(move,board))));\r
+ strcat(string,tmp_string);\r
+ }\r
+\r
+ // check\r
+\r
+check:\r
+\r
+ if (move_is_mate(move,board)) {\r
+ strcat(string,"#");\r
+ } else if (move_is_check(move,board)) {\r
+ strcat(string,"+");\r
+ }\r
+\r
+ return TRUE;\r
+}\r
+\r
+// move_from_san()\r
+\r
+int move_from_san(const char string[], const board_t * board) {\r
+\r
+ char s[256];\r
+ int move;\r
+\r
+ ASSERT(string!=NULL);\r
+ ASSERT(board_is_ok(board));\r
+\r
+ san_to_lan(string,board,s,256);\r
+ move = move_from_lan(s,board);\r
+\r
+ ASSERT(!UseSlowDebug||move==move_from_san_debug(string,board));\r
+\r
+ return move;\r
+}\r
+\r
+// move_from_san_debug()\r
+\r
+int move_from_san_debug(const char string[], const board_t * board) {\r
+\r
+ list_t list[1];\r
+ int i, move;\r
+ char move_string[256];\r
+\r
+ ASSERT(string!=NULL);\r
+ ASSERT(board_is_ok(board));\r
+\r
+ gen_legal_moves(list,board);\r
+\r
+ for (i = 0; i < list_size(list); i++) {\r
+ move = list_move(list,i);\r
+ if (!move_to_san(move,board,move_string,256)) ASSERT(FALSE);\r
+ if (my_string_equal(move_string,string)) return move;\r
+ }\r
+\r
+ return MoveNone;\r
+}\r
+\r
+// san_to_lan()\r
+\r
+static bool san_to_lan(const char san[], const board_t * board, char string[], int size) {\r
+\r
+ int len;\r
+ int left, right;\r
+ int c;\r
+ int king, rook;\r
+ char king_string[3], rook_string[3];\r
+\r
+ ASSERT(san!=NULL);\r
+ ASSERT(board_is_ok(board));\r
+ ASSERT(string!=NULL);\r
+ ASSERT(size>=8);\r
+\r
+ // init\r
+\r
+ if (size < 8) return FALSE;\r
+ strcpy(string,"???????");\r
+\r
+ len = strlen(san);\r
+\r
+ left = 0;\r
+ right = len;\r
+\r
+ // skip trailing '+' or '#'\r
+\r
+ if (left < right) {\r
+ c = san[right-1];\r
+ if (c == '+' || c == '#') right--;\r
+ }\r
+\r
+ // castling\r
+\r
+ ASSERT(left==0);\r
+\r
+ if (FALSE) {\r
+\r
+ } else if (right == 3 && strncmp(san,"O-O",3) == 0) {\r
+\r
+ if (board->castle[board->turn][SideH] == SquareNone) return FALSE;\r
+\r
+ king = king_pos(board,board->turn);\r
+ rook = board->castle[board->turn][SideH];\r
+\r
+ square_to_string(king,king_string,3);\r
+ square_to_string(rook,rook_string,3);\r
+\r
+ sprintf(string,"K%s?%s?",king_string,rook_string);\r
+\r
+ } else if (right == 5 && strncmp(san,"O-O-O",5) == 0) {\r
+\r
+ if (board->castle[board->turn][SideA] == SquareNone) return FALSE;\r
+\r
+ king = king_pos(board,board->turn);\r
+ rook = board->castle[board->turn][SideA];\r
+\r
+ square_to_string(king,king_string,3);\r
+ square_to_string(rook,rook_string,3);\r
+\r
+ sprintf(string,"K%s?%s?",king_string,rook_string);\r
+\r
+ } else {\r
+\r
+ // moved piece\r
+\r
+ if (left < right) {\r
+\r
+ c = san[left];\r
+\r
+ if (char_is_piece(c)) {\r
+ string[0] = c;\r
+ left++;\r
+ }\r
+ }\r
+\r
+ // promotion\r
+\r
+ if (left < right) {\r
+\r
+ c = toupper(san[right-1]);\r
+\r
+ if (char_is_piece(c)) {\r
+\r
+ string[6] = c;\r
+ right--;\r
+\r
+ // skip '='\r
+\r
+ if (left < right && san[right-1] == '=') right--;\r
+ }\r
+ }\r
+\r
+ // to-square rank\r
+\r
+ if (left < right) {\r
+\r
+ c = san[right-1];\r
+\r
+ if (char_is_rank(c)) {\r
+ string[5] = c;\r
+ right--;\r
+ }\r
+ }\r
+\r
+ // to-square file\r
+\r
+ if (left < right) {\r
+\r
+ c = san[right-1];\r
+\r
+ if (char_is_file(c)) {\r
+ string[4] = c;\r
+ right--;\r
+ }\r
+ }\r
+\r
+ // captured piece\r
+\r
+ if (left < right) {\r
+\r
+ c = san[right-1];\r
+\r
+ if (char_is_piece(c)) {\r
+ string[3] = c;\r
+ right--;\r
+ }\r
+ }\r
+\r
+ // skip middle '-' or 'x'\r
+\r
+ if (left < right) {\r
+ c = san[right-1];\r
+ if (c == '-' || c == 'x') right--;\r
+ }\r
+\r
+ // from-square file\r
+\r
+ if (left < right) {\r
+\r
+ c = san[left];\r
+\r
+ if (char_is_file(c)) {\r
+ string[1] = c;\r
+ left++;\r
+ }\r
+ }\r
+\r
+ // from-square rank\r
+\r
+ if (left < right) {\r
+\r
+ c = san[left];\r
+\r
+ if (char_is_rank(c)) {\r
+ string[2] = c;\r
+ left++;\r
+ }\r
+ }\r
+\r
+ if (left != right) return FALSE;\r
+ }\r
+\r
+ // end\r
+\r
+ return TRUE;\r
+}\r
+\r
+// move_from_lan()\r
+\r
+static int move_from_lan(const char string[], const board_t * board) {\r
+\r
+ int len;\r
+ int move;\r
+ int promote;\r
+ char s[256];\r
+ int from, to;\r
+ int colour;\r
+ int inc;\r
+ int piece_char;\r
+ int n;\r
+ const uint8 * ptr;\r
+ int piece;\r
+ int side;\r
+\r
+ ASSERT(string!=NULL);\r
+ ASSERT(board_is_ok(board));\r
+\r
+ // init\r
+\r
+ len = strlen(string);\r
+ if (len != 7) return MoveNone;\r
+\r
+ move = MoveNone;\r
+ colour = board->turn;\r
+\r
+ // promote\r
+\r
+ promote = 0;\r
+\r
+ switch (string[6]) {\r
+ case '?': // not a promotion\r
+ break;\r
+ case 'N':\r
+ promote = MovePromoteKnight;\r
+ break;\r
+ case 'B':\r
+ promote = MovePromoteBishop;\r
+ break;\r
+ case 'R':\r
+ promote = MovePromoteRook;\r
+ break;\r
+ case 'Q':\r
+ promote = MovePromoteQueen;\r
+ break;\r
+ default:\r
+ return MoveNone;\r
+ break;\r
+ }\r
+\r
+ // to square\r
+\r
+ s[0] = string[4];\r
+ s[1] = string[5];\r
+ s[2] = '\0';\r
+\r
+ to = square_from_string(s);\r
+ if (to == SquareNone) return MoveNone;\r
+\r
+ // known from square?\r
+\r
+ if (string[1] != '?' && string[2] != '?') {\r
+\r
+ // from square\r
+\r
+ s[0] = string[1];\r
+ s[1] = string[2];\r
+ s[2] = '\0';\r
+\r
+ from = square_from_string(s);\r
+ if (from == SquareNone) return MoveNone;\r
+\r
+ // convert "king slide" castling to KxR\r
+\r
+ if (piece_is_king(board->square[from])\r
+ && square_rank(to) == square_rank(from)\r
+ && abs(to-from) > 1) {\r
+ side = (to > from) ? SideH : SideA;\r
+ to = board->castle[colour][side];\r
+ if (to == SquareNone) return MoveNone;\r
+ }\r
+\r
+ // move\r
+\r
+ move = move_make(from,to) | promote;\r
+\r
+ return move;\r
+ }\r
+\r
+ // pawn non-capture?\r
+\r
+ if (string[0] == '?' && string[1] == '?') {\r
+\r
+ if (board->square[to] != Empty) return MoveNone; // useful?\r
+\r
+ inc = (colour_is_white(colour)) ? +16 : -16;\r
+\r
+ from = to - inc;\r
+ if (board->square[from] == Empty && square_side_rank(to,colour) == Rank4) {\r
+ from -= inc;\r
+ }\r
+\r
+ if (board->square[from] != piece_make_pawn(colour)) { // useful?\r
+ return MoveNone;\r
+ }\r
+\r
+ // move\r
+\r
+ move = move_make(from,to) | promote;\r
+\r
+ return move;\r
+ }\r
+\r
+ // pawn capture?\r
+\r
+ piece_char = string[0];\r
+\r
+ if (piece_char == '?' && string[1] != '?') {\r
+ piece_char = 'P';\r
+ }\r
+\r
+ // attack loop\r
+\r
+ n = 0;\r
+\r
+ for (ptr = board->list[colour]; (from=*ptr) != SquareNone; ptr++) {\r
+\r
+ piece = board->square[from];\r
+\r
+ if (toupper(piece_to_char(piece)) == piece_char) {\r
+ if (piece_attack(board,piece,from,to)) {\r
+ if (TRUE\r
+ && (string[1] == '?' || file_to_char(square_file(from)) == string[1])\r
+ && (string[2] == '?' || rank_to_char(square_rank(from)) == string[2])) {\r
+ if (!is_pinned(board,from,to,colour)) {\r
+ move = move_make(from,to) | promote;\r
+ n++;\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ if (n != 1) move = MoveNone;\r
+\r
+ return move;\r
+}\r
+\r
+// ambiguity()\r
+\r
+static int ambiguity(int move, const board_t * board) {\r
+\r
+ int from, to, piece;\r
+ list_t list[1];\r
+ int i, n, m;\r
+\r
+ // init\r
+\r
+ from = move_from(move);\r
+ to = move_to(move);\r
+ piece = move_piece(move,board);\r
+\r
+ gen_legal_moves(list,board);\r
+\r
+ // no ambiguity?\r
+\r
+ n = 0;\r
+\r
+ for (i = 0; i < list_size(list); i++) {\r
+ m = list_move(list,i);\r
+ if (move_piece(m,board) == piece && move_to(m) == to) {\r
+ n++;\r
+ }\r
+ }\r
+\r
+ if (n == 1) return AMBIGUITY_NONE;\r
+\r
+ // file ambiguity?\r
+\r
+ n = 0;\r
+\r
+ for (i = 0; i < list_size(list); i++) {\r
+ m = list_move(list,i);\r
+ if (move_piece(m,board) == piece && move_to(m) == to) {\r
+ if (square_file(move_from(m)) == square_file(from)) n++;\r
+ }\r
+ }\r
+\r
+ if (n == 1) return AMBIGUITY_FILE;\r
+\r
+ // rank ambiguity?\r
+\r
+ n = 0;\r
+\r
+ for (i = 0; i < list_size(list); i++) {\r
+ m = list_move(list,i);\r
+ if (move_piece(m,board) == piece && move_to(m) == to) {\r
+ if (square_rank(move_from(m)) == square_rank(from)) n++;\r
+ }\r
+ }\r
+\r
+ if (n == 1) return AMBIGUITY_RANK;\r
+\r
+ // square ambiguity\r
+\r
+ return AMBIGUITY_SQUARE;\r
+}\r
+\r
+// end of san.cpp\r
+\r