/*
 
    Protector -- a UCI chess engine
 
 
 
    Copyright (C) 2009-2010 Raimund Heid (Raimund_Heid@yahoo.com)
 
 
 
    This program is free software: you can redistribute it and/or modify
 
    it under the terms of the GNU General Public License as published by
 
    the Free Software Foundation, either version 3 of the License, or
 
    (at your option) any later version.
 
 
 
    This program is distributed in the hope that it will be useful,
 
    but WITHOUT ANY WARRANTY; without even the implied warranty of
 
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
    GNU General Public License for more details.
 
 
 
    You should have received a copy of the GNU General Public License
 
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 
 
*/
 
 
 
#include "fen.h"
 
#include <string.h>
 
#include <stdlib.h>
 
#include <ctype.h>
 
#include <stdio.h>
 
#include <assert.h>
 
 
 
static int initialized = 0;
 
static Piece pieceCode[256];
 
static char pieceToken[16];
 
 
 
static void initialize()
 
{
 
   if (!initialized)
 
   {
 
      int i;
 
 
 
      for (i = 0; i < 256; i++)
 
      {
 
         pieceCode[i] = NO_PIECE;
 
      }
 
 
 
      pieceCode['K'] = WHITE_KING;
 
      pieceCode['Q'] = WHITE_QUEEN;
 
      pieceCode['R'] = WHITE_ROOK;
 
      pieceCode['B'] = WHITE_BISHOP;
 
      pieceCode['N'] = WHITE_KNIGHT;
 
      pieceCode['P'] = WHITE_PAWN;
 
      pieceCode['k'] = BLACK_KING;
 
      pieceCode['q'] = BLACK_QUEEN;
 
      pieceCode['r'] = BLACK_ROOK;
 
      pieceCode['b'] = BLACK_BISHOP;
 
      pieceCode['n'] = BLACK_KNIGHT;
 
      pieceCode['p'] = BLACK_PAWN;
 
 
 
      pieceToken[WHITE_KING] = 'K';
 
      pieceToken[WHITE_QUEEN] = 'Q';
 
      pieceToken[WHITE_ROOK] = 'R';
 
      pieceToken[WHITE_BISHOP] = 'B';
 
      pieceToken[WHITE_KNIGHT] = 'N';
 
      pieceToken[WHITE_PAWN] = 'P';
 
      pieceToken[BLACK_KING] = 'k';
 
      pieceToken[BLACK_QUEEN] = 'q';
 
      pieceToken[BLACK_ROOK] = 'r';
 
      pieceToken[BLACK_BISHOP] = 'b';
 
      pieceToken[BLACK_KNIGHT] = 'n';
 
      pieceToken[BLACK_PAWN] = 'p';
 
 
 
      initialized = 1;
 
   }
 
}
 
 
 
static int setPieces(const char *fen, Position * position)
 
{
 
   Square square;
 
   Rank rank = RANK_8;
 
   File file = FILE_A;
 
   Piece p;
 
   int index = 0;
 
   int imax 
= (int) (strlen(fen
)) - 1;  
   char current = '\0';
 
 
 
   ITERATE(square)
 
   {
 
      position->piece[square] = NO_PIECE;
 
   }
 
 
 
   while (index <= imax && (current = fen[index]) != ' ')
 
   {
 
      if (current == '/')
 
      {
 
         rank--;
 
         file = FILE_A;
 
      }
 
      else if (current >= '1' && current <= '8')
 
      {
 
         file = (File) (file + current - '0');
 
      }
 
      else
 
      {
 
         p = pieceCode[(int) current];
 
 
 
         if (p != NO_PIECE)
 
         {
 
            position->piece[getSquare(file, rank)] = p;
 
            file++;
 
         }
 
         else
 
         {
 
            return -1;
 
         }
 
      }
 
 
 
      index++;
 
   }
 
 
 
   return rank == RANK_1 ? index : -1;
 
}
 
 
 
static int stringContainsChar(char *string, char c)
 
{
 
   while (*string != '\0')
 
   {
 
      if (*(string++) == c)
 
      {
 
         return 1;
 
      }
 
   }
 
 
 
   return 0;
 
}
 
 
 
int readFen(const char *fen, Position * position)
 
