//////////////////////////////////////////////////
// (c) MMIV Sven Reichard
// 

# include <Game/PawnStructureFeature.h>
# include <Game/Game.h>
# include <numeric>
Alice::PawnStructureFeature::PawnStructureFeature()
  : 
  EvaluationFeature(MAX_WEIGHT, "pawnStructure"),
   _whitePawnsOnFile(8),
   _blackPawnsOnFile(8),
   _whiteTopPawnOnFile(8),
   _blackTopPawnOnFile(8),
   _whiteBottomPawnOnFile(8),
   _blackBottomPawnOnFile(8)
{
};

Alice::PawnStructureFeature::~PawnStructureFeature()
{

};

void
Alice::PawnStructureFeature::countPawns(const Game* g)
{
  game = g;
  for (int file = 0; file < 8; file ++)
    {
      _whitePawnsOnFile[file] = 0;
      _blackPawnsOnFile[file] = 0;
      _whiteTopPawnOnFile[file] = 0;
      _blackTopPawnOnFile[file] = 0;
      _whiteBottomPawnOnFile[file] = 7;
      _blackBottomPawnOnFile[file] = 7;
      
      for (int rank = 1; rank < 7; rank ++)
	{
	  Square sq(file, rank);
	  const Piece* p = g->board().at(sq);
	  if (!p->isPawn())
	    continue;
	  
	  if (p->color() == Color::white())
	    {
	      _whitePawnsOnFile[file] ++;
	      _whiteTopPawnOnFile[file] = rank;
	      if (_whiteBottomPawnOnFile[file] == 7)
		_whiteBottomPawnOnFile[file] = rank;
	    }
	  else
	    {
	      _blackPawnsOnFile[file] ++;
	      _blackTopPawnOnFile[file] = rank;
	      if (_blackBottomPawnOnFile[file] == 7)
		_blackBottomPawnOnFile[file] = rank;
	    }
	  
	}
    }
};


int
Alice::PawnStructureFeature::numberOfPawnsOnFile(int file, 
						 const Color* color) const
{
  if (color == Color::white())
    return (_whitePawnsOnFile[file]);
  else
    return (_blackPawnsOnFile[file]);
};


int
Alice::PawnStructureFeature::topPawn(int file,
				     const Color* color) const
{
  if (color == Color::white())
    return _whiteTopPawnOnFile[file];
  else
    return _blackTopPawnOnFile[file];
};

int
Alice::PawnStructureFeature::bottomPawn(int file, const Color* color) const
{
  if (color == Color::white())
    return _whiteBottomPawnOnFile[file];
  else
    return _blackBottomPawnOnFile[file];
};


int
Alice::PawnStructureFeature::kingSafetyDeficiencies( const Color* color ) const
{
  int result = 0;
  Square king = game->findKing(color);
  int startingFile = king.file() < 4? 0 : 5;
  if (color == Color::white())
    {
      for (int i = startingFile; i <startingFile + 3; i++)
	if (bottomPawn(i, color) !=1)
	  result ++;
    }
  else
    {
      for (int i = startingFile; i <startingFile + 3; i++)
	if (topPawn(i, color) !=6)
	  result ++;
    }
  return result;
};

void
Alice::PawnStructureFeature::evaluateIsolatedPawns(const Alice::Color* color)
{

  Evaluation::Score adjustment = 0;
  if (numberOfPawnsOnFile(0, color) && ! numberOfPawnsOnFile(1, color))
    adjustment --;
  if (numberOfPawnsOnFile(7, color) && ! numberOfPawnsOnFile(6, color))
    adjustment --;
  for (int file = 1; file < 7; file ++)
    {
      if (numberOfPawnsOnFile(file, color) 
	  && ! numberOfPawnsOnFile(file-1, color)
	  && ! numberOfPawnsOnFile(file+1, color))
	adjustment --;
    }
  if (color->isWhite())
    addGradient(ISOLATED_PAWN,adjustment);
  else
    addGradient(ISOLATED_PAWN,  adjustment);
  
};

void
Alice::PawnStructureFeature::evaluateDoubledPawns()
{

  Evaluation::Score adjustment(0);
  for (int i = 0; i < 8; i++)
    {
      int nrPawns = numberOfPawnsOnFile(i, Color::white());
      if (nrPawns > 1)
	adjustment -= (nrPawns - 1) ;
      nrPawns = numberOfPawnsOnFile(i, Color::black());
      if (nrPawns > 1)
	adjustment += (nrPawns - 1);
      
    };
  setGradient(DOUBLE_PAWN, adjustment);

};

bool
Alice::PawnStructureFeature::isPassedPawn(Game* g, const Square& sq)
{

  const Piece* p  = g->board().at(sq);
  if (!p->isPawn())
    return false;
  if (p->color() == Color::white())
    {
      int rank = sq.rank();
      int file = sq.file();
      if (topPawn(file, Color::black()) > rank)
	return false;
      if (file > 0 && topPawn(file-1, Color::black()) > rank)
	return false;
      if (file < 7 && topPawn(file+1, Color::black()) > rank)
	return false;
      return true;
    }
  else // color == black
    {

      int rank = sq.rank();
      int file = sq.file();
      if (bottomPawn(file, Color::white()) < rank)
	return false;
      if (file > 0 && bottomPawn(file-1, Color::white()) < rank)
	return false;
      if (file < 7 && bottomPawn(file+1, Color::white()) < rank)
	return false;
      return true;
    }
};

int
Alice::PawnStructureFeature::countPassedPawns(Game* g)
{
  int result = 0;
  for (int i = 0; i < 64; i++)
    {
      if (! isPassedPawn(g, i))
	continue;
      if (g->board().at(i)->color()->isWhite())
	result ++;
      else
	result --;
    }
  setGradient(PASSED_PAWN, result);
  return result;
};

void
Alice::PawnStructureFeature::computeGradient(Game* g)
{
  clearGradient();
  countPawns(g);
  //  Evaluation::Score positionalScore = 0;
  
  // double pawns
  evaluateDoubledPawns();
  //isolated pawns
  evaluateIsolatedPawns(Color::white());
  evaluateIsolatedPawns(Color::black());
  countPassedPawns( g );
  setGradient(KING_SAFETY, kingSafetyDeficiencies(Color::white())
	      - kingSafetyDeficiencies(Color::black()));
};

