// Copyright 1994 by Jon Dart.  All Rights Reserved.

#include "stdafx.h"
#include "movegen.h"
#include "bearing.h"
#include "emove.h"
#include "rmove.h"
#include "scoring.h"
#include "moveord.h"
#include "attacke.h"
#include "util.h"
#include "debug.h"
#include "chessio.h"
#include "bititer.h"
#include <iostream.h>
#include <fstream.h>

Move_Generator::Move_Generator(const Board & ABoard,
   const unsigned ply, const Move & last_move):
   board(ABoard), my_ply(ply), my_last_move(last_move)
{
}

Move_Generator::~Move_Generator()
{
}

inline void Move_Generator::
SetMove(const Square source, const Square dest,
	const Piece::PieceType promotion, Move * moves, unsigned &NumMoves)
{
   ASSERT(source.OnBoard());
   ASSERT(dest.OnBoard());
   ASSERT(NumMoves <= Move_Generator::MaxMoves);
   moves[NumMoves] = Move(source, dest, promotion);
   ++NumMoves;
}

int Move_Generator::
Generate_Moves(Move * moves,
	       const BOOL repeatable)
{
   unsigned NumMoves;
   if (board.CheckStatus() == Board::InCheck)
   {
      NumMoves = Check_Evasions(moves,FALSE);
   }
   else
   {
      NumMoves = 0;

      // castling moves
	 const ColorType side = board.Side();
         Board::CastleType CS = board.CastleStatus(side);
         if ((CS == Board::CanCastleEitherSide) ||
             (CS == Board::CanCastleKSide))
	 {
	    const Square kp = board.KingPos(side);
	    if (board[kp + 1].IsEmpty() &&
	        board[kp + 2].IsEmpty() &&
		!board.any_attacks(kp,OppositeColor(side)) &&
		!board.any_attacks(kp + 1,OppositeColor(side)) &&
		!board.any_attacks(kp + 2,OppositeColor(side)))
		  // can castle
		  SetMove(kp, kp + 2,
	                  Piece::Invalid,
			  moves, NumMoves);
	 }
         if ((CS == Board::CanCastleEitherSide) ||
             (CS == Board::CanCastleQSide))
	 {
	    const Square kp = board.KingPos(side);
	    if (board[kp - 1].IsEmpty() &&
	        board[kp - 2].IsEmpty() &&
	        board[kp - 3].IsEmpty() && 
		!board.any_attacks(kp,OppositeColor(side)) &&
		!board.any_attacks(kp - 1,OppositeColor(side)) &&
		!board.any_attacks(kp - 2,OppositeColor(side)))
		    // can castle
		    SetMove(kp, kp - 2,
	                    Piece::Invalid,
			    moves, NumMoves);
	 }
      // other moves
      static Square squares[Bearing::MaxBearSq];
      BitIterator pieces(board.occupied[board.Side()]);
      for (;;)
      {
	 Square loc(pieces.Next());
         if (loc.IsInvalid())
            break; 
         int n = Bearing::BearSq(board, loc, squares);
	 const Piece piecemoved = board[loc];
	 for (int j = 0; j < n; j++)
	 {
	     const Square dest(squares[j]);
	     int promotion =
	       piecemoved.Type() == Piece::Pawn &&
	     dest.Rank(board.Side()) == 8;
	     if (promotion)
	     {
	        SetMove(loc, dest, Piece::Queen, moves, NumMoves);
	        SetMove(loc, dest, Piece::Rook, moves, NumMoves);
	        SetMove(loc, dest, Piece::Bishop, moves, NumMoves);
	        SetMove(loc, dest, Piece::Knight, moves, NumMoves);
	     }
             else 
	        SetMove(loc, dest, Piece::Invalid, moves, NumMoves);
         }
      }
   }
   
   if (repeatable)
   {
      static int scores[MaxMoves];
      for (unsigned i = 0; i < NumMoves; i++)
      {
	 scores[i] = (int)(moves[i].StartSquare()) + ((int)(moves[i].DestSquare()) << 7);
	 switch (moves[i].PromoteTo())
	 {
	 case Piece::Queen:
	    scores[i] &= 0xB000;
	    break;
	 case Piece::Rook:
	    scores[i] &= 0x8000;
	    break;
	 case Piece::Bishop:
	    scores[i] &= 0x4000;
	    break;
	 default:
	    break;
	 }
      }
      Move_Ordering::sort_moves(moves, scores, NumMoves);
   }
   return NumMoves;
}

