// Copyright 1992-1996 by Jon Dart.  All Rights Reserved.

#include "stdafx.h"
#include "bearing.h"
#include "util.h"
#include "beardata.h"
#include "bititer.h"

const int RankIncr = 8;	// add this to move 1 rank

Bitmap Bearing::knight_attacks[64];
Bitmap Bearing::king_attacks[64];
Bitmap Bearing::pawn_attacks[64][2];
// for each 8-bit value that a rank can have, and each
// offset to a piece within that rank, return the non-zero
// bits closest to the offset bit.
byte Bearing::rank_attacks[256][8];
// for each 8-bit value that a rank can have, and each
// offset to a piece within that rank, return the non-zero
// bits closest to left of the offset bit.
byte Bearing::next_bit_left[256][8];
// for each 8-bit value that a rank can have, and each
// offset to a piece within that rank, return the non-zero
// bits closest to right of the offset bit.
byte Bearing::next_bit_right[256][8];
int Bearing::mobility[256][8];
Bitmap  Bearing::unrot90[256][8];
Bitmap  Bearing::unrotda1[256][8];
Bitmap  Bearing::unrotda8[256][8];
int Bearing::diagonal_length[64] = 
{ 1,2,3,4,5,6,7,8,
  2,3,4,5,6,7,8,1,
  3,4,5,6,7,8,1,2,
  4,5,6,7,8,1,2,3,
  5,6,7,8,1,2,3,4,
  6,7,8,1,2,3,4,5,
  7,8,1,2,3,4,5,6,
  8,1,2,3,4,5,6,7};
int Bearing::Directions[64][64];
    
static inline void 
SetSquare(
	  const Square sq, Square * squares, unsigned &NumSquares)
{
    ASSERT(sq.OnBoard());
    ASSERT(NumSquares < Bearing::MaxBearSq);
    squares[NumSquares++] = sq;
}

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

static void
PawnMoves(const Board & board,
	  const ColorType side, const Square i,
	  Square * squares, unsigned &NumSquares)
{
    Square start, dest, dest2;
    Piece piece;

    start = i;
    dest = side == White ? i - RankIncr : i + RankIncr;
    ASSERT(dest.OnBoard());
    if (board[dest].IsEmpty())
    {
	// 1-square pawn advance

        SetSquare(dest, squares, NumSquares);
	if (i.Rank(board.Side()) == 2)
	{
           // 2-square pawn advance
	   dest2 = (Direction[side] > 0) ?
	    dest + RankIncr : dest - RankIncr;
	   if (board[dest2].IsEmpty())	// 2-square advance
	      SetSquare(dest2, squares, NumSquares);
	}
    }
    // check for possible captures
    if (i.File() != 1)
    {
      dest = (side == White) ? i - (RankIncr + 1) :
      i + (RankIncr - 1);
      piece = board[dest];
      if (!piece.IsEmpty() && piece.Color() != board.Side())
	 SetSquare(dest, squares, NumSquares);
    }
    if (i.File() != 8)
    {
      dest = (side == White) ? i - (RankIncr - 1) :
      i + (RankIncr + 1);
      piece = board[dest];
      if (!piece.IsEmpty() && piece.Color() != board.Side())
         SetSquare(dest, squares, NumSquares);
    }
    Square epsq = board.EnPassantSq(board.OppositeSide());
    if (!epsq.IsInvalid())
    {
	if ((i.File() != 8 && i + 1 == epsq) || 
	    (i.File() != 1 && i - 1 == epsq))
	{
	    Square dest(epsq + RankIncr * Direction[side]);
	    if (board[dest].IsEmpty())
		SetSquare(dest, squares, NumSquares);
	}
    }
}

