
/*	 CASTLE BITFIELD

	 00000001  | White 0-0  
	 00000010  | Black 0-0
	 00000100  | White 0-0-0
	 00001000  | Black 0-0-0
*/

const uint WHITE_OO_FLAG   = 1;
const uint BLACK_OO_FLAG   = 2;
const uint WHITE_OOO_FLAG  = 4;
const uint BLACK_OOO_FLAG  = 8;
const uint WHITE_CASTLE_FLAG = WHITE_OO_FLAG | WHITE_OOO_FLAG;

class Board {
public:
	uint  ply, mat_key;
	int opening, endgame, mat_diff;
	uint8 stm, type[64], ep_file, castle, ksq[2], fifty;
	BitBoard piece[8], occupied, occupied_rl90, occupied_rl45, occupied_rr45, occupied_by[2];	

	// hash keys
	uint64 key,
		pawn_key,
		material_key,
		keys[MAX_KEYS];

	inline uint InCheck(BitBoard *checkers) {
		BitBoard p, n, b, rr, rf, me, opp;
		uint sq = ksq[stm], attackers = 0;
		me = Occupied(stm);
		opp = Occupied(stm^1);

		if(n = Knight[sq] & piece[KNIGHT] & opp)								attackers++;
		if(p = Pawn[stm][sq] & piece[PAWN] & opp)								attackers++;		
		if(b = BISHOP_ATT(sq) & (piece[BISHOP]|piece[QUEEN]) & opp)				attackers++;
		if(rr = ATT_RANK(sq,occupied) & (piece[ROOK]|piece[QUEEN]) & opp)		attackers++;
		if(rf = ATT_FILE(sq,occupied_rl90) & (piece[ROOK]|piece[QUEEN]) & opp)	attackers++;
		// hack: it`s possible when pawn promotes with capture (discovered check)
		// king`s checks are impossible

		*checkers = p|n|b|rr|rf;
		return attackers;
	}
	
	inline BitBoard Occupied(uint colour)	{return occupied_by[colour]						;}
	inline BitBoard White()					{return occupied_by[WHITE]						;}
	inline BitBoard Black()					{return occupied_by[BLACK]						;}		
	inline bool InCheck()					{return IsAttacked(stm^1,ksq[stm])				;}	
	inline bool CanCastleShort(uint colour)	{return castle & (WHITE_OO_FLAG << colour)		;}
	inline bool CanCastleLong(uint colour)	{return castle & (WHITE_OOO_FLAG << colour)		;}
	inline bool CanCastle(uint colour)		{return castle & (WHITE_CASTLE_FLAG << colour)	;}

	inline bool PawnIsPassed(uint c, int s)	{
		bool passed, doubled;
		passed = !(AntiPassedPawns[c][s] & piece[PAWN] & Occupied(c^1));
		doubled = FILE_BB(s) & FRONT_RANKS(c, s) & piece[PAWN] & Occupied(c);		
		return passed && (!doubled);
	}
	inline bool MoveIsPassedPawnAdvance(Move *m) {
		return (m->Piece() == PAWN) 
				&& PawnIsPassed(stm, m->To());
	}
	inline bool IsAttacked(uint att_colour, uint sq) {
		register BitBoard enemy = Occupied(att_colour);
		if(Pawn[att_colour^1][sq] & (piece[PAWN]|piece[BISHOP]) & enemy)	return true;
		if(KNIGHT_ATT(sq) & piece[KNIGHT] & enemy)							return true;		
		if(KING_ATT(sq) & (piece[KING]|piece[QUEEN]) & enemy)				return true;		
		if(BISHOP_ATT(sq) & (piece[BISHOP]|piece[QUEEN]) & enemy)			return true;
		if(ROOK_ATT(sq) & (piece[ROOK]|piece[QUEEN]) & enemy)				return true;
		return false;
	}
		
	int  Eval(EvalInfo *ev_info, Personality *p, PawnInfo *pi);
	int RookEndgame(Personality *p, PawnEntry *p_ent);
	int PawnEndgame(Personality *p, PawnEntry *p_ent);
	void ResetPST();
	bool Draw();
	void Clear();			
	char *ToFen();
	void PrintFen();
	void Print();
	void Print(FILE *file);	
	void FromFEN(char *fen);
	bool MoveIsLegal(Move *m);
	bool MoveIsLegal(Move *m, BitBoard pinned_rooks, BitBoard pinned_bishops);
	bool MoveIsLegalDebug(Move *m, BitBoard pinned_rooks, BitBoard pinned_bishops);	
	void GenLegalMoves(MoveList *list);
	void GenEvasions(MoveList *list);
	int SEE(Move *m, bool debug = false);
	void GenLegalCapturesDebug(MoveList *list);
	void GenQuietChecksDebug(MoveList *list);
	void GenQuietMovesDebug(MoveList *list);
	bool UnmakeMove(Move *m, undo_t *undo);	
	bool MakeMove(Move *m, undo_t *undo);
	void UnmakeNullMove(undo_t *undo);	
	void MakeNullMove(undo_t *undo);
	uint64 InitPawnHashKey();
	bool CanUseNullMove();
	uint64 InitHashKey();	
		