int Move_Generator::Generate_Captures(Move * moves)
{
   unsigned NumMoves;
   if (board.CheckStatus() == Board::InCheck)
   {
      NumMoves = Check_Evasions(moves,FALSE);
   }
   else
   {
      NumMoves = 0;
 
      static Square squares[Bearing::MaxBearSq];
      const ColorType side = board.Side();
      Bitmap pieces(board.occupied[side]);
      BitIterator piecebits(pieces);
      for(;;)
      {
	 Square loc(piecebits.Next());
         if (loc.IsInvalid())
            break;
	 int n = Bearing::Captures(board, loc, moves+NumMoves);
         NumMoves += n;
      }
   }

   return NumMoves;
}

static BOOL is_pinned(const Board & board, const Square source,
	  const Square dest)
{
   BOOL pin = FALSE;
   if (board.any_attacks(source, board.OppositeSide()))
   {
      // It is possible that the piece we plan to move is pinned.
      Piece PinnedByPiece;
      Square PinnedBySquare;
      int dir;
      if (Bearing::Pinned(board, source, PinnedByPiece,
	  PinnedBySquare, dir))
      {
	 // This code checks for moves in the direction of the pin,
	 // which are ok:
	 int dir1 = Util::Abs((int) source - (int) dest);
	 int dir2 = Util::Abs(dir);
	 if (dir2 == 1 && source.Rank() != dest.Rank())
	    pin = TRUE;
	 else if (dir2 == RankIncr && source.File() != dest.File())
	    pin = TRUE;
	 else if (dir1 % dir2)
	    pin = TRUE;
      }
   }
   return pin;
}

