// board.c

#include <string.h>
#include <stdio.h>
#include <ctype.h>

#include "genmove.h"
#include "util.h"
#include "movelist.h"
#include "material.h"
#include "board.h"
#include "bitboard.h"
#include "move.h"
#include "util.h"
#include "piece.h"
#include "square.h"
#include "epd.h"
#include "atak.h"
#include "game.h"

static const int OrigCboard[64] = 
{ rook,  knight, bishop, queen, king,  bishop, knight, rook,
  pawn,  pawn,   pawn,   pawn,  pawn,  pawn,   pawn,   pawn,
  empty, empty,  empty,  empty, empty, empty,  empty,  empty,
  empty, empty,  empty,  empty, empty, empty,  empty,  empty,
  empty, empty,  empty,  empty, empty, empty,  empty,  empty,
  empty, empty,  empty,  empty, empty, empty,  empty,  empty,
  pawn,  pawn,   pawn,   pawn,  pawn,  pawn,   pawn,   pawn,
  rook,  knight, bishop, queen, king,  bishop, knight, rook };


void BoardUpdateMaterialIndex(board_t *board){
    int material[2][8];
    int color;
    enum Piece piece;
    for(color=white;color<=black;color++){
	for(piece=pawn;piece<=queen;piece++){
	    material[color][piece]=nbits(board->b[color][piece]);
	}
    }

    board->material_index=MaterialToIndex(material);

}


void BoardFromGame(board_t *board, game_t *game, int gamecnt){
  int i;
  int move;
  if(gamecnt==BOARD_LAST){
    gamecnt=game->RealGameCnt;
  }else if(gamecnt==BOARD_FIRST){
    gamecnt=game->InitialGameCnt;
  }
  BoardFromFen(board,game->InitialFen);
  for(i=game->InitialGameCnt;i<gamecnt;i++){
    move=game->boards[i-game->InitialGameCnt].move;
    MoveMake(board,move);
  }
}

int BoardFromFen(board_t *board, const char *fen){
  return ParseEPD(board,fen,NULL,NULL);
}

int BoardState(board_t *board){
  int ret=BOARD_PLAYING;
  movelist_t movelist[1];
  GenMoves (board,movelist);
  if (movelist->gencnt==0){
    if (board->in_check) {
       ret=(board->side==white?BOARD_BLACK_WINS:BOARD_WHITE_WINS);
     }else{
       ret=BOARD_STALEMATE;
     }
  }else if   ((board->GameCnt-board->Game50) >= 100){
    ret=BOARD_50MOVERULE;
  }else if(BoardRepeat(board) >= 2){
    ret=BOARD_3FOLD_REPETITION;
  }
  return ret;
}

void BoardToFen(board_t *board, char *fen){
  int r, c, sq, k;
  char c1;
  char tmp[MAXSTR];

  fen[0]=0;
  for (r = A8; r >= A1; r -= 8){
      k = 0;
      for (c = 0; c < 8; c++){
	  sq = r + c;
	  if (board->cboard[sq] == empty){
	    k++;
	  }else{
	    if (k){
	      sprintf (tmp, "%1d", k);
	      strcat(fen,tmp);
	    }
	    k = 0;
	    c1 = notation[board->cboard[sq]];
	    if (bitboards.BitPosArray[sq] & board->friends[black]){
	      c1 = tolower (c1);
	    }
	    sprintf (tmp, "%c", c1);
	    strcat(fen,tmp);
	  }
      }
      if (k){
	sprintf (tmp, "%1d", k);
	strcat(fen,tmp);
      }
      if (r > A1){
	sprintf (tmp, "/");
	strcat(fen,tmp);
      }
  }
 
  /* Other stuff */
  sprintf (tmp, (board->side == white ? " w " : " b "));
  strcat(fen,tmp);
  
  if (board->flag & WKINGCASTLE){
    sprintf (tmp, "K");
    strcat(fen,tmp);
  }
  if (board->flag & WQUEENCASTLE){
    sprintf (tmp, "Q");
    strcat(fen,tmp);
  }
  if (board->flag & BKINGCASTLE){
    sprintf (tmp, "k");
    strcat(fen,tmp);
  }
  if (board->flag & BQUEENCASTLE){
    sprintf (tmp, "q");
    strcat(fen,tmp);
  }
  if (!(board->flag & (WCASTLE | BCASTLE))){
    sprintf (tmp, "-");
    strcat(fen,tmp);
  }
  sprintf (tmp, " %s", (board->ep > -1 ? algbr[board->ep] : "-"));
  strcat(fen,tmp);
  sprintf (tmp," %d",board->GameCnt-board->Game50);
  strcat(fen,tmp);
  sprintf (tmp," %d",(board->GameCnt+2)/2);
  strcat(fen,tmp);
}

 


