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

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

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

static inline void 
SetSquare(
	  const Square sq, Square * squares, unsigned &NumSquares)
{
    ASSERT(sq.OnBoard());
    ASSERT(NumSquares < Bearing::MaxBearSq);
    squares[NumSquares++] = sq;
}

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

    if (!attacks)
    {
	start = i;
	dest = (Direction[side] > 0) ? i + RankIncr : i - RankIncr;
	if (dest.OnBoard())
	{
  	   piece = board[dest];
	   if (piece.IsEmpty())
	   {
		// 1-square pawn advance

	       SetSquare(dest, squares, NumSquares);
	       if (i.Rank(board.Side()) == 2)
	       {
		   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 = (Direction[side] > 0) ? i + (RankIncr - 1) :
      i - (RankIncr + 1);
      if (dest.OnBoard())
      {
        piece = board[dest];
	if (attacks ||
	    (!piece.IsEmpty() && (piece.Color() != board.Side())))
	    SetSquare(dest, squares, NumSquares);
      }
    }
    if (i.File() != 8)
    {
      dest = (Direction[side] > 0) ? i + (RankIncr + 1) :
      i - (RankIncr - 1);
      if (dest.OnBoard())
      {
        piece = board[dest];
	if (attacks ||
	    (!piece.IsEmpty() && (piece.Color() != board.Side())))
	    SetSquare(dest, squares, NumSquares);
      }
    }
    Square epsq = board.EnPassantSq(board.OppositeSide());
    if (!epsq.IsInvalid() && !attacks)
    {
	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);
	}
    }
}

#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, data, squares, NumSquares) \
    Square i; \
    while ((i = *data) != 255 && board[i].IsEmpty()) \
    { \
       SetSquare(i,squares,NumSquares); \
       ++data; \
    } \
    if (i != 255) \
       SetSquare(i,squares,NumSquares) \

static void
KnightMoves(
	    const Board & board,
	    const Square loc,
	    Square * squares,
	    unsigned &NumSquares,
	    const BOOL attacks)
{
    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() || (attacks || piece.Color() != side)))
	    SetSquare(dest, squares, NumSquares);
    }
}

static void
KingMoves(
	  const Board & board,
	  const Square loc,
	  Square * squares,
	  unsigned &NumSquares,
	  const BOOL attacks)
{
    const ColorType side = board[loc].Color();
    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() || (attacks || piece.Color() != side)))
	   SetSquare(dest, squares, NumSquares);
    }
}

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, FALSE);
	break;
    case Piece::Knight:
	KnightMoves(board, loc, squares, NumSquares, FALSE);
	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:
	KingMoves(board, loc, squares, NumSquares, FALSE);
	break;
    case Piece::Invalid:
	break;
    }
    return NumSquares;
}

unsigned 
Bearing::Attack(const Board & board,
		const Square loc, const ColorType side,
		Square * squares,
		BOOL indirect)
{
    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;
    int offset;
    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;
	offset = 0;
        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+offset] = square1;
 	       if (indirect)
	           offset += Bearing::Offset;
	       else 
	           break;
	   }
	   else
	       break;
	}
	if (found)
            ++index;
        data = RookSquares[loc] + (8*i);
	square1 = loc;
	offset = 0;
	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+offset] = square1;
 	       if (indirect)
	           offset += Bearing::Offset;
	       else 
	           break;
	   }
	   else
	       break;
	}
	if (found)
	   ++index;
    }
    return n;
}

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

    switch (board[loc].Type())
    {
    case Piece::Empty:
	break;
    case Piece::Pawn:
	PawnMoves(board, board[loc].Color(),
		  loc, squares, NumSquares, TRUE);
	break;
    case Piece::Knight:
	KnightMoves(board, loc, squares, NumSquares, TRUE);
	break;
    case Piece::Bishop:
	for (j = 0; j < 4; j++)
	{
	    const byte *data = BishopSquares[loc] + (j*8);
	    RBAttacks(board, data, squares, NumSquares);
	}
	break;
    case Piece::Rook:
	for (j = 0; j < 4; j++)
	{
	    const byte *data = RookSquares[loc] + (j*8);
	    RBAttacks(board, data, squares, NumSquares);
	}
	break;
    case Piece::Queen:
	for (j = 0; j < 4; j++)
	{
	    const byte *data = BishopSquares[loc] + (j*8);
	    RBAttacks(board, data, squares, NumSquares);
	}
	for (j = 0; j < 4; j++)
	{
	    const byte *data = RookSquares[loc] + (j*8);
	    RBAttacks(board, data, squares, NumSquares);
	}
	break;
    case Piece::King:
	KingMoves(board, loc, squares, NumSquares, TRUE);
	break;
    case Piece::Invalid:
	break;
    }
    return NumSquares;
}

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;
}