	void GenRegularMoves(MoveList *list, BitBoard piece_target, BitBoard pieces);
	void GenRookMoves(MoveList *list, BitBoard rook_target, BitBoard pieces);
	void GenBishopMoves(MoveList *list, BitBoard bishop_target, BitBoard pieces);
	void GenKnightMoves(MoveList *list, BitBoard piece_target, BitBoard pieces);
	void GenKingMoves(MoveList *list, BitBoard king_target);
	void GenPawnCaptures(MoveList *list, BitBoard pawn_target, BitBoard pawns);
	void GenPawnPromotes(MoveList *list, BitBoard pawn_target, BitBoard pawns);
	void GenPawnQuiet(MoveList *list, BitBoard pawn_target, BitBoard pawns);
	void GenEPCaptures(MoveList *list, BitBoard pawn_target, BitBoard pawns);
	void GenCastleMoves(MoveList *list);
	void FindPinned(BitBoard checkers, BitBoard *pinned_rooks, BitBoard *pinned_bishops);
	void GenCaptures(MoveList *list, BitBoard pinned_rooks, BitBoard pinned_bishops, BitBoard capture_mask);
	void GenQuietMoves(MoveList *list, BitBoard pinned_rooks, BitBoard pinned_bishops);
	void GenEvasions(MoveList *list, BitBoard pinned_rooks, BitBoard pinned_bishops, BitBoard checkers, uint in_check);
	void GenQuietChecks(MoveList *list, BitBoard pinned_rooks, BitBoard pinned_bishops);
	void FindDiscovered(BitBoard *discovered_rooks, BitBoard *discovered_bishops);
	void GenQuietPseudoChecks(MoveList *list, BitBoard pinned_rooks, BitBoard pinned_bishops, 
								 BitBoard discovered_rooks, BitBoard discovered_bishops);
	bool IsMate(uint in_check, BitBoard checkers, BitBoard pinned_rooks, BitBoard pinned_bishops);

	bool MoveIsCheck(Move *m);
	bool MoveIsCheck(Move *m, BitBoard discovered_rooks, BitBoard discovered_bishops);
	bool MoveIsTactical(Move *m);
};

void Board::Clear() {

	int i ;
	for(i = a1; i <= h8; i++)			type[i] = EMPTY;
	for(i = EMPTY; i <= KING+1; i++)	piece[i] = (BitBoard)0;

	occupied_by[WHITE] = occupied_by[BLACK]	= (BitBoard)0;
	occupied = occupied_rl90 = occupied_rl45 = occupied_rr45 = (BitBoard)0;

	ply = fifty = 0;     	                 
	ksq[0] = ksq[1] = 0;          
	ep_file = EMPTY; 
	stm = WHITE;
	castle = 0; 	

	opening = endgame = 0;
	mat_key = mat_diff = 0;
}

bool Board::Draw() {

	if(fifty > 100)						return true;
	if((fifty == 100) && !InCheck())	return true;

	// repetitions
	for(int i = 2; i <= fifty; i += 2)
		if(keys[ply-i] == key)			return true;	

	return false;
}

void Board::Print(FILE *file) {
	char piece_str[] = " pnbrqkPNBRQK";	
	#define SQFUNC	((m<<3) + (i-9))
	int i, k, m;	
		
	fprintf(file, "\n       %c%c%c%c", 201,205,205,205);
	for(k = 0; k < 7; k++)
		fprintf(file, "%c%c%c%c", 209,205,205,205);
	fprintf(file, "%c\n", 187);

	for (m = 8; m >= 1; m--) {
		fprintf(file, "     %ld %c",m, 186); 
		for (i=1; i<=7; i++)	
			fprintf(file, " %c %c", piece_str[type[SQFUNC]+6*((BIT(SQFUNC) & White())!=0)], 179);		
		fprintf(file, " %c %c", piece_str[type[SQFUNC]+6*((BIT(SQFUNC) & White())!=0)], 186);				
		if(m != 1) {
			fprintf(file, "\n       %c", 199);
			for(int m = 0; m < 7; m++)	
				fprintf(file, "%c%c%c%c", 196,196,196,197);
			fprintf(file, "%c%c%c%c\n", 196,196,196,182);
		} else {				
			fprintf(file, "\n       %c%c%c%c", 200,205,205,205);
			for(int n = 0; n < 7; n++)	
				fprintf(file, "%c%c%c%c", 207,205,205,205);
			fprintf(file, "%c\n", 188);
		}
	}		
	fprintf(file, "         A   B   C   D   E   F   G   H\n\n");
	return;
}
void Board::Print() {Print(stdout)	;}

#ifdef _M_X64

// CountBits()

