version 1.4.30b
[polyglot.git] / fen.c
diff --git a/fen.c b/fen.c
new file mode 100644 (file)
index 0000000..be3ec17
--- /dev/null
+++ b/fen.c
@@ -0,0 +1,392 @@
+\r
+// fen.c\r
+\r
+// includes\r
+\r
+#include <ctype.h>\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+\r
+#include "board.h"\r
+#include "colour.h"\r
+#include "fen.h"\r
+#include "option.h"\r
+#include "piece.h"\r
+#include "square.h"\r
+#include "util.h"\r
+\r
+// "constants"\r
+\r
+// const char * StartFen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w HAha - 0 1";\r
+const char * StartFen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";\r
+\r
+// variables\r
+\r
+static const bool Strict = FALSE;\r
+\r
+// macros\r
+\r
+#define skip_white_space() \\r
+        c=string[pos];\\r
+        if (c != ' ' && c!='\t') my_fatal("board_from_fen(): bad FEN (pos=%d)\n",pos); \\r
+        while(c==' ' || c=='\t') c=string[++pos];\r
+\r
+\r
+// functions\r
+\r
+// board_from_fen()\r
+\r
+bool board_from_fen(board_t * board, const char string[]) {\r
+\r
+   int pos;\r
+   int file, rank, sq;\r
+   int c;\r
+   int i, len;\r
+   int piece;\r
+   int king_pos[ColourNb];\r
+\r
+   ASSERT(board!=NULL);\r
+   ASSERT(string!=NULL);\r
+\r
+   board_clear(board);\r
+\r
+   king_pos[White] = SquareNone;\r
+   king_pos[Black] = SquareNone;\r
+\r
+   pos = 0;\r
+   c = string[pos];\r
+\r
+   // piece placement\r
+\r
+   for (rank = 7; rank >= 0; rank--) {\r
+\r
+      for (file = 0; file < 8;) {\r
+\r
+         sq = square_make(file,rank);\r
+\r
+         if (c >= '1' && c <= '8') { // empty square(s)\r
+\r
+            len = c - '0';\r
+            if (file + len > 8) my_fatal("board_from_fen(): bad FEN (pos=%d)\n",pos);\r
+\r
+            for (i = 0; i < len; i++) {\r
+               board->square[sq++] = Empty;\r
+               file++;\r
+            }\r
+\r
+         } else { // piece\r
+\r
+            piece = piece_from_char(c);\r
+            if (piece == PieceNone256) my_fatal("board_from_fen(): bad FEN (pos=%d)\n",pos);\r
+\r
+            if (piece_is_king(piece)) king_pos[piece_colour(piece)] = sq;\r
+\r
+            board->square[sq++] = piece;\r
+            file++;\r
+         }\r
+\r
+         c = string[++pos];\r
+      }\r
+\r
+      if (rank > 0) {\r
+         if (c != '/') my_fatal("board_from_fen(): bad FEN (pos=%d)\n",pos);\r
+         c = string[++pos];\r
+     }\r
+   }\r
+\r
+   // active colour\r
+\r
+   skip_white_space();\r
+\r
+   switch (c) {\r
+   case 'w':\r
+      board->turn = White;\r
+      break;\r
+   case 'b':\r
+      board->turn = Black;\r
+      break;\r
+   default:\r
+      my_fatal("board_from_fen(): bad FEN (pos=%d)\n",pos);\r
+      break;\r
+   }\r
+\r
+   c = string[++pos];\r
+\r
+   // castling\r
+\r
+   skip_white_space();\r
+\r
+   board->castle[White][SideH] = SquareNone;\r
+   board->castle[White][SideA] = SquareNone;\r
+   board->castle[Black][SideH] = SquareNone;\r
+   board->castle[Black][SideA] = SquareNone;\r
+\r
+   if (c == '-') { // no castling rights\r
+\r
+      c = string[++pos];\r
+\r
+   } else {\r
+\r
+      // TODO: filter out illegal rights\r
+\r
+      do {\r
+\r
+         if (FALSE) {\r
+\r
+         } else if (c == 'K') {\r
+\r
+            for (sq = H1; sq > king_pos[White]; sq--) {\r
+               if (board->square[sq] == WhiteRook256) {\r
+                  board->castle[White][SideH] = sq;\r
+                  break;\r
+               }\r
+            }\r
+\r
+         } else if (c == 'Q') {\r
+\r
+            for (sq = A1; sq < king_pos[White]; sq++) {\r
+               if (board->square[sq] == WhiteRook256) {\r
+                  board->castle[White][SideA] = sq;\r
+                  break;\r
+               }\r
+            }\r
+\r
+         } else if (c == 'k') {\r
+\r
+            for (sq = H8; sq > king_pos[Black]; sq--) {\r
+               if (board->square[sq] == BlackRook256) {\r
+                  board->castle[Black][SideH] = sq;\r
+                  break;\r
+               }\r
+            }\r
+\r
+         } else if (c == 'q') {\r
+\r
+            for (sq = A8; sq < king_pos[Black]; sq++) {\r
+               if (board->square[sq] == BlackRook256) {\r
+                  board->castle[Black][SideA] = sq;\r
+                  break;\r
+               }\r
+            }\r
+\r
+         } else if (c >= 'A' && c <= 'H') {\r
+\r
+            // white castling right\r
+\r
+            sq = square_make(file_from_char(tolower(c)),Rank1);\r
+\r
+            if (sq > king_pos[White]) { // h side\r
+               board->castle[White][SideH] = sq;\r
+            } else { // a side\r
+               board->castle[White][SideA] = sq;\r
+            }\r
+\r
+         } else if (c >= 'a' && c <= 'h') {\r
+\r
+            // black castling right\r
+\r
+            sq = square_make(file_from_char(tolower(c)),Rank8);\r
+\r
+            if (sq > king_pos[Black]) { // h side\r
+               board->castle[Black][SideH] = sq;\r
+            } else { // a side\r
+               board->castle[Black][SideA] = sq;\r
+            }\r
+\r
+         } else {\r
+\r
+            my_fatal("board_from_fen(): bad FEN (pos=%d)\n",pos);\r
+         }\r
+\r
+         c = string[++pos];\r
+\r
+      } while (c != ' ');\r
+   }\r
+\r
+   // en-passant\r
+\r
+   skip_white_space();\r
+\r
+   if (c == '-') { // no en-passant\r
+\r
+      sq = SquareNone;\r
+      c = string[++pos];\r
+\r
+   } else {\r
+\r
+      if (c < 'a' || c > 'h') my_fatal("board_from_fen(): bad FEN (pos=%d)\n",pos);\r
+      file = file_from_char(c);\r
+      c = string[++pos];\r
+\r
+      if (c < '1' || c > '8') my_fatal("board_from_fen(): bad FEN (pos=%d)\n",pos);\r
+      rank = rank_from_char(c);\r
+      c = string[++pos];\r
+\r
+      sq = square_make(file,rank);\r
+   }\r
+\r
+   board->ep_square = sq;\r
+\r
+   // halfmove clock\r
+\r
+   board->ply_nb = 0;\r
+   board->move_nb = 0; // HACK, in case of broken syntax\r
+\r
+   if (c != ' ') {\r
+      if (!Strict) goto update;\r
+      my_fatal("board_from_fen(): bad FEN (pos=%d)\n",pos);\r
+   }\r
+   c = string[++pos];\r
+\r
+   if (!isdigit(c)) {\r
+      if (!Strict) goto update;\r
+      my_fatal("board_from_fen(): bad FEN (pos=%d)\n",pos);\r
+   }\r
+\r
+   board->ply_nb = atoi(&string[pos]);\r
+   do c = string[++pos]; while (isdigit(c));\r
+\r
+   // fullmove number\r
+\r
+   board->move_nb = 0;\r
+\r
+   if (c != ' ') {\r
+      if (!Strict) goto update;\r
+      my_fatal("board_from_fen(): bad FEN (pos=%d)\n",pos);\r
+   }\r
+   c = string[++pos];\r
+\r
+   if (!isdigit(c)) {\r
+      if (!Strict) goto update;\r
+      my_fatal("board_from_fen(): bad FEN (pos=%d)\n",pos);\r
+   }\r
+\r
+   board->move_nb = atoi(&string[pos]) - 1;\r
+   do c = string[++pos]; while (isdigit(c));\r
+\r
+   // board update\r
+\r
+update:\r
+   board_init_list(board);\r
+\r
+   return TRUE;\r
+}\r
+\r
+// board_to_fen()\r
+\r
+bool board_to_fen(const board_t * board, char string[], int size) {\r
+\r
+   int pos;\r
+   int file, rank;\r
+   int sq, piece;\r
+   int c;\r
+   int len;\r
+   int old_pos;\r
+\r
+   ASSERT(board_is_ok(board));\r
+   ASSERT(string!=NULL);\r
+   ASSERT(size>=92);\r
+\r
+   // init\r
+\r
+   if (size < 92) return FALSE;\r
+\r
+   pos = 0;\r
+\r
+   // piece placement\r
+\r
+   for (rank = 7; rank >= 0; rank--) {\r
+\r
+      for (file = 0; file < 8;) {\r
+\r
+         sq = square_make(file,rank);\r
+         piece = board->square[sq];\r
+         ASSERT(piece==Empty||piece_is_ok(piece));\r
+\r
+         if (piece == Empty) {\r
+\r
+            len = 0;\r
+            for (; file < 8 && board->square[square_make(file,rank)] == Empty; file++) {\r
+               len++;\r
+            }\r
+\r
+            ASSERT(len>=1&&len<=8);\r
+            c = '0' + len;\r
+\r
+         } else {\r
+\r
+            c = piece_to_char(piece);\r
+            file++;\r
+         }\r
+\r
+         string[pos++] = c;\r
+      }\r
+\r
+      string[pos++] = '/';\r
+   }\r
+\r
+   string[pos-1] = ' '; // HACK: remove the last '/'\r
+\r
+   // active colour\r
+\r
+   string[pos++] = (colour_is_white(board->turn)) ? 'w' : 'b';\r
+   string[pos++] = ' ';\r
+\r
+   // castling\r
+\r
+   old_pos = pos;\r
+\r
+   if (option_get_bool("Chess960")) {\r
+\r
+      // FEN-960\r
+\r
+      if (board->castle[White][SideH] != SquareNone) {\r
+         string[pos++] = toupper(file_to_char(square_file(board->castle[White][SideH])));\r
+      }\r
+\r
+      if (board->castle[White][SideA] != SquareNone) {\r
+         string[pos++] = toupper(file_to_char(square_file(board->castle[White][SideA])));\r
+      }\r
+\r
+      if (board->castle[Black][SideH] != SquareNone) {\r
+         string[pos++] = tolower(file_to_char(square_file(board->castle[Black][SideH])));\r
+      }\r
+\r
+      if (board->castle[Black][SideA] != SquareNone) {\r
+         string[pos++] = tolower(file_to_char(square_file(board->castle[Black][SideA])));\r
+      }\r
+\r
+   } else {\r
+\r
+      // FEN\r
+\r
+      if (board->castle[White][SideH] != SquareNone) string[pos++] = 'K';\r
+      if (board->castle[White][SideA] != SquareNone) string[pos++] = 'Q';\r
+      if (board->castle[Black][SideH] != SquareNone) string[pos++] = 'k';\r
+      if (board->castle[Black][SideA] != SquareNone) string[pos++] = 'q';\r
+   }\r
+\r
+   if (pos == old_pos) string[pos++] = '-';\r
+\r
+   string[pos++] = ' ';\r
+\r
+   // en-passant\r
+\r
+   if (board->ep_square == SquareNone) {\r
+      string[pos++] = '-';\r
+   } else {\r
+      if (!square_to_string(board->ep_square,&string[pos],3)) return FALSE;\r
+      pos += 2;\r
+   }\r
+\r
+   string[pos++] = ' ';\r
+\r
+   // halfmove clock and fullmove number\r
+\r
+   sprintf(&string[pos],"%d %d",board->ply_nb,board->move_nb+1);\r
+\r
+   return TRUE;\r
+}\r
+\r
+// end of fen.cpp\r
+\r