static void
PawnAttacks(const Board & board,
	  const ColorType side, const Square i,
	  Move *moves, unsigned &NumMoves)
{
    Square start, dest, dest2;
    Piece piece;

    if (i.Rank(side) == 7)
    {
	dest = (side == White) ? i - RankIncr : i + RankIncr;
        piece = board[dest];
	if (piece.IsEmpty())
	{
	    // promotion
	    SetMove(i, dest, Piece::Queen, moves, NumMoves);
	    //SetMove(i, dest, Piece::Rook, moves, NumMoves);
	    //SetMove(i, dest, Piece::Bishop, moves, NumMoves);
	    SetMove(i, dest, Piece::Knight, moves, NumMoves);
	}
    }
    // check for possible captures
    if (i.File() != 1)
    {
      dest = (side == White) ? i - (RankIncr + 1) :
      i + (RankIncr - 1);
      piece = board[dest];
      if (!piece.IsEmpty() && (piece.Color() != board.Side()))
	SetMove(i,dest, Piece::Invalid, moves, NumMoves);
    }
    if (i.File() != 8)
    {
      dest = (side == White) ? i - (RankIncr - 1) :
      i + (RankIncr + 1);
      piece = board[dest];
      if (!piece.IsEmpty() && (piece.Color() != board.Side()))
	SetMove(i, dest, Piece::Invalid, moves, NumMoves);
    }
    Square epsq = board.EnPassantSq(board.OppositeSide());
    if (!epsq.IsInvalid())
    {
	if ((i.File() != 8 && i + 1 == epsq) || 
	    (i.File() != 1 && i - 1 == epsq))
	{
	    Square dest(epsq + RankIncr * Direction[side]);
	    if (board[dest].IsEmpty())
		SetMove(i, dest, Piece::Invalid, moves, NumMoves);
	}
    }
}

#define RBMoves(board, data, squares, NumSquares) \
    Square i; \
    while ((i = *data) != 255 && board[i].IsEmpty()) \
    { \
       SetSquare(i,squares,NumSquares); \
       ++data; \
    } \
    if (i != 255 && board[i].Color() != side) \
       SetSquare(i,squares,NumSquares) \

#define RBAttacks(board, loc, data, moves, NumMoves) \
    Square i; \
    while ((i = *data) != 255 && board[i].IsEmpty()) \
    { \
       ++data; \
    } \
    if (i != 255 && board[i].Color() != side) \
       SetMove(loc, i, Piece::Invalid, moves, NumMoves) \

unsigned 
Bearing::BearSq(const Board & board,
		const Square loc, Square * squares)
{
    unsigned NumSquares = 0;
    int j;

    const ColorType side = board[loc].Color();
    switch (board[loc].Type())
    {
    case Piece::Empty:
	break;
    case Piece::Pawn:
	PawnMoves(board, side,
		  loc, squares, NumSquares);
	break;
    case Piece::Knight:
    {
       const ColorType side = board[loc].Color();
       const byte *data = KnightSquares[(int)loc];
       int i = 0;
       while (i < 8 && *data != 255)
       {
          ++i;
	  Square dest(*data++);
          Piece piece = board[dest];
	  if (piece.IsEmpty() || piece.Color() != side)
	      SetSquare(dest, squares, NumSquares);
       }
    }
    break;
    case Piece::Bishop:
	for (j = 0; j < 4; j++)
	{
	    const byte *data = BishopSquares[loc] + (j*8);
	    RBMoves(board, data, squares, NumSquares);
	}
	break;
    case Piece::Rook:
	for (j = 0; j < 4; j++)
	{
	    const byte *data = RookSquares[loc] + (j*8);
	    RBMoves(board, data, squares, NumSquares);
	}
	break;
    case Piece::Queen:
	for (j = 0; j < 4; j++)
	{
	    const byte *data = BishopSquares[loc] + (j*8);
	    RBMoves(board, data, squares, NumSquares);
	}
	for (j = 0; j < 4; j++)
	{
	    const byte *data = RookSquares[loc] + (j*8);
	    RBMoves(board, data, squares, NumSquares);
	}
	break;
    case Piece::King:
    {
        const byte *data = KingSquares[(int)loc];
        for (int i = 0; i <8 && *data != 255 ;i++)
        {
	   Square dest(*data++);
           Piece piece = board[dest];
	   if (piece.IsEmpty() || piece.Color() != side)
	       SetSquare(dest, squares, NumSquares);
        }
    }
    case Piece::Invalid:
	break;
    }
    return NumSquares;
}