inline uint CountBits(BitBoard bb) {
  bb -= ((bb>>1) & 0x5555555555555555ULL);
  bb = ((bb>>2) & 0x3333333333333333ULL) + (bb & 0x3333333333333333ULL);
  bb = ((bb>>4) + bb) & 0x0F0F0F0F0F0F0F0FULL;
  bb *= 0x0101010101010101ULL;
  return int(bb >> 56);
}

// CountFirstBits()

inline uint CountFirstBits(BitBoard bb) { // MAX 15 bits
  bb -= (bb>>1) & 0x5555555555555555ULL;
  bb = ((bb>>2) & 0x3333333333333333ULL) + (bb & 0x3333333333333333ULL);
  bb *= 0x1111111111111111ULL;
  return int(bb >> 60);
}
#else

// CountBits()

inline uint CountBits(BitBoard bb) {
  unsigned w = unsigned(bb >> 32), v = unsigned(bb);
  v -= (v >> 1) & 0x55555555;
  w -= (w >> 1) & 0x55555555;
  v = ((v >> 2) & 0x33333333) + (v & 0x33333333);
  w = ((w >> 2) & 0x33333333) + (w & 0x33333333);
  v = ((v >> 4) + v) & 0x0F0F0F0F;
  v += (((w >> 4) + w) & 0x0F0F0F0F);
  v *= 0x01010101;
  return int(v >> 24);
}

// CountFirstBits()

inline uint CountFirstBits(BitBoard bb) { // MAX 15 bits
  unsigned w = unsigned(bb >> 32), v = unsigned(bb);
  v -= (v >> 1) & 0x55555555;
  w -= (w >> 1) & 0x55555555;
  v = ((v >> 2) & 0x33333333) + (v & 0x33333333);
  w = ((w >> 2) & 0x33333333) + (w & 0x33333333);
  v += w;
  v *= 0x11111111;
  return int(v >> 28);
}
#endif

inline uint FindLastBit(BitBoard bb) { 
	int r = 0;
	while (bb >>= 1)  r++;
	return r;
}

const uint BitTable[64] = {
    0, 1, 2, 7, 3,13, 8,19,
    4,25,14,28, 9,44,20,34,
    5,17,26,42,15,55,29,57,
   10,31,52,45,21,48,59,35,
   63, 6,12,18,24,27,43,33,
   16,41,54,56,30,51,47,58,
   62,11,23,32,40,53,50,46,
   61,22,39,49,60,38,37,36 };

uint FindFirstBit(BitBoard b) {	
    return BitTable[((b&-b) * 0x0218a392cfd376af) >> 58];
}
uint ExtractFirstBit(BitBoard &b) {
	uint64 fold = b & -b;	
    b &= b-1;
	return BitTable[(fold * 0x0218a392cfd376af) >> 58];
}

void PrintBitBoard(FILE *file, BitBoard bb) {
	int i = a8;
	while(i > -1) {
		fprintf(file, "%c", (BIT(i) & bb) ? 254 : 45);
		if((++i)%8 == 0) {
			fprintf(file, "\n");
			i -= 16; 
		}
	}
	fprintf(file, "\n");
}

void PrintBitBoard(BitBoard bb) {
	int i = a8;
	while(i > -1) {
		printf("%c", (BIT(i) & bb) ? 254 : 45);
		if((++i)%8 == 0) {
			printf("\n");
			i -= 16; 
		}
	}
	printf("\n");
}

char* Version() {
	char *result = new char[128];
	char tmp[128];
	int y, m, d, h;

	strcpy(tmp, __DATE__);

	// year
	y = atoi(tmp + strlen(tmp) - 4);
	y -= (y/1000)*1000;
	
	// month
	if(strstr(tmp, "Jan") == tmp)		m = 1;
	else if(strstr(tmp, "Feb") == tmp)	m = 2;
	else if(strstr(tmp, "Mar") == tmp)	m = 3;
	else if(strstr(tmp, "Apr") == tmp)	m = 4;
	else if(strstr(tmp, "May") == tmp)	m = 5;
	else if(strstr(tmp, "Jun") == tmp)	m = 6;
	else if(strstr(tmp, "Jul") == tmp)	m = 7;
	else if(strstr(tmp, "Aug") == tmp)	m = 8;
	else if(strstr(tmp, "Sep") == tmp)	m = 9;
	else if(strstr(tmp, "Oct") == tmp)	m = 10;
	else if(strstr(tmp, "Nov") == tmp)	m = 11;
	else if(strstr(tmp, "Dec") == tmp)	m = 12;
	else {ASSERT(false);				m = 13;}

	// day
	d = atoi(tmp + 4);
	// hour
	strcpy(tmp, __TIME__);
	h = atoi(tmp);
#ifdef PUBLIC_RELEASE
	sprintf(result, "%d.%d", y,m);
#else
	sprintf(result, "%d.%d.%d%c", y,m,d, 'a'+h);	
#endif
	return result; 
}