version 1.4.30b
[polyglot.git] / san.c
diff --git a/san.c b/san.c
new file mode 100644 (file)
index 0000000..2756550
--- /dev/null
+++ b/san.c
@@ -0,0 +1,567 @@
+\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