{
 
   int index;
 
 
 
   /*
 
    * Initialize this module.
 
    */
 
   initialize();
 
 
 
   /*
 
    * Get the piece positions.
 
    */
 
   if ((index = setPieces(fen, position)) < 0)
 
   {
 
      return -1;
 
   }
 
 
 
   /*
 
    * Get the active color.
 
    */
 
   while (fen[index] == ' ')
 
   {
 
      index++;
 
   }
 
 
 
   if (stringContainsChar("bw", fen[index]))
 
   {
 
      position->activeColor = (fen[index] == 'w' ? WHITE : BLACK);
 
      index++;
 
   }
 
   else
 
   {
 
      return -1;                /* fen format error */
 
   }
 
 
 
   /*
 
    * Get the castling rights.
 
    */
 
   while (fen[index] == ' ')
 
   {
 
      index++;
 
   }
 
 
 
   position->castlingRights = NO_CASTLINGS;
 
 
 
   while (fen[index] != ' ')
 
   {
 
      switch (fen[index++])
 
      {
 
      case 'K':
 
         position->castlingRights += WHITE_00;
 
         break;
 
      case 'Q':
 
         position->castlingRights += WHITE_000;
 
         break;
 
      case 'k':
 
         position->castlingRights += BLACK_00;
 
         break;
 
      case 'q':
 
         position->castlingRights += BLACK_000;
 
         break;
 
      case '-':
 
         position->castlingRights = NO_CASTLINGS;
 
         break;
 
      default:
 
         return -1;             /* fen format error */
 
      }
 
   }
 
 
 
   /*
 
    * Get the en passant square.
 
    */
 
   while (fen[index] == ' ')
 
   {
 
      index++;
 
   }
 
 
 
   if (fen[index] == '-')
 
   {
 
      position->enPassantSquare = NO_SQUARE;
 
      index++;
 
   }
 
   else if (fen[index] >= 'a' && fen[index] <= 'h' &&
 
            (fen[index + 1] == '3' || fen[index + 1] == '6'))
 
   {
 
      File file = (File) (fen[index++] - 'a');
 
      Rank rank = (Rank) (fen[index++] - '1');
 
 
 
      position->enPassantSquare = getSquare(file, rank);
 
   }
 
   else
 
   {
 
      return -1;                /* fen format error */
 
   }
 
 
 
   /*
 
    * Get the half move clock value.
 
    */
 
   while (fen[index] == ' ')
 
   {
 
      index++;
 
   }
 
 
 
   position
->halfMoveClock 
= atoi(fen 
+ index
); 
 
 
   /*
 
    * Get the move number value.
 
    */
 
   while (fen[index] != ' ')
 
   {
 
      {
 
         return -1;             /* fen format error */
 
      }
 
 
 
      index++;
 
   }
 
 
 
   while (fen[index] == ' ')
 
   {
 
      index++;
 
   }
 
 
 
   position
->moveNumber 
= atoi(fen 
+ index
); 
 
 
   return 0;
 
}
 
 
 
void getFen(const Position * position, char *fen)
 