unsigned 
Bearing::Captures(const Board & board,
		  const Square loc, Move *moves)
{
    unsigned NumMoves = 0;
    int j;

    const ColorType side = board[loc].Color();
    switch (board[loc].Type())
    {
    case Piece::Empty:
	break;
    case Piece::Pawn:
	PawnAttacks(board, side,
  		    loc, moves, NumMoves);
	break;
    case Piece::Knight:
       {
           Bitmap dests(knight_attacks[loc]);
           if (side == White)
              dests.and(board.occupied[Black]);
           else
              dests.and(board.occupied[White]);
           BitIterator it(dests);
           for (;;)
           {
              Square sq(it.Next());
              if (sq.IsInvalid())
                  break;
              SetMove(loc,sq,Piece::Invalid,moves,NumMoves);
           }
        }
	break;
    case Piece::Bishop:
	for (j = 0; j < 4; j++)
	{
	    const byte *data = BishopSquares[loc] + (j*8);
	    RBAttacks(board, loc, data, moves, NumMoves);
	}
	break;
    case Piece::Rook:
	for (j = 0; j < 4; j++)
	{
	    const byte *data = RookSquares[loc] + (j*8);
	    RBAttacks(board, loc, data, moves, NumMoves);
	}
	break;
    case Piece::Queen:
	for (j = 0; j < 4; j++)
	{
	    const byte *data = BishopSquares[loc] + (j*8);
	    RBAttacks(board, loc, data, moves, NumMoves);
	}
	for (j = 0; j < 4; j++)
	{
	    const byte *data = RookSquares[loc] + (j*8);
	    RBAttacks(board, loc, data, moves, NumMoves);
	}
	break;
    case Piece::King:
        {
           Bitmap dests(king_attacks[loc]);
           if (side == White)
              dests.and(board.occupied[Black]);
           else
              dests.and(board.occupied[White]);
           BitIterator it(dests);
           for (;;)
           {
              Square sq(it.Next());
              if (sq.IsInvalid())
                  break;
              SetMove(loc,sq,Piece::Invalid,moves,NumMoves);
           }
        }
	break;
    case Piece::Invalid:
	break;
    }
    return NumMoves;
}

unsigned 
Bearing::Attack(const Board & board,
		const Square loc, const ColorType side,
		Square * squares)
{
    ASSERT(loc.OnBoard());
    int index = 0;
    int i, n;
    Square square1, sq;
    Piece my_pawn(Piece::Pawn, side);
    if (side == Black)
    {
	if (board[loc].IsEmpty())
	{
            Square origin(loc-RankIncr);
	    if (origin.OnBoard() &&
	    	board[origin] == my_pawn)
		squares[index++] = origin;
	    if (loc.Rank(side) == 4 && board[origin].IsEmpty() &&
		board[origin - RankIncr] == my_pawn)
		squares[index++] = origin - RankIncr;
	} 
	else if (loc.Rank(side) != 1)
	{
	    // square is occupied, try pawn captures
	    if (loc.File() != 1 &&
	        board[loc - (RankIncr+1)] == my_pawn)
		squares[index++] = loc - (RankIncr+1);
	    if (loc.File() != 8 &&
	        board[loc - (RankIncr-1)] == my_pawn)
		squares[index++] = loc - (RankIncr-1);
	}
    } 
    else
    {
	if (board[loc].IsEmpty())
	{
            Square origin(loc+RankIncr);
	    if (origin.OnBoard() &&
	        board[origin] == my_pawn)
		squares[index++] = origin;
	    if (loc.Rank(side) == 4 && board[origin].IsEmpty() &&
		board[origin + RankIncr] == my_pawn)
		squares[index++] = origin + RankIncr;
	} 
	else if (loc.Rank(side) != 1)
	{
	    if (loc.File() != 8 &&
	        board[loc + RankIncr+1] == my_pawn)
		squares[index++] = loc + RankIncr + 1;
	    if (loc.File() != 1 &&
	        board[loc + RankIncr-1] == my_pawn)
		squares[index++] = loc + RankIncr - 1;
	}
    }
    Piece knight(Piece::Knight, side);
    Piece king(Piece::King, side);
    for (i = 0; i < 8; i++)
    {
	sq = KnightSquares[loc][i];
	if (sq == 255)
	    break;
	if (board[sq] == knight)
	    squares[index++] = sq;
    }
    for (i = 0; i <8; i++)
    {
	sq = KingSquares[loc][i];
	if (sq == 255)
	    break;
	if (board[sq] == king)
	    squares[index++] = sq;
    }
    n = index;
    Piece bishop(Piece::Bishop, side);
    Piece queen(Piece::Queen, side);
    Piece rook(Piece::Rook, side);
    BOOL found;
    for (i = 0; i < 4; i++)
    {
	square1 = loc;
	found = FALSE;
        const byte *data = BishopSquares[loc] + (8*i);
	while (*data != 255)
	{
   	   Piece piece;
	   do
	   {
	       if (*data == 255) break;
	       square1 = *data++;
	       piece = board[square1];
	   } while (piece.IsEmpty());
	   if (piece == bishop || piece == queen)
	   {
	       found = TRUE;
	       ++n;
	       squares[index] = square1;
               break;
	   }
	   else
	       break;
	}
	if (found)
            ++index;
        data = RookSquares[loc] + (8*i);
	square1 = loc;
	found = FALSE;
	for (;;)
	{
	   Piece piece;
	   do
	   {
	       if (*data == 255)
	          break;
	       square1 = *data++;
	       piece = board[square1];
	   } while (piece.IsEmpty());
	   if (piece == rook || piece == queen)
	   {
	       found = TRUE;
	       ++n;
	       squares[index] = square1;
               break;
	   }
	   else
	       break;
	}
	if (found)
	   ++index;
    }
    ASSERT(n < 36);
    return n;
}

