/*--------------------------------------------------------------------------
    Pawny 0.3, chess engine (source code).
    Copyright (C) 2009 - 2011 by Mincho Georgiev.
    
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
    
    contact: pawnychess@gmail.com 
    web: http://www.pawny.netii.net/
----------------------------------------------------------------------------*/

#include "data.h"
#include "bits.h"

#define ROOK_PAWN_CORRECTION 15
#define PASSED_PAWN_REWARD 20
#define PAWN_CHAIN_ELEMENT 5
#define PAWN_ON_7TH_REWARD 40
#define BACKWARD_ON_SEMI_OP 2

//pawn table entry. (32 bytes of size)
typedef struct pte_t_ 
{	int score[2];
	hashkey_t  pkey;
}pte_t;

#define PT_ALIGNMENT 64

//pawn table pointer:
pte_t *pnt = 0;

//king near passed pawn bonus:
const int king_activity[8] = 
{0, 50, 40, 30, 20, 10, 8, 0};

//passer advancement:
const int passer_bonus[2][8] = 
{{0, 5, 10, 20, 30, 40, 50, 100},
{100, 50, 40, 30, 20, 10, 5, 0}};	

//connected or protected passer:
const int protected_passer[2][8] =
{{0, 0, 1, 3, 6, 12, 25, 35},
{35, 25, 12, 6, 3, 1, 0, 0}};

const int isolated_penalty[8] = 
{0, 6, 8, 10, 10, 8, 6, 0};

const int doubled_penalty[8] =
{0, 8, 10, 12, 12, 10, 8, 0};

const int backwards_penalty[8] =
{0, 4, 6, 8, 8, 6, 4, 0};

const int hanging_penalty[8] =
{0, 1, 2, 3, 3, 2, 1, 0};


void __inline pnt_free() {aligned_free((void *)pnt); pnt = 0;}
void __inline pnt_clear(){aligned_wipe_out((void *)pnt, (size_t)opt->pnt_size, PT_ALIGNMENT);}

//initializing the pawn hash table
int pnt_init(int MB)
{///min size - 8MB, max - 128:
	pnt_free();
	if(MB < 8) MB = 8; if(MB > 128) MB = 128;
	opt->pnt_size = (MB*1024*1024);
	opt->pnt_numentry = (opt->pnt_size / sizeof(pte_t)) - 1;	
	pnt = (pte_t *)aligned_malloc((size_t)opt->pnt_size, PT_ALIGNMENT);
	if(!pnt) return false;
	pnt_clear();
	return true;
}

static bool pnt_probe(int score[])
{	pte_t *p = pnt + (board->phash & opt->pnt_numentry);
	if(p->pkey == board->phash)
	{	score[W] = p->score[W];
		score[B] = p->score[B];
		return true;
	}
	return false;
}

static void pnt_save(int score[])
{	pte_t *p = pnt + (board->phash & opt->pnt_numentry);
	p->pkey = board->phash;
	p->score[W] = score[W];
	p->score[B] = score[B];
}

static __inline bool is_passed(int color, int sq)
{	if(board->bb_pawns[color^1] & passer_mask[color][rsz[sq]])
		return false;
	return true;
}

static __inline bool is_isolated(int color, int sq)
{	if(board->bb_pawns[color] & isolated_mask[calc_file(sq)])
		return false;
	return true;
}

static int eval_white_passer(int p_sq)
{
	bitboard_t bb;
	int sq, score = 0;
	int king_sq = King_Square(W);
	int op_king_sq = King_Square(B);
	int file = calc_file(p_sq);
	int rank = calc_rank(p_sq);
	
	//is the pawn an isolated passer?:
	bb = board->bb_pawns[W] & isolated_mask[file];
	if(bb)//if not, find the closest protector
	//or connected pawn, if any, and score it,
	{//depending on the distance to the passer.
	//note: only the rightmost connected passer gets bonus.
		if(calc_rank(rsu[bitscanf(bb)]) < rank) 
		{	//shift the pawns, so the closest protector will pop-up next.
			bb <<= 63 - rsz[p_sq]; 
			score += protected_passer[W][calc_rank(rsu[bitscanr(bb)])];
		}
	}
	score += passer_bonus[W][rank];
	//adding score for king near that pawn
	//and penalty for opking being near:
	score += king_activity[distance_table[king_sq][p_sq]];
	score -= king_activity[distance_table[op_king_sq][p_sq]];
	//is it backed up by a rook?:
	for(sq = PLS(WR); sq <= H8; sq = PLN(sq))
	{	if((calc_file(sq) == file) && (sq < p_sq))
			score += 15;
	}
	return score;
}