unsigned Move_Generator::Check_Evasions(Move * moves, BOOL captures_only)
{
   int num_moves = 0;
   unsigned n;
   unsigned i;
   static Square squares[36];
   Square source;
   const Square kp = board.KingPos(board.Side());
   Bitmap king_attacks(board.calc_attacks(kp, board.OppositeSide()));
   ASSERT(!king_attacks.is_clear());
   int num_attacks = 0;
   int fo = king_attacks.FirstOne();
   num_attacks = king_attacks.bit_count();
   if (num_attacks == 1)
   {
      // try to capture checking piece
#ifdef _DEBUG

      unsigned n = Bearing::Attack(board, kp, board.OppositeSide(), squares);
	  if (n == 0)
	  {
         // bug
		 ofstream of;
		 of.open("c:\\bad.fen",ios::out);
		 char buf[128];
		 ChessIO::write_fen(board,buf,FALSE);
		 of << buf << endl;
		 of.close();

	  }
      ASSERT(n);
      source = squares[0];
#else
      source = Square(fo);
#endif          
      //ASSERT(!board[source].IsEmpty() && board[source].Type() != Piece::King);
//      n = Bearing::Attack(board, source, board.Side(), squares);
      BitIterator atcks(board.calc_attacks(source,board.Side()));
      for (;;)
      {
         Square sq(atcks.Next());
         if (sq.IsInvalid())
            break;
         if (board[sq].Type() == Piece::King)
	 {
	    // We can capture with the king only if the piece
	    // checking us is undefended.  But always allow a
            // capture *of* the king - for illegal move detection.
	    if (board[source].Type() == Piece::King ||
                !board.any_attacks(source, board.OppositeSide()))
            {
	       moves[num_moves++].set(sq, source);
            }
	 }
         else
	 {
	    if (!is_pinned(board, sq, source))
            {
	       moves[num_moves++].set(sq, source);
            }
	 }
      }
      // Bearing::Attack does not return en passant captures, so try
      // this as a special case
      if (board.EnPassantSq(board.OppositeSide()) == source)
      {
	 Square dest(source + RankIncr * Direction[board.Side()]);
	 Piece myPawn(Piece::Pawn,board.Side());
	 if (source.File() != 8 && board[source + 1] == myPawn)
	 {
	    Piece tmp(board[source]);
	    Piece &place = (Piece &)board[source];
	    place = Piece::EmptyPiece(); // imagine me gone
	    if (!is_pinned(board, source + 1, dest))
	       moves[num_moves++].set(source + 1, dest);
	    place = tmp;
	 }
	 if (source.File() != 1 && board[source - 1] == myPawn)
	 {
	    Piece tmp(board[source]);
	    Piece &place = (Piece &)board[source];
	    place = Piece::EmptyPiece(); // imagine me gone
	    if (!is_pinned(board, source - 1, dest))
	       moves[num_moves++].set(source - 1, dest);
	    place = tmp;
	 }
      }
      if (captures_only)
         return num_moves;
      // try to interpose a piece
      if (board[source].Type() != Piece::Knight &&
          board[source].Type() != Piece::Pawn)
      {
	 static Square btwn_squares[8];
	 unsigned nbsq = board.Between(source, kp, btwn_squares);
         ASSERT(nbsq<8);
	 for (i = 0; i < nbsq; i++)
	 {
            n = Bearing::Attack(board, btwn_squares[i],
		   board.Side(), squares);
	    for (unsigned j = 0; j < n; j++)
	    {
	        Piece p(board[squares[j]]);
                if (p.Type() != Piece::King &&
		    !is_pinned(board, squares[j], btwn_squares[i]))
                {
                   Square dest(btwn_squares[i]);
                   if (p.Type() == Piece::Pawn &&
                       dest.Rank(board.Side()) == 8)
                   {
                      // interposition is a promotion
		      moves[num_moves++].set(
                          squares[j], dest, Piece::Queen);
		      moves[num_moves++].set(
                          squares[j], dest, Piece::Rook);
		      moves[num_moves++].set(
                          squares[j], dest, Piece::Knight);
		      moves[num_moves++].set(
                          squares[j], dest, Piece::Bishop);
                   }
                   else
                   {
		      moves[num_moves++].set(squares[j], dest);
                   }
                }
	    }
	 }
      }
   }
   if (captures_only)
       return num_moves;
   // generate evasive moves
   n = Bearing::BearSq(board, kp, squares);
//   Square atck_squares[8];
//   unsigned num_attackers = Bearing::Attack(board, kp,
//					board.OppositeSide(), atck_squares);
   for (i = 0; i < n; i++)
   {
      if (!board.any_attacks(squares[i], board.OppositeSide()))
	 if (num_attacks > 1 || squares[i] != source)
	 {
	    // We need to do some extra checking, since the board
	    // info on attacks reflects the state before the move,
	    // and we need to be sure that the destination square
	    // is not attacked after the move.
	    BOOL illegal = FALSE;
            BitIterator it(king_attacks);
            int atck_sq;
            while ((atck_sq = it.Next()) != 127 && !illegal)
	    {
	       Piece attacker(board[Square(atck_sq)]);
	       if (attacker.sliding())
	       {
		  int dir = board.Direction(atck_sq, squares[i]);
		  // check for movement in the direction of the
		  // attacker:
		  if (dir != 0 && (kp + dir == squares[i]))
		     illegal = TRUE;
	       }
	    }
	    if (!illegal)
	       moves[num_moves++].set(kp, squares[i]);
	 }
   }
   return num_moves;
}
