# include "Game/PositionalEvaluation.h"
# include "Pieces/Piece.h"
# include "Game/Game.h"
# include "Directions/Direction.h"
#  include "Color.h"
# include <Game/PiecePlacementFeature.h>
# include <Game/PawnStructureFeature.h>
# include <TestData/TestPositionSuite.h>
# include <cmath>
using namespace Alice;

PositionalEvaluation::PositionalEvaluation()
  :_basicValue(1000)
{
  pawnStructure = new PawnStructureFeature;
  piecePlacementFeature = new PiecePlacementFeature;
};
PositionalEvaluation::~PositionalEvaluation()
{
  delete piecePlacementFeature;
  delete pawnStructure;
};
void
PositionalEvaluation::setBasicValue(short int newValue)
{
  _basicValue = newValue;
};

short int
PositionalEvaluation::getBasicValue() const
{
  return _basicValue;
};

Evaluation::Score
PositionalEvaluation::pieceValue(const Piece* p) const
{
  return Score(getBasicValue() * p->basicValue());
};

int
PositionalEvaluation::centralization(const Square& sq)
{
  int i = sq.file();
  int j = sq.rank();
  return std::min(i*(7-i), j*(7-j));
};


Evaluation::Score 
PositionalEvaluation::evaluateCentralization(const Color* myColor) const
{
  
  Score adjustment(0);
  for (Square::SquareIterator it = Square::allInstances().begin();
       it != Square::allInstances().end();
       ++it)
    {
      const Square& sq(*it);
      int i = sq.file();
      const Piece* p = observedGame->board().at(sq);
      if (p->isNull())
	continue;
      int centralizationWeight = p->centralizationFactor(); 	    
      if (p->color()== myColor)
	adjustment += Score(centralizationWeight *centralization(sq));
      else
	adjustment -= Score(centralizationWeight*centralization(sq));
      if (p->displayLetter() == 'R')
	{	      
	  if (p->color() == myColor)
	    adjustment -= BLOCKED_ROOK_PENALTY;
	  else
	    adjustment += BLOCKED_ROOK_PENALTY;
	}
    }
  
  return adjustment;
};


Evaluation::Score
PositionalEvaluation::boundedScore( Score alpha, Score beta ) const
{
  Score basicScore =  Evaluation::boundedScore(alpha, beta);
  if (isMate(basicScore))
    return basicScore;
  // lazy evaluation
# if 0
  if (basicScore < alpha - 2*_basicValue)
    return basicScore + 2*_basicValue;
  if (basicScore > beta + 2*_basicValue)
    return basicScore - 2*_basicValue;
# endif // 0
  Game* g =  observedGame;
  Score positionalScore = 0;
  //positionalScore += developmentFeature->score(g);
  //positionalScore += castlingFeature->score(g );
  //positionalScore += evaluateCentralization(Color::white());
  positionalScore += piecePlacementFeature->score(g);
  positionalScore += pawnStructure->score( g );
  Color* myColor = g->colorToMove();
  if (myColor == Color::white())
    basicScore += positionalScore;
  else
    basicScore -= positionalScore;
  return basicScore;
};

void
PositionalEvaluation::addDifference( float factor )
{
  //developmentFeature->addDifference( factor/4 );
  //castlingFeature->addDifference( factor/4 );
  pawnStructure->addDifference(factor);
  piecePlacementFeature->addDifference(factor);
};

void
PositionalEvaluation::adjustWeights()
{
  //developmentFeature->adjustWeights();
  //castlingFeature->adjustWeights();
  pawnStructure->adjustWeights();
  piecePlacementFeature->adjustWeights();
};

void
PositionalEvaluation::print()
{
  //std::cout<<"development: ";
  //developmentFeature->print();
  //std::cout<<"castling: ";
  //castlingFeature->print();
  std::cout<<"pawn structure: ";
  pawnStructure->print();
  std::cout<<"piece placement: ";
  piecePlacementFeature->print();
};

Evaluation::Score
PositionalEvaluation::totalError( const std::vector<Evaluation::Score>& scores )
{
  
  Evaluation::Score error = 0;
  for (int i = 0; i < scores.size()-1; i++)
    {
      Evaluation::Score difference = sigmoid(scores[i+1])-sigmoid(scores[i]);
      error += difference * difference;
    }
  return error;
};

void
PositionalEvaluation::computeScores(TestPositionSuite& suite,
	      std::vector<Evaluation::Score>& scores)
{

  for (int i = 0; i < suite.numberOfPositions(); i++)
    {
      Game g;
      g.forsytheString(suite.getPosition(i).forsythe());
      if (suite.getPosition(i).getColorToMove() == "b")
	g.doNullMove();
      startObserving(&g);
      Evaluation::Score score = this->score();
      if (g.isStalemate()) 
	if (g.isInCheck(g.colorToMove()))
	  score = Evaluation::mate();
	else
	  score = Evaluation::stalemate();
      if (suite.getPosition(i).getColorToMove() == "b")
	score = - score;
      scores[i] = score;
      //std::cout<<scores[i]<<std::endl;
      stopObserving();
    }
};

void
PositionalEvaluation::adjustWeights(TestPositionSuite& suite,
	      const std::vector<Evaluation::Score>& scores)
{


  const float lambda = 0.9f;
  const float alpha = 10000;
  std::vector<Evaluation::Score> dx(suite.numberOfPositions()-1);
  //  std::cout<<"dx.size: "<<dx.size()<<std::endl;
  Evaluation::Score diff = 0;
  for (int i = dx.size()-1; i >= 0; i--)
    {
      diff = diff*lambda + sigmoid(scores[i+1]) - sigmoid(scores[i]);
      dx[i] = diff;
      //  std::cout<<"dx["<<i<<"] = "<<diff<<std::endl;
    }
  for (int i = 0; i < suite.numberOfPositions() -1; i++)
    {
      Game g;
      g.forsytheString(suite.getPosition(i).forsythe());
      if (suite.getPosition(i).getColorToMove() == "b")
	{
	  g.doNullMove();
	}
      startObserving(&g);
      score();
      const float score = scores[i];

      const float derivative = sigmoidDerivative(score);
      addDifference( alpha * derivative * dx[i] );
      stopObserving();
    };
  adjustWeights();
};

double
PositionalEvaluation::sigmoid( double x ) const
{
  return std::tanh( 0.000255 * x );
};

double
PositionalEvaluation::sigmoidDerivative( double x ) const
{
  double csh = std::cosh( 0.000255*x);
  return   0.000255 /( csh * csh);
};