static int eval_black_passer(int p_sq)
{
	bitboard_t bb;
	int sq, score = 0;
	int king_sq = King_Square(B);
	int op_king_sq = King_Square(W);
	int file = calc_file(p_sq);
	int rank = calc_rank(p_sq);
	
	//is the pawn an isolated passer?:
	bb = board->bb_pawns[B] & isolated_mask[file];
	if(bb)//if not, find the closest protector
	//or connected pawn, if any, and score it,
	{//depending on the distance to the passer.
	//note: only the leftmost connected passer gets bonus.
		if(calc_rank(rsu[bitscanr(bb)]) > rank) 
		{	//shift the pawns, so the closest protector will pop-up next.
			bb >>= rsz[p_sq]; 
			score += protected_passer[B][calc_rank(rsu[bitscanf(bb)])];
		}
	}	
	score += passer_bonus[B][rank];
	//adding score for king near that pawn
	//and penalty for opking being near:
	score += king_activity[distance_table[king_sq][p_sq]];
	score -= king_activity[distance_table[op_king_sq][p_sq]];
	//is it backed up by a rook?:
	for(sq = PLS(BR); sq <= H8; sq = PLN(sq))
	{	if((calc_file(sq) == file) && (sq > p_sq))
			score += 15;
	}
	return score;
}

static int eval_white_pawn(int sq)
{
	bitboard_t bb;
	int score = 0,back = 0;
	int file = calc_file(sq);
	int rank = calc_rank(sq);
		
	//pawn on 7th
	if(rank == RANK_7) score += PAWN_ON_7TH_REWARD;
	
	//doubled pawn:
	if(calc_rank(rsu[bitscanf(board->bb_pawns[W] \
	& file_mask[file])]) < rank)
		score -= doubled_penalty[rank];
	
	if(file == FILE_A)//the rook pawn gets scored 15% lower:
	{	score -= ROOK_PAWN_CORRECTION;
		if(PieceType(sq - 15) == WP) score += PAWN_CHAIN_ELEMENT;
		if(is_isolated(W,sq)) score -= isolated_penalty[rank];
		else
		{	bb = board->bb_pawns[W] & file_mask[file + 1];
			if(calc_rank(rsu[bitscanf(bb)]) > rank)
				score -= backwards_penalty[rank];
		}
	}
	else if(file == FILE_H)
	{	score -= ROOK_PAWN_CORRECTION;
		if(PieceType(sq - 17) == WP) score += PAWN_CHAIN_ELEMENT;
		if(is_isolated(W,sq)) score -= isolated_penalty[rank];
		else
		{	bb = board->bb_pawns[W] & file_mask[file - 1];
			if(calc_rank(rsu[bitscanf(bb)]) > rank)
				score -= backwards_penalty[rank];
		}
	}
	else //not a rook pawn:
	{	if(PieceType(sq - 15) == WP) score += PAWN_CHAIN_ELEMENT;
		if(PieceType(sq - 17) == WP) score += PAWN_CHAIN_ELEMENT;
		
		if(is_isolated(W,sq)) score -= isolated_penalty[rank];
		else
		{	//starting from the left file:
			bb = board->bb_pawns[W] & file_mask[file - 1];
			if(bb)
			{	if(calc_rank(rsu[bitscanf(bb)]) > rank)
					back++;
				bb = board->bb_pawns[W] & file_mask[file + 1];
				if((bb) && (back) && (calc_rank(rsu[bitscanf(bb)]) > rank))
				{	if(!(file_mask[file] & board->bb_pawns[B]))
						score -= BACKWARD_ON_SEMI_OP;
					score -= backwards_penalty[rank];
				}
				else if(!bb) score -= hanging_penalty[rank];
			}
			else 
			{	score -= hanging_penalty[rank];
				bb = board->bb_pawns[W] & file_mask[file + 1];
				if(calc_rank(rsu[bitscanf(bb)]) > rank)
					score -= backwards_penalty[rank];
			}
		}
	}
	return score;
}
static int eval_black_pawn(int sq)
{
	bitboard_t bb;
	int score = 0,back = 0;
	int file = calc_file(sq);
	int rank = calc_rank(sq);
		
	//pawn on 7th
	if(rank == RANK_2) score += PAWN_ON_7TH_REWARD;
	
	//doubled pawn:
	if(calc_rank(rsu[bitscanr(board->bb_pawns[B] \
	& file_mask[file])]) > rank)
		score -= doubled_penalty[rank];
	
	if(file == FILE_A)//the rook pawn gets scored 15% lower:
	{	score -= ROOK_PAWN_CORRECTION;
		if(PieceType(sq + 17) == BP) score += PAWN_CHAIN_ELEMENT;
		if(is_isolated(B,sq)) score -= isolated_penalty[rank];
		else
		{	bb = board->bb_pawns[B] & file_mask[file+1];
			if(calc_rank(rsu[bitscanr(bb)]) < rank)
				score -= backwards_penalty[rank];
		}
	}
	else if(file == FILE_H)
	{	score -= ROOK_PAWN_CORRECTION;
		if(PieceType(sq + 15) == BP) score += PAWN_CHAIN_ELEMENT;
		if(is_isolated(B,sq)) score -= isolated_penalty[rank];
		else
		{	bb = board->bb_pawns[B] & file_mask[file - 1];
			if(calc_rank(rsu[bitscanr(bb)]) < rank)
				score -= backwards_penalty[rank];
		}
	}
	else //not a rook pawn:
	{	if(PieceType(sq + 15) == BP) score += PAWN_CHAIN_ELEMENT;
		if(PieceType(sq + 17) == BP) score += PAWN_CHAIN_ELEMENT;
		
		if(is_isolated(B,sq)) score -= isolated_penalty[rank];
		else
		{	//starting from the left file:
			bb = board->bb_pawns[B] & file_mask[file - 1];
			if(bb)
			{	if(calc_rank(rsu[bitscanr(bb)]) < rank)
					back++;
				bb = board->bb_pawns[B] & file_mask[file + 1];
				if(bb && back && (calc_rank(rsu[bitscanr(bb)]) < rank))
				{	if(!(file_mask[file] & board->bb_pawns[W]))
						score -= BACKWARD_ON_SEMI_OP;
					score -= backwards_penalty[rank];
				}
				else if(!bb) score -= hanging_penalty[rank];
			}
			else 
			{	score -= hanging_penalty[rank];
				bb = board->bb_pawns[B] & file_mask[file + 1];
				if(calc_rank(rsu[bitscanr(bb)]) < rank)
					score -= backwards_penalty[rank];
			}
		}
	}
	return score;
}

