//   GreKo chess engine
//   (c) 2002-2021 Vladimir Medvedev <vrm@bk.ru>
//   http://greko.su

#include "eval.h"
#include "eval_params.h"
#include "utils.h"

extern string g_weightsFile;
Pair PSQ[14][64];

void InitPSQ(const vector<double>& x);

void AddPsq(
	vector<double>& features,
	size_t index,
	COLOR side,
	FLD f,
	double stage)
{
	double UNIT = (1 - 2 * side) * stage;
	FLD f1 = FLIP_SIDE[side][f];

#ifdef PSQ_5
	double X = (Col(f1) - 3.5) / 3.5;
	double Y = (3.5 - Row(f1)) / 3.5;
	double X2 = X * X;
	double Y2 = Y * Y;
	double XY = X * Y;

	features[index] += UNIT;
	features[index + 1] += UNIT * X2;
	features[index + 2] += UNIT * X;
	features[index + 3] += UNIT * Y2;
	features[index + 4] += UNIT * Y;
	features[index + 5] += UNIT * XY;
#endif

#ifdef PSQ_12
	int col = (Col(f1) < 4)? Col(f1) : 7 - Col(f1);
	int row = Row(f1);
	features[index] += UNIT;
	features[index + 1 + col] += UNIT;
	features[index + 5 + row] += UNIT;
#endif

#ifdef PSQ_16
	int col = Col(f1);
	int row = Row(f1);
	features[index] += UNIT;
	features[index + 1 + col] += UNIT;
	features[index + 9 + row] += UNIT;
#endif

#ifdef PSQ_64
	features[index] += UNIT;
	features[index + 1 + f1] += UNIT;
#endif
}
////////////////////////////////////////////////////////////////////////////////

void AddQuadratic(
	vector<double>& features,
	size_t index,
	COLOR side,
	double z,
	double stage)
{
	double UNIT = (1 - 2 * side) * stage;

	features[index] += UNIT * z * z;
	features[index + 1] += UNIT * z;
}
////////////////////////////////////////////////////////////////////////////////

void AddLinear(
	vector<double>& features,
	size_t index,
	COLOR side,
	double z,
	double stage)
{
	double UNIT = (1 - 2 * side) * stage;
	features[index] += UNIT * z;
}
////////////////////////////////////////////////////////////////////////////////

int Distance(FLD f1, FLD f2)
{
	static const int dist[100] =
	{
		0, 1, 1, 1, 2, 2, 2, 2, 2, 3,
		3, 3, 3, 3, 3, 3, 4, 4, 4, 4,
		4, 4, 4, 4, 4, 5, 5, 5, 5, 5,
		5, 5, 5, 5, 5, 5, 6, 6, 6, 6,
		6, 6, 6, 6, 6, 6, 6, 6, 6, 7,
		7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
		7, 7, 7, 7, 8, 8, 8, 8, 8, 8,
		8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
		8, 9, 9, 9, 9, 9, 9, 9, 9, 9,
		9, 9, 9, 9, 9, 9, 9, 9, 9, 9
	};

	int drow = Row(f1) - Row(f2);
	int dcol = Col(f1) - Col(f2);
	return dist[drow * drow + dcol * dcol];
}
////////////////////////////////////////////////////////////////////////////////

EVAL Evaluate(const Position& pos)
{
	COLOR side = pos.Side();
	COLOR opp = side ^ 1;

	EVAL e = (EVAL)DotProduct(pos.Stage(), pos.Score(side) - pos.Score(opp));

	if (e > 0 && pos.Bits(PAWN | side) == 0 && pos.MatIndex(side) < 5)
		e = 0; 
	if (e < 0 && pos.Bits(PAWN | opp) == 0 && pos.MatIndex(opp) < 5)
		e = 0;

	if (pos.Fifty() > 100)
		e = 0;
	else
		e = e * (100 - pos.Fifty()) / 100;

	return e;
}
////////////////////////////////////////////////////////////////////////////////