{
 
   int rank, file;
 
   char buffer[16];
 
 
 
   /*
 
    * Initialize this module.
 
    */
 
   initialize();
 
 
 
   for (rank = RANK_8; rank >= RANK_1; rank--)
 
   {
 
      int emptyCount = 0;
 
 
 
      for (file = FILE_A; file <= FILE_H; file++)
 
      {
 
         Square square = getSquare(file, rank);
 
 
 
         if (position->piece[square] != NO_PIECE)
 
         {
 
            if (emptyCount > 0)
 
            {
 
               *fen++ = ((char) ('0' + emptyCount));
 
               emptyCount = 0;
 
            }
 
 
 
            *fen++ = pieceToken[position->piece[square]];
 
         }
 
         else
 
         {
 
            emptyCount++;
 
         }
 
      }
 
 
 
      if (emptyCount > 0)
 
      {
 
         *fen++ = ((char) ('0' + emptyCount));
 
      }
 
 
 
      if (rank > RANK_1)
 
      {
 
         *fen++ = '/';
 
      }
 
   }
 
 
 
   *fen++ = ' ';
 
   *fen++ = (position->activeColor == WHITE ? 'w' : 'b');
 
   *fen++ = ' ';
 
 
 
   if (position->castlingRights)
 
   {
 
      if (position->castlingRights & WHITE_00)
 
      {
 
         *fen++ = 'K';
 
      }
 
 
 
      if (position->castlingRights & WHITE_000)
 
      {
 
         *fen++ = 'Q';
 
      }
 
 
 
      if (position->castlingRights & BLACK_00)
 
      {
 
         *fen++ = 'k';
 
      }
 
 
 
      if (position->castlingRights & BLACK_000)
 
      {
 
         *fen++ = 'q';
 
      }
 
   }
 
   else
 
   {
 
      *fen++ = '-';
 
   }
 
 
 
   *fen++ = ' ';
 
 
 
   if (position->enPassantSquare != NO_SQUARE)
 
   {
 
      char file = (char) (file(position->enPassantSquare) + 'a');
 
      char rank = (char) (rank(position->enPassantSquare) + '1');
 
 
 
      *fen++ = file;
 
      *fen++ = rank;
 
   }
 
   else
 
   {
 
      *fen++ = '-';
 
   }
 
 
 
   *fen = '\0';
 
   sprintf(buffer
, " %i %i", position
->halfMoveClock
, position
->moveNumber
);  
}
 
 
 
int initializeModuleFen()
 
{
 
   return 0;
 
}
 
 
 
int testModuleFen()
 
{
 
   char *fen1 = FEN_GAMESTART;
 
   char *fen2 =
 
      "rn3rk1/pbppq1pp/1p2pb2/4N2Q/3PN3/3B4/PPP2PPP/R3K2R w KQ - 4 11";
 
   char *fen3 = "8/8/1R5p/q5pk/PR3pP1/7P/8/7K b - g3 0 1";
 
   char buffer[256];
 
   Position position;
 
   Square square;
 
 
 
   readFen(fen1, &position);
 
   getFen(&position, buffer);
 
 
 
   assert(position.
activeColor == WHITE
);  
   assert(position.
enPassantSquare == NO_SQUARE
);  
   assert(position.
halfMoveClock == 0);  
   assert(position.
moveNumber == 1);  
   assert(position.
castlingRights ==  
          (WHITE_00 | WHITE_000 | BLACK_00 | BLACK_000));
 
   assert(position.
piece[A1
] == WHITE_ROOK
);  
   assert(position.
piece[H1
] == WHITE_ROOK
);  
   assert(position.
piece[B1
] == WHITE_KNIGHT
);  
   assert(position.
piece[G1
] == WHITE_KNIGHT
);  
   assert(position.
piece[C1
] == WHITE_BISHOP
);  
   assert(position.
piece[F1
] == WHITE_BISHOP
);  
   assert(position.
piece[D1
] == WHITE_QUEEN
);  
   assert(position.
piece[E1
] == WHITE_KING
);  
 
 
   assert(position.
piece[A8
] == BLACK_ROOK
);  
   assert(position.
piece[H8
] == BLACK_ROOK
);  
   assert(position.
piece[B8
] == BLACK_KNIGHT
);  
   assert(position.
piece[G8
] == BLACK_KNIGHT
);  
   assert(position.
piece[C8
] == BLACK_BISHOP
);  
   assert(position.
piece[F8
] == BLACK_BISHOP
);  
   assert(position.
piece[D8
] == BLACK_QUEEN
);  
   assert(position.
piece[E8
] == BLACK_KING
);  
 
 
   for (square = A2; square <= H2; square++)
 
   {
 
      assert(position.
piece[square
] == WHITE_PAWN
);  
      assert(position.
piece[square 
+ 8] == NO_PIECE
);  
      assert(position.
piece[square 
+ 16] == NO_PIECE
);  
      assert(position.
piece[square 
+ 24] == NO_PIECE
);  
      assert(position.
piece[square 
+ 32] == NO_PIECE
);  
      assert(position.
piece[square 
+ 40] == BLACK_PAWN
);  
   }
 
 
 
   readFen(fen2, &position);
 
   getFen(&position, buffer);
 
 
 
   readFen(fen3, &position);
 
   getFen(&position, buffer);
 
 
 
   return 0;
 
}