int BoardRepeat (board_t *board){
   int i, k;
   k = 0;
   for (i = board->GameCnt-4; i >= board->Game50; i-=2) 
   {
      if (board->stack[i-board->Game50] == board->HashKey)
         k++;
   }
   return (k);
}

int BoardIsEqual(board_t *first, board_t *second){
    int ret;
    ret=0;
    if(memcmp(&first->b,&second->b,sizeof(first->b))!=0){
	ret=-1;
    }else if(memcmp(&first->friends,&second->friends,sizeof(first->friends))!=0){
	ret=-2;
    }else if(memcmp(&first->material,&second->material,sizeof(first->material))!=0){
	ret=-3;
    }else if(memcmp(&first->pmaterial,&second->pmaterial,sizeof(first->pmaterial))!=0){
	ret=-4;
    }else if(memcmp(&first->castled,&second->castled,sizeof(first->castled))!=0){
	ret=-5;
    }else if(memcmp(&first->king,&second->king,sizeof(first->king))!=0){
	ret=-6;
    }else if(memcmp(&first->cboard,&second->cboard,sizeof(first->cboard))!=0){
	ret=-7;
    }else if(memcmp(&first->Mvboard,&second->Mvboard,sizeof(first->Mvboard))!=0){
	ret=-8;
    }else if(memcmp(&first->stack,&second->stack,sizeof(first->stack))!=0){
	ret=-9;
    }else if(first->ep!=second->ep){
	ret=1;
    }else  if(first->flag!=second->flag){
	ret=2;
    }else  if(first->blocker!=second->blocker){
	ret=3;
    }else  if(first->side!=second->side){
	ret=7;
    }else  if(first->Game50!=second->Game50){
	ret=8;
    }else  if(first->GameCnt!=second->GameCnt){
	ret=9;
    }else  if(first->HashKey!=second->HashKey){
	ret=10;
    }else  if(first->phase!=second->phase){ 
	ret=11;
    }else  if(first->PawnHashKey!=second->PawnHashKey){
	ret=12;
    }else if(first->in_check!=second->in_check){
	ret=13;
    }else if(first->xin_check!=second->xin_check){
	ret=14;
    }
    return ret;
}