BOOL Bearing::Pinned( const Board &board, const Square loc,
     Piece &PinnedByPiece, 
     Square &PinnedBySquare,
     int &dir)
{
     Piece p = board[loc];
     if (p.IsEmpty() || p.Type() == Piece::King)
        return FALSE;
     const ColorType side = p.Color();
     const ColorType oside = OppositeColor(side);
     // We "cheat" on constness by altering board[loc].  But we will
     // put the original contents back .. honest
     Piece &place = (Piece &)board[loc];
     place = Piece::EmptyPiece(); // imagine me gone
     Square ks(board.KingPos(side));
     int offset = (int)loc - (int)ks;
     int signOfOffset = Util::Sign(offset);
     BOOL attackerFound = FALSE;
     if (ks.File() == loc.File())  // possible pin on file
     {
        const byte *data = (signOfOffset > 0) ?
	    RookSquares[loc]+8 : RookSquares[loc];
	Square newsquare;
	Piece attackPiece;
	while (*data != 255)
	{
	    newsquare = *data++;
	    attackPiece = board[newsquare];
	    if (!attackPiece.IsEmpty())
	       break;
	}
	if (attackPiece.Color() == oside && (
	   (attackPiece.Type() == Piece::Rook) ||
	   (attackPiece.Type() == Piece::Queen)))
        {
	    place = p; // put piece back
	    PinnedBySquare = newsquare;
	    PinnedByPiece = attackPiece;
	    dir = ((int)newsquare - (int)loc > 0) ? RankIncr : -RankIncr;
	    attackerFound = TRUE;
	}
    }	
    else if (ks.Rank(White) == loc.Rank(White))  // possible pin on rank
    {
        const byte *data = (signOfOffset > 0) ?
           RookSquares[loc]+16 : RookSquares[loc]+24;
	Square newsquare;
	Piece attackPiece;
	while (*data != 255)
	{
	    newsquare = *data++;
	    attackPiece = board[newsquare];
	    if (!attackPiece.IsEmpty())
	       break;
	}
	if (attackPiece.Color() == oside && (
	   (attackPiece.Type() == Piece::Rook) ||
	   (attackPiece.Type() == Piece::Queen)))
        {
	    place = p; // put piece back
	    PinnedBySquare = newsquare;
	    PinnedByPiece = attackPiece;
	    dir = ((int)newsquare > (int)loc) ? 1 : -1;
	    attackerFound = TRUE;
	}
    }
    else if (ks.Color() == loc.Color())
    {
    if (offset % 9 == 0) // possible diagonal pin
    {
        const byte *data = (signOfOffset > 0) ?
	   BishopSquares[loc]+16 : BishopSquares[loc]+8;
	Square newsquare;
	Piece attackPiece;
	while (*data != 255)
	{
	    newsquare = *data++;
	    attackPiece = board[newsquare];
	    if (!attackPiece.IsEmpty())
	       break;
	}
	if (attackPiece.Color() == oside && (
	   (attackPiece.Type() == Piece::Bishop) ||
	   (attackPiece.Type() == Piece::Queen)))
        {
	    place = p; // put piece back
	    PinnedBySquare = newsquare;
	    PinnedByPiece = attackPiece;
	    dir = ((int)newsquare > (int)loc) ? 9 : -9;
	    attackerFound = TRUE;
	}
    }
    else if (offset % 7 == 0) // possible diagonal pin
    {
        const byte *data = (signOfOffset > 0) ?
	    BishopSquares[loc] + 24 : BishopSquares[loc];
	Square newsquare;
	Piece attackPiece;
	while (*data != 255)
	{
	    newsquare = *data++;
	    attackPiece = board[newsquare];
	    if (!attackPiece.IsEmpty())
	       break;
	}
	if (attackPiece.Color() == oside && (
	   (attackPiece.Type() == Piece::Bishop) ||
	   (attackPiece.Type() == Piece::Queen)))
        {
	    place = p; // put piece back
	    PinnedBySquare = newsquare;
	    PinnedByPiece = attackPiece;
	    dir = ((int)newsquare > (int)loc) ? 7 : -7;
	    attackerFound = TRUE;
	}
    }
    }
    place = p;
    if (attackerFound)
    {
        // see if the path is clear to the king
        Square sq(loc);
	do
	{
	   sq -= dir;
	   if (sq != ks && !board[sq].IsEmpty())
	      return FALSE;
	} while (sq != ks);
        return TRUE;
    }
    else
        return FALSE;
}

