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