short BoardValidate (board_t *board)
/***************************************************************************
 *
 *  Check the board to make sure that its valid.  Some things to check are
 *  a.  Both sides have only 1 king.
 *  b.  Side not on the move must not be in check.
 *  c.  If en passant square is set, check it is possible.
 *  d.  Check if castling status are all correct.
 *
 ***************************************************************************/
{
    int side, xside, sq, i;
    board_t board1[1];

    int ret=0;
    if (nbits (board->b[white][king]) != 1) {
	ret=1;
	goto fini;
    }
    if (nbits (board->b[black][king]) != 1) {
	ret=2;
	goto fini;
    }
    side = board->side;  
    xside = 1^side;

    if (board->ep > -1)
	{
	    sq = board->ep + (xside == white ? 8 : -8);
	    if (!(bitboards.BitPosArray[sq] & board->b[xside][pawn])){
		ret=4;
		goto fini;
	    }
	}

    if (board->flag & WKINGCASTLE)
	{
	    if (!(bitboards.BitPosArray[E1] & board->b[white][king])){
		ret=5;
		goto fini;
	    }
	    if (!(bitboards.BitPosArray[H1] & board->b[white][rook])){
		ret=6;
		goto fini;
	    }
	}
    if (board->flag & WQUEENCASTLE)
	{
	    if (!(bitboards.BitPosArray[E1] & board->b[white][king])){
		ret=7;
		goto fini;
	    }
	    if (!(bitboards.BitPosArray[A1] & board->b[white][rook])){
		ret=8;
		goto fini;;
	    }
	}
    if (board->flag & BKINGCASTLE)
	{
	    if (!(bitboards.BitPosArray[E8] & board->b[black][king])){
		ret=9;
		goto fini;
	    }
	    if (!(bitboards.BitPosArray[H8] & board->b[black][rook])){
		ret=10;
		goto fini;
	    }
	}
    if (board->flag & BQUEENCASTLE)
	{
	    if (!(bitboards.BitPosArray[E8] & board->b[black][king])){
		ret=11;
		goto fini;
	    }
	    if (!(bitboards.BitPosArray[A8] & board->b[black][rook])){
		ret=12;
		goto fini;
	    }
	}
    if(nbits(board->b[white][pawn])>8){
	ret=14;
	goto fini;
    }
    if(nbits(board->b[black][pawn])>8){
	ret=15;
	goto fini;
    }
    if(board->GameCnt<board->Game50){
	ret=16;
	goto fini;
    }
    memcpy(board1,board,sizeof(board_t));
    BoardUpdateFriends(board1);
    BoardUpdateCBoard(board1);
    BoardHashKeyUpdate(board1);
    BoardUpdateMaterialIndex(board1);
    for(i=0;i<64;i++){
	if(board->cboard[i]!=board1->cboard[i]){
	    ret=18;
	    goto fini;
	}
	if(board->Mvboard[i]<0){
	    ret=19;
	    goto fini;
	}
    }
    if(board->HashKey!=board1->HashKey){
	ret=20;
	goto fini;
    }
    if(board->friends[white]!=board1->friends[white]){
	ret=26;
	goto fini;
    }
    if(board->friends[black]!=board1->friends[black]){
	ret=27;
	goto fini;
    }
    if(board->blocker!=board1->blocker){
	ret=28;
	goto fini;
    }
    if(board->phase!=Material[board->material_index].phase){
	ret=29;
	goto fini;
    }
    if(board->in_check!=SqAtakd (board,board->king[board->side], 1^board->side)){
	ret=30;
	goto fini;
    }
    if(board->xin_check!=SqAtakd (board,board->king[1^board->side], board->side)){
	ret=31;
	goto fini;
    }
    if(board->material_index!=board1->material_index){
	ret=32;
	goto fini;
    }
 fini:
    return ret;
}


void BoardShow (board_t *board)     
/*****************************************************************************
 *
 *  Display the board.  Not only that but display some useful information
 *  like whether enpassant is legal and castling state.
 *
 *****************************************************************************/ 
{
   int r, c, sq;
   int i;
   char fen[MAXFEN];

   BoardToFen(board,fen);
   for(i=0;i<board->GameCnt-board->Game50;i++){
     OutputConsole("stack[%d]="U64_FORMAT"\n",i,board->stack[i]);
   }
   OutputConsole("HashKey="U64_FORMAT"\n",board->HashKey);
   OutputConsole("Repeat=%d\n",BoardRepeat(board));
   OutputConsole("FEN=%s\n",fen);
   if (board->side == white){
     OutputConsole("white  ");
   }else{
     OutputConsole("black  ");
   }
   if (board->flag & WKINGCASTLE){
     OutputConsole("K");
   }
   if (board->flag & WQUEENCASTLE){
     OutputConsole("Q");
   }
   if (board->flag & BKINGCASTLE){
     OutputConsole("k");
   }
   if (board->flag & BQUEENCASTLE){
     OutputConsole("q");
   }
   if (board->ep > -1){
     OutputConsole("  %s", algbr[board->ep]);
   }
   OutputConsole("\n");
   for (r = 56; r >= 0; r -= 8){
      for (c = 0; c < 8; c++){
         sq = r + c;
         if (board->b[white][pawn]   & bitboards.BitPosArray[sq]){
	   OutputConsole("P ");
         }else if (board->b[white][knight] & bitboards.BitPosArray[sq]){
	   OutputConsole("N ");
         }else if (board->b[white][bishop] & bitboards.BitPosArray[sq]){
	   OutputConsole("B ");
         }else if (board->b[white][rook]   & bitboards.BitPosArray[sq]){
	   OutputConsole("R ");
         }else if (board->b[white][queen]  & bitboards.BitPosArray[sq]){
	   OutputConsole("Q ");
	 }else if (board->b[white][king]   & bitboards.BitPosArray[sq]){
           OutputConsole("K ");
         }else if (board->b[black][pawn]   & bitboards.BitPosArray[sq]){
           OutputConsole("p ");
         }else if (board->b[black][knight] & bitboards.BitPosArray[sq]){
           OutputConsole("n ");
         }else if (board->b[black][bishop] & bitboards.BitPosArray[sq]){
           OutputConsole("b ");
         }else if (board->b[black][rook]   & bitboards.BitPosArray[sq]){
           OutputConsole("r ");
         }else if (board->b[black][queen]  & bitboards.BitPosArray[sq]){
           OutputConsole("q ");
         }else if (board->b[black][king]   & bitboards.BitPosArray[sq]){
           OutputConsole("k ");
         }else{
           OutputConsole(". ");
	 }
      }
      OutputConsole("\n");
   }
   OutputConsole("\n");
}