void GetFeaturesSide(const Position& pos, vector<double>& features, COLOR side)
{
	U64 x;
	FLD f;

	Pair stage = pos.Stage();
	double mid = stage.mid;
	double end = stage.end;

#define ADD_PSQ(tag)                               \
    {                                              \
        AddPsq(features, Mid_##tag, side, f, mid); \
        AddPsq(features, End_##tag, side, f, end); \
    }

#define ADD_UNIT(tag)                                 \
    {                                                 \
        AddLinear(features, Mid_##tag, side, 1, mid); \
        AddLinear(features, End_##tag, side, 1, end); \
    }

#define ADD_LINEAR(tag)                               \
    {                                                 \
        AddLinear(features, Mid_##tag, side, z, mid); \
        AddLinear(features, End_##tag, side, z, end); \
    }

#define ADD_QUADRATIC(tag)                               \
    {                                                    \
        AddQuadratic(features, Mid_##tag, side, z, mid); \
        AddQuadratic(features, End_##tag, side, z, end); \
    }

#define ADD_VECTOR(tag, index)                                \
    {                                                         \
        AddLinear(features, Mid_##tag + index, side, 1, mid); \
        AddLinear(features, End_##tag + index, side, 1, end); \
    }

	//
	//   PAWNS
	//

	U64 pawns = pos.Bits(PAWN | side);

	x = pawns;
	while (x)
	{
		f = PopLSB(x);
		ADD_PSQ(Pawn)
	}

	//
	//   KNIGHTS
	//

	x = pos.Bits(KNIGHT | side);
	while (x)
	{
		f = PopLSB(x);
		ADD_PSQ(Knight)
	}

	//
	//   BISHOPS
	//

	x = pos.Bits(BISHOP | side);
	while (x)
	{
		f = PopLSB(x);
		ADD_PSQ(Bishop)
	}

	//
	//   ROOKS
	//

	x = pos.Bits(ROOK | side);
	while (x)
	{
		f = PopLSB(x);
		ADD_PSQ(Rook)
	}

	//
	//   QUEENS
	//

	x = pos.Bits(QUEEN | side);
	while (x)
	{
		f = PopLSB(x);
		ADD_PSQ(Queen)
	}

	//
	//   KINGS
	//

	{
		f = pos.King(side);
		ADD_PSQ(King)
	}

#undef ADD_PSQ
#undef ADD_UNIT
#undef ADD_LINEAR
#undef ADD_QUADRATIC
#undef ADD_VECTOR
}
////////////////////////////////////////////////////////////////////////////////

void GetFeatures(const Position& pos, vector<double>& features)
{
	features.clear();
	features.resize(NUMBER_OF_FEATURES);

	GetFeaturesSide(pos, features, WHITE);
	GetFeaturesSide(pos, features, BLACK);
}
////////////////////////////////////////////////////////////////////////////////

void InitEval(const vector<double>& x)
{
	g_w = x;
	InitPSQ(x);

#define INIT_VARIABLE(variable, tag) \
    variable.mid = x[Mid_##tag];     \
    variable.end = x[End_##tag];

#define INIT_LINEAR(table, tag)                 \
    for (size_t i = 0; i <= MAX_##table; ++i)   \
    {                                           \
        double z = double(i) / MAX_##table;     \
        table[i].mid =                          \
            x[Mid_##tag] * z;                   \
        table[i].end =                          \
            x[End_##tag] * z;                   \
    }

#define INIT_QUADRATIC(table, tag)        \
    for (size_t i = 0; i <= MAX_##table; ++i)   \
    {                                           \
        double z = double(i) / MAX_##table;     \
        table[i].mid =                          \
            x[Mid_##tag] * z * z +              \
            x[Mid_##tag + 1] * z;               \
        table[i].end =                          \
            x[End_##tag] * z * z +              \
            x[End_##tag + 1] * z;               \
    }

#define INIT_VECTOR_TABLE(table, tag)           \
    for (size_t i = 0; i <= MAX_##table; ++i)   \
    {                                           \
        table[i].mid = x[Mid_##tag + i];        \
        table[i].end = x[End_##tag + i];        \
    }

#undef INIT_VARIABLE
#undef INIT_LINEAR
#undef INIT_QUADRATIC
#undef INIT_VECTOR_TABLE
}
////////////////////////////////////////////////////////////////////////////////

void InitEval()
{
	InitFeatures();

	vector<double> x;
	if (!ReadWeights(x, g_weightsFile))
	{
		SetDefaultWeights(x);
		WriteWeights(x, g_weightsFile);
	}
	InitEval(x);
}
////////////////////////////////////////////////////////////////////////////////

void InitPSQ(const vector<double>& x)
{
	for (FLD f = 0; f < 64; ++f)
	{
#ifdef PSQ_5
		double X = (Col(f) - 3.5) / 3.5;
		double Y = (3.5 - Row(f)) / 3.5;
		double X2 = X * X;
		double Y2 = Y * Y;
		double XY = X * Y;

#define INIT_PSQ(table, index, tag)                           \
        table[index][f].mid =                                 \
            x[Mid_##tag] +                                    \
            x[Mid_##tag + 1] * X2 +                           \
            x[Mid_##tag + 2] * X +                            \
            x[Mid_##tag + 3] * Y2 +                           \
            x[Mid_##tag + 4] * Y +                            \
            x[Mid_##tag + 5] * XY;                            \
        table[index][f].end =                                 \
            x[End_##tag] +                                    \
            x[End_##tag + 1] * X2 +                           \
            x[End_##tag + 2] * X +                            \
            x[End_##tag + 3] * Y2 +                           \
            x[End_##tag + 4] * Y +                            \
            x[End_##tag + 5] * XY;                            \
        table[index ^ 1][FLIP[f]].mid = table[index][f].mid;  \
        table[index ^ 1][FLIP[f]].end = table[index][f].end;
#endif

#ifdef PSQ_12
#define INIT_PSQ(table, index, tag)                           \
        {                                                     \
        int col = (Col(f) < 4)? Col(f) : 7 - Col(f);          \
        int row = Row(f);                                     \
        table[index][f].mid =                                 \
            x[Mid_##tag] +                                    \
            x[Mid_##tag + 1 + col] +                          \
            x[Mid_##tag + 5 + row];                           \
        table[index][f].end =                                 \
            x[End_##tag] +                                    \
            x[End_##tag + 1 + col] +                          \
            x[End_##tag + 5 + row];                           \
        table[index ^ 1][FLIP[f]].mid = table[index][f].mid;  \
        table[index ^ 1][FLIP[f]].end = table[index][f].end;  \
        }
#endif

#ifdef PSQ_16
#define INIT_PSQ(table, index, tag)                           \
        {                                                     \
        int col = Col(f);                                     \
        int row = Row(f);                                     \
        table[index][f].mid =                                 \
            x[Mid_##tag] +                                    \
            x[Mid_##tag + 1 + col] +                          \
            x[Mid_##tag + 9 + row];                           \
        table[index][f].end =                                 \
            x[End_##tag] +                                    \
            x[End_##tag + 1 + col] +                          \
            x[End_##tag + 9 + row];                           \
        table[index ^ 1][FLIP[f]].mid = table[index][f].mid;  \
        table[index ^ 1][FLIP[f]].end = table[index][f].end;  \
        }
#endif

#ifdef PSQ_64
#define INIT_PSQ(table, index, tag)                           \
        {                                                     \
        table[index][f].mid =                                 \
            x[Mid_##tag] +                                    \
            x[Mid_##tag + 1 + f];                             \
        table[index][f].end =                                 \
            x[End_##tag] +                                    \
            x[End_##tag + 1 + f];                             \
        table[index ^ 1][FLIP[f]].mid = table[index][f].mid;  \
        table[index ^ 1][FLIP[f]].end = table[index][f].end;  \
        }
#endif

		INIT_PSQ(PSQ, PW, Pawn)
		INIT_PSQ(PSQ, NW, Knight)
		INIT_PSQ(PSQ, BW, Bishop)
		INIT_PSQ(PSQ, RW, Rook)
		INIT_PSQ(PSQ, QW, Queen)
		INIT_PSQ(PSQ, KW, King)

#undef INIT_PSQ
	}
}
////////////////////////////////////////////////////////////////////////////////