void Bearing::init()
{
    for (int i = 0; i < 64; i++)
    {
        const byte *data = KnightSquares[i];
        int j = 0;
        while (j < 8 && *data != 255)
        {
            knight_attacks[i].set(*data++);
            ++j;
        }
        const byte *data2= KingSquares[i];
        j = 0;
        while (j < 8 && *data2 != 255)
        {
            king_attacks[i].set(*data2++);
            ++j;
        }
        
        Square sq(i);
        if (sq.File() != 1)
        {
            if (i-9>=0) pawn_attacks[i][Black].set(i-9);
            if (i+7<=63) pawn_attacks[i][White].set(i+7);
        }
        if (sq.File() != 8)
        {
            if (i-7>=0) pawn_attacks[i][Black].set(i-7);
            if (i+9<=63) pawn_attacks[i][White].set(i+9);
        }
        for (j = 0; j < 64; j++)
        {
            if (i == j)
            {
               Directions[i][j] = 0;
               continue;
            }
            Square sq1(i);
            Square sq2(j);
            int offset;
            int abs = (sq2 > sq1) ? (int)sq2 - (int)sq1 : (int)sq1 - (int)sq2;
            if (sq1.File() == sq2.File())
               offset = RankIncr;
            else if (sq1.Rank(White) == sq2.Rank(White))
               offset = 1;
            else if (sq1.Color() == sq2.Color())
            {
               if (abs % (RankIncr+1) == 0)
                 offset = RankIncr+1;
               else if (abs % (RankIncr-1) == 0)
                 offset = RankIncr-1;
               else
               {
                 Directions[i][j] = 0;
                 continue;
               }
            }
            else
            {
               Directions[i][j] = 0;
               continue;
             }
            Directions[i][j] = (sq2 > sq1) ? offset : -offset;
        }
    }
    int m;
    for (i=0; i<256; i++)
    {
        for (int off=0; off <8; off++)
        {
            byte b,lb,rb;
            b = lb = rb = 0;
            m = 0;
            for (int j=off+1;j<8;j++)
            {
               if (i & (1<<j))
               {
                  b |= (1<<j);
                  rb |= (1<<j);
                  break;
               }
               ++m;
            }
            for (j=off-1;j>=0;--j)
            {
               if (i & (1<<j))
               {
                  b |=(1<<j);
                  lb |= (1<<j);
                  break;
               }
               ++m;
            }
            rank_attacks[i][off] = b;
            next_bit_left[i][off] = lb;
            next_bit_right[i][off] = rb;
            mobility[i][off] = m;
            // compute a bitmap used for unrotating 90-degree bitboards
            Bitmap bm(i);
            Bitmap bunrot;
            for (int k=0;k<8;k++)
            {
               if (bm.is_set(k))
                   bunrot.set(Bitmap::unrot90[k+8*off]);
            }
            unrot90[i][off] = bunrot;
            Bitmap bunrot2;
            for (k=0;k<8;k++)
            {
               if (bm.is_set(k))
               {
                  bunrot2.set(Bitmap::unrot_da1[k+8*off]);
               }
            }
            unrotda1[i][off] = bunrot2;
            Bitmap bunrot3;
            for (k=0;k<8;k++)
            {
               if (bm.is_set(k))
               {
                  bunrot3.set(Bitmap::unrot_da8[k+8*off]);
               }
            }
            unrotda8[i][off] = bunrot3;
            
        }
    }
}