void BoardShowEx(board_t *board){
    int r, c;
    
    BoardShow (board);
    
    for (r = 56; r >= 0; r -= 8) {
	for (c = 0; c < 8; c++){
	    OutputConsole("%2d ", board->Mvboard[r + c]);
	}
	OutputConsole("\n");
    }
    OutputConsole("\n");     
}

void BoardCopy(board_t *dst_board, board_t *src_board){
  memcpy(dst_board, src_board, sizeof(board_t));
}


void BoardHashKeyUpdate (board_t *board)
/***************************************************************************
 *
 *  Calculates the hashkey for the current board position.  We sometimes
 *  need to do this especially when loading an EPD  position.
 *  Note:  The hashkey is a 64 bit unsigned integer number.  
 *  Added in pawnhashkey calculation.
 *
 ***************************************************************************/
{
   int sq, piece, color;
   BitBoard b;

   board->PawnHashKey = board->HashKey = (HashType) 0;
   for (color = white; color <= black; color++)
   {
      for (piece = pawn; piece <= king; piece++)
      {
	 b = board->b[color][piece];
	 while (b)
	 {
	    sq = leadz (b);
  	    CLEARBIT (b, sq);
	    board->HashKey ^= hashcode[color][piece][sq];
	    if (piece == pawn)
	       board->PawnHashKey ^= hashcode[color][piece][sq];
	 }
      }
   }

   /* Take into account castling status & en passant */
   if (board->ep > -1)
      board->HashKey ^= ephash[board->ep];
   if (board->flag & WKINGCASTLE)
      board->HashKey ^= WKCastlehash;
   if (board->flag & WQUEENCASTLE)
      board->HashKey ^= WQCastlehash;
   if (board->flag & BKINGCASTLE)
      board->HashKey ^= BKCastlehash;
   if (board->flag & BQUEENCASTLE)
      board->HashKey ^= BQCastlehash;

   if (board->side == white){
      board->HashKey ^= Sidehash;
   }
}

void BoardUpdateFriends (board_t *board)
/***************************************************************************
 *
 *  Update friend and enemy bitboard.
 *
 ***************************************************************************/
{
   register BitBoard *w, *b;

   w = board->b[white];
   b = board->b[black];
   board->friends[white] = 
      w[pawn] | w[knight] | w[bishop] | w[rook] | w[queen] | w[king];
   board->friends[black] = 
      b[pawn] | b[knight] | b[bishop] | b[rook] | b[queen] | b[king];
   board->blocker = board->friends[white] | board->friends[black];
}


void BoardUpdateCBoard (board_t *board)
/**************************************************************************
 *
 *  Updates cboard[].  cboard[i] returns the piece on square i.
 *
 **************************************************************************/
{
   BitBoard b;
   int piece, sq;

   memset (board->cboard, 0, sizeof (board->cboard));
   for (piece = pawn; piece <= king; piece++)
   {
      b = board->b[white][piece] | board->b[black][piece];
      while (b)
      {
         sq = leadz (b);
         CLEARBIT (b, sq);
         board->cboard[sq] = piece;
      }
   }
}


void BoardUpdateMvboard (board_t *board)
/**************************************************************************
 *
 *  Updates Mvboard[].  Mvboard[i] returns the number of times the piece
 *  on square i has moved.  When loading from EPD, if a piece is not on
 *  its original square, set it to 1, otherwise set to 0.
 *
 **************************************************************************/
{
   int sq;
 
   for (sq = 0; sq < 64; sq++)
   {
      if (board->cboard[sq] == empty || board->cboard[sq] == OrigCboard[sq])
         board->Mvboard[sq] = 0;
      else
         board->Mvboard[sq] = 1;
   } 
}   
