// 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 <iostream.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.num_attacks(kp,OppositeColor(side)) == 0 &&
		board.num_attacks(kp + 1,OppositeColor(side)) == 0 &&
		board.num_attacks(kp + 2,OppositeColor(side)) == 0)
		  // 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.num_attacks(kp,OppositeColor(side)) == 0 &&
		board.num_attacks(kp - 1,OppositeColor(side)) == 0 &&
		board.num_attacks(kp - 2,OppositeColor(side)) == 0)
		    // can castle
		    SetMove(kp, kp - 2,
	                    Piece::Invalid,
			    moves, NumMoves);
	 }
      // other moves
      static Square squares[Bearing::MaxBearSq];

      for (int i = 0; i < 16; i++)
      {
	 Square loc = board.PiecePos(board.Side(), i);
	 if (!loc.IsInvalid())
	 {
	    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)
   {
      int *scores = new int[NumMoves];
      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);
      delete[] scores;
   }
   return NumMoves;
}

static BOOL is_stacked(const Board &board, ExtendedMove &emove)
{
     if (emove.PieceMoved().sliding() &&
        !board.pawn_attacks(emove.DestSquare(),board.OppositeSide()))
     {
          // Arasan's attack estimation code does not handle
          // "stacked" attackers.  To partially compensate for
          // this, we extend the search when the capturing piece
          // is "backed up" by a piece of the same color:      
          int dir = board.Direction(emove.StartSquare(),
                          emove.DestSquare());
          if (dir)
          {
              Square sq(emove.StartSquare());
              while (!sq.OnEdge()) 
              {
                 sq -= dir;
                 Piece p = board[sq];
                 if (p.IsEmpty())
                     continue;
                 else if (p.Color() != board.Side())
                     break;
                 if (p.sliding())
                 {
                     BOOL skip = FALSE;
                     Piece::PieceType ptype = p.Type();
                     if (emove.PieceMoved().Type() == Piece::Rook)
                          skip = ptype == Piece::Bishop;
                     else if (emove.PieceMoved().Type() == 
                              Piece::Bishop)
                          skip = ptype == Piece::Rook;
                     else
                     {
                          int abs = Util::Abs(dir);
                          if (abs == 1 || abs == RankIncr)
                              skip = ptype == Piece::Bishop;
                          else
                              skip = ptype == Piece::Rook;
                     }
                     if (!skip)
                     {
                          return TRUE;
                     }
                 }
              }
          }
    }
    return FALSE;
}

int Move_Generator::Generate_Captures(Move * moves)
{
   unsigned NumMoves;
   static int scores[Move_Generator::MaxMoves];
   if (board.CheckStatus() == Board::InCheck)
   {
      NumMoves = Check_Evasions(moves,FALSE);
   }
   else
   {
      NumMoves = 0;
 
      static Square squares[Bearing::MaxBearSq];

      for (int i = 0; i < 16; i++)
      {
	 Square loc = board.PiecePos(board.Side(), i);
	 if (!loc.IsInvalid())
	 {
	    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)
	       {
                  scores[NumMoves] = Piece::Value(Piece::Queen) - 
                                     Piece::Value(Piece::Pawn);
	          SetMove(loc, dest, Piece::Queen, moves, NumMoves);
                  scores[NumMoves] = Piece::Value(Piece::Rook) - 
                                     Piece::Value(Piece::Pawn);
	          SetMove(loc, dest, Piece::Rook, moves, NumMoves);
                  scores[NumMoves] = Piece::Value(Piece::Bishop) - 
                                     Piece::Value(Piece::Pawn);
	          SetMove(loc, dest, Piece::Bishop, moves, NumMoves);
                  scores[NumMoves] = Piece::Value(Piece::Knight) - 
                                     Piece::Value(Piece::Pawn);
	          SetMove(loc, dest, Piece::Knight, moves, NumMoves);
	       }
               else
	       {
                  // Capture move
		  if (!board[dest].IsEmpty() ||
  	              ((piecemoved.Type() == Piece::Pawn) &&
		       (dest.File() != loc.File())))
                  {
                     int est;
                     if (board[dest].Value() > board[loc].Value())
                     {
                        est = board[dest].Value() - board[loc].Value();
                     }
                     else
                     {
                       est =
                         attack_estimate(board,ExtendedMove(board,
                                     Move(loc,dest)));
                     }
                     if (est > 0)
                     {
                        scores[NumMoves] = est;
   	                SetMove(loc, dest, Piece::Invalid, moves,
                        NumMoves);
                     }
                     else
                     {
                        if (is_stacked(board,ExtendedMove(board,
                            loc, dest)))
                        {
                           scores[NumMoves] = 1;
     	                   SetMove(loc, dest, Piece::Invalid, moves,
                                   NumMoves);
                        }
                     }
                  }
	       } 
	    }
	 }
      }
      if (NumMoves)
      {
          Move_Ordering::sort_moves(moves,scores,NumMoves);
      }
   }

   return NumMoves;
}

static BOOL is_pinned(const Board & board, const Square source,
	  const Square dest)
{
   BOOL pin = FALSE;
   if (board.num_attacks(source, board.OppositeSide()) > 0)
   {
      // 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());
   unsigned num_attacks = board.num_attacks(kp, board.OppositeSide());
   const int double_check = num_attacks > 1;
   if (!double_check)
   {
      // try to capture checking piece
      unsigned n = Bearing::Attack(board, kp, board.OppositeSide(), squares);
      ASSERT(n);
      source = squares[0];
      ASSERT(!board[source].IsEmpty() && board[source].Type() != Piece::King);
      n = Bearing::Attack(board, source, board.Side(), squares);
      for (i = 0; i < n; i++)
      {
         if (board[squares[i]].Type() == Piece::King)
	 {
	    // We can capture with the king only if the piece
	    // checking us is undefended.
	    if (board.num_attacks(source, board.OppositeSide()) == 0)
	       moves[num_moves++] = Move(squares[i], source);
	 }
         else
	 {
	    if (!is_pinned(board, squares[i], source))
	       moves[num_moves++] = Move(squares[i], 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++] = Move(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++] = Move(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)
      {
	 Square btwn_squares[8];
	 unsigned nbsq = board.Between(source, kp, btwn_squares);
	 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]))
                {
                   if (p.Type() == Piece::Pawn &&
                       btwn_squares[i].Rank(board.Side()) == 8)
                   {
                      // interposition is a promotion
		      moves[num_moves++] = 
                          Move(squares[j], btwn_squares[i],Piece::Queen);
		      moves[num_moves++] = 
                          Move(squares[j], btwn_squares[i],Piece::Rook);
		      moves[num_moves++] = 
                          Move(squares[j], btwn_squares[i],Piece::Knight);
		      moves[num_moves++] = 
                          Move(squares[j], btwn_squares[i],Piece::Bishop);
                   }
                   else
		      moves[num_moves++] = Move(squares[j], btwn_squares[i]);
                }
	    }
	 }
      }
   }
   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.num_attacks(squares[i], board.OppositeSide()) == 0)
	 if (double_check || 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;
	    for (unsigned j = 0; j < num_attackers && !illegal; j++)
	    {
	       Piece attacker(board[atck_squares[j]]);
	       if (attacker.sliding())
	       {
		  int dir = board.Direction(atck_squares[j], 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++] = Move(kp, squares[i]);
	 }
   }
   return num_moves;
}