void eval_pawn_struct(int score[])
{	
	int sq;
	int pstruct[2] = {0};
	int psqtbl[2]  = {0};

	///probe the pawn table:
	if(pnt_probe(&pstruct[0]))
	{	for(sq = PLS(WP); sq <= H8; sq = PLN(sq))
		{	if(is_passed(W, sq)) score[W] += PASSED_PAWN_REWARD;
			psqtbl[W] += psq_table[WP][sq];
		}
		for(sq = PLS(BP); sq <= H8; sq = PLN(sq))
		{	if(is_passed(B, sq)) score[B] += PASSED_PAWN_REWARD;
			psqtbl[B] += psq_table[BP][sq];
		}
	}
	else ///full evaluation
	{	for(sq = PLS(WP); sq <= H8; sq = PLN(sq))
		{	psqtbl[W] += psq_table[WP][sq];
			pstruct[W] += eval_white_pawn(sq);
			if(is_passed(W, sq)) score[W] += PASSED_PAWN_REWARD;
		
		}
		for(sq = PLS(BP); sq <= H8; sq = PLN(sq))
		{	psqtbl[B] += psq_table[BP][sq];
			pstruct[B] += eval_black_pawn(sq);
			if(is_passed(B, sq)) score[B] += PASSED_PAWN_REWARD;
				
		}
		//saving static positional score to the pawn table:
		pnt_save(&pstruct[0]);
	}
	score[W] += pstruct[W] + psqtbl[W];
	score[B] += pstruct[B] + psqtbl[B];
}

void eval_pawn_struct_endgame(int score[])
{	
	int sq;
	int pstruct[2] = {0};
	int psqtbl[2]  = {0};

	///probe the pawn table:
	if(pnt_probe(&pstruct[0]))
	{	for(sq = PLS(WP); sq <= H8; sq = PLN(sq))
		{	if(is_passed(W, sq)) score[W] += eval_white_passer(sq);
			psqtbl[W] += endgame_pawn_psq[W][sq];
		}
		for(sq = PLS(BP); sq <= H8; sq = PLN(sq))
		{	if(is_passed(B, sq)) score[B] += eval_black_passer(sq);
			psqtbl[B] += endgame_pawn_psq[B][sq];
		}
	}
	else ///full evaluation 
	{	for(sq = PLS(WP); sq <= H8; sq = PLN(sq))
		{	psqtbl[W] += endgame_pawn_psq[W][sq];
			pstruct[W] += eval_white_pawn(sq);
			if(is_passed(W, sq)) score[W] += eval_white_passer(sq);
		
		}
		for(sq = PLS(BP); sq <= H8; sq = PLN(sq))
		{	psqtbl[B] += endgame_pawn_psq[B][sq];
			pstruct[B] += eval_black_pawn(sq);
			if(is_passed(B, sq)) score[B] += eval_black_passer(sq);
				
		}
		//saving static positional score to the pawn table:
		pnt_save(&pstruct[0]);
	}
	score[W] += pstruct[W] + psqtbl[W];
	score[B] += pstruct[B] + psqtbl[B];
}

