#include <stdio.h>
#include <malloc.h>
#include <memory.h>
#include <math.h>
#include "data.h"
//
// Slow Chess has its own table to tell if an endgame with only 1 pawn (+kings) is won or drawn
// It also now has King+Pawn v King+Pawn , King+Pawn v King+Rook , King+Pawn v King+Queen, King+Pawn+Pawn v. King
// and King+Pawn+Pawn v King+Pawn where a white and black pawn block each other.
//
// You may use this code and these bitbases freely in your own program, as long as you give credit somewhere.
//

int FlipH64 ( int nSq ) { return (nSq&56) + 7-(nSq&7); }
int PSQ		( int nSq ) { return ((nSq&56)>>1)-4 + (nSq&7); }
int FlipPSQ ( int nSq ) { return ((nSq&56)>>1)-4 + 7 - (nSq&7); }
// -----------

struct SBitbase 
{
int m_nSize;
int m_nElz; //Size of each table
int m_nTables;
unsigned short *m_pTable;
unsigned char *m_pData;

SBitbase ()
	{
	m_pData = NULL;
	m_pTable = NULL;
	}

~SBitbase ()
	{
	free (m_pData);
	free (m_pTable);
	}

int Load ( char *sName);
int IsLoaded () { return (int)m_pData; }

int inline ProbeBit (int nEntry)
	{
	int nDataEntry = m_pTable [ (nEntry >> 3) / m_nElz ] * m_nElz + ((nEntry>>3)&(m_nElz-1));
	return m_pData [ nDataEntry ] & (1<<(nEntry&7));
	}

void inline Probe (int nEntry, int &eval, int nWin, int nDraw, const int bFlip)
	{
	if (nEntry <0 || nEntry >= m_nSize*8) return; 
	if ( ProbeBit (nEntry) )
		{if (bFlip) eval+=nWin;    else eval = nDraw;}
	else {if (bFlip) eval = nDraw; else eval+= nWin;}
	}
};

int SBitbase::Load ( char *sName)
	{
	FILE *pFile;

	pFile = fopen (sName, "rb");
	if (pFile == NULL) return 0;

	fread (&m_nSize, sizeof(int), 1, pFile);
	fread (&m_nTables, sizeof(int), 1, pFile);
	fread (&m_nElz, sizeof(int), 1, pFile);
	m_pTable = (unsigned short*) malloc ( sizeof(short) * m_nSize/m_nElz );
	m_pData  = (unsigned char*) malloc ( m_nTables * m_nElz );
	fread (m_pTable, 2, (m_nSize / m_nElz), pFile);
	fread (m_pData , m_nElz, m_nTables, pFile);
	fclose (pFile);

	return 1;
	}

SBitbase KPPK, KPK, KPKR, KPPKP, KPKQ, KPKP;

void InitEndgameTables ( )
{
 KPK.Load   ("KPK.sbb");
 KPKP.Load  ("KPKP.sbb");
 KPKQ.Load  ("KPKQ.sbb");
 KPPKP.Load ("KPPKP.sbb");
 KPKR.Load  ("KPKR.sbb");
 KPPK.Load  ("KPPK.sbb");
}

//
// Black calls the same routine but flips vertically the sqs and swaps the king sqs before calling it
//
void inline KingPawnProbe (int nWKingSq, int nBKingSq, int nPawnSq, int nTM, int &eval, const int nWin)
{
// Get Entry Key... May have to flip horizontally because table only stores positions for 
// the pawn on one side.
int nEntry; 
if ( (nPawnSq&7) > 3) nEntry = (FlipH64(nWKingSq)) + (FlipH64(nBKingSq)<<6) + (nTM<<12) + (FlipPSQ(nPawnSq) <<13);
	else nEntry = (nWKingSq) + (nBKingSq<<6) + (nTM<<12) + (PSQ(nPawnSq) <<13);
KPK.Probe (nEntry, eval, nWin, 0, 0);
}

//
// 2 Pawns
//
void inline KingPawnsProbe (SBitbase &BitBase, int nWKingSq, int nBKingSq, int nWPawnSq, int nBPawnSq, int nTM, int &eval, const int nWin, int bFlip)
{
int nEntry; 
if ( (nWPawnSq&7) > 3) nEntry = (nTM) + (FlipH64(nWKingSq)<<1) + (FlipH64(nBKingSq)<<7) + (FlipPSQ(nWPawnSq) <<13) + (FlipH64(nBPawnSq)-8) * (0x30000);
	else nEntry = (nTM) + (nWKingSq<<1) + (nBKingSq<<7) + (PSQ(nWPawnSq) <<13) + (nBPawnSq-8) * (0x30000);
BitBase.Probe (nEntry, eval, nWin, 0,  bFlip);
}

//
// Pawn v Queen
//
void inline KingPawnQueenProbe (int nWKingSq, int nBKingSq, int nBQueenSq, int nWPawnSq, int nTM, int &eval)
{
int nEntry = -1, nPSq;
if ( nWKingSq >= 24) return;
if (nWPawnSq == 8) nPSq = 0; else if (nWPawnSq == 10) nPSq = 1; else if (nWPawnSq==16) nPSq = 2; else if (nWPawnSq == 18) nPSq=3;
else if (nWPawnSq==15) nPSq = 4; else if (nWPawnSq == 13) nPSq = 5; else if (nWPawnSq==23) nPSq = 6; else if (nWPawnSq == 21) nPSq=7;
else return;

if (nPSq > 3) nEntry = (FlipH64(nBQueenSq)) + (FlipH64(nBKingSq)<<6) + (nTM<<12) + ((nPSq-4)<<13) + (FlipH64(nWKingSq)<<15);
else nEntry = (nBQueenSq) + (nBKingSq<<6) + (nTM<<12) + (nPSq<<13) + (nWKingSq << 15);

KPKQ.Probe (nEntry, eval, 0, (eval>>5) + (eval>>6), 0);
}

//
// Pawn Versus Rook
// 
void inline KingPawnRookProbe (int nWKingSq, int nBKingSq, int nRookSq, int nPawnSq, int nTM, int &eval, const int nWin)
{
if ((nPawnSq>>3) > 4) // Table doesn't include pawn on first ranks, do the pawn move first then check the table
	{
	if (nTM == 1 && nPawnSq-8!=nWKingSq && nPawnSq-8!=nBKingSq && nPawnSq-8!=nRookSq) 
		{nPawnSq-=8;
		if ((nPawnSq>>3) > 4)
			if (nPawnSq-8!=nWKingSq && nPawnSq-8!=nBKingSq && nPawnSq-8!=nRookSq) nPawnSq-=8; else {eval+=nWin; return;}
		}
	else {eval+=nWin; return;}
	nTM^=1;
	}
int nEntry; 
if ( (nPawnSq&7) > 3) nEntry = (FlipH64(nRookSq)) + (FlipH64(nBKingSq)<<6) + (FlipH64(nWKingSq)<<17) + (nTM<<16) + (FlipPSQ(nPawnSq) <<12);
	else nEntry = (nRookSq) + (nBKingSq<<6) + (nWKingSq<<17) + (nTM<<16) + (PSQ(nPawnSq) <<12);

KPKR.Probe ( nEntry, eval, nWin, 0, 0);
}

#define BITBASE_WIN 550
//
// From Board, check if a bitbase is applicable, and if it is convert to the appropriate piece list and probe it.
// Because of quite different board formats, this code is longer than I'd like it to be.
//
int ProbeBitbases ( const SBoard &Board, int &eval, const int nWKingSq, const int nBKingSq )
{
int i = 0, nPawnSq = CAPTURED;
if (KPKR.IsLoaded())
	{
	if (Board.nWPawns == 1 && Board.nWP == 0 && Board.nBPawns == 0 && (Board.BPiece[12]!=CAPTURED || Board.BPiece[13]!=CAPTURED))
		{
		int nRookSq = Board.BPiece[12];
		if (nRookSq == CAPTURED) nRookSq = Board.BPiece[13];
		while (i < 8 && nPawnSq == CAPTURED) nPawnSq = Board.WPiece [i++];
		KingPawnRookProbe (SQ64[nWKingSq], SQ64[nBKingSq], SQ64[nRookSq], SQ64[nPawnSq], Board.SideToMove == WHITE ? 1:0, eval, -200);
		return 1;
		}
	if (Board.nBPawns == 1 && Board.nBP == 0 && Board.nWPawns == 0 && (Board.WPiece[12]!=CAPTURED || Board.WPiece[13]!=CAPTURED))
		{
		int nRookSq = Board.WPiece[12];
		if (nRookSq == CAPTURED) nRookSq = Board.WPiece[13];
		while (i < 8 && nPawnSq == CAPTURED) nPawnSq = Board.BPiece [i++];
		KingPawnRookProbe (63-SQ64[nBKingSq], 63-SQ64[nWKingSq], 63-SQ64[nRookSq], 63-SQ64[nPawnSq], Board.SideToMove == BLACK ? 1:0, eval, 200 );
		return 1;
		}
	}
if (KPKQ.IsLoaded())
	{
	if (Board.nWPawns == 1 && Board.nWP == 0 && Board.nBPawns == 0 && Board.BPiece[14]!=CAPTURED )
		{
		while (i < 8 && nPawnSq == CAPTURED) nPawnSq = Board.WPiece [i++];
		KingPawnQueenProbe (SQ64[nWKingSq], SQ64[nBKingSq], SQ64[ Board.BPiece[14] ], SQ64[nPawnSq], Board.SideToMove == WHITE ? 1:0, eval);
		return 1;
		}
	if (Board.nBPawns == 1 && Board.nBP == 0 && Board.nWPawns == 0 && Board.WPiece[14]!=CAPTURED )
		{
		while (i < 8 && nPawnSq == CAPTURED) nPawnSq = Board.BPiece [i++];
		KingPawnQueenProbe (63-SQ64[nBKingSq], 63-SQ64[nWKingSq], 63-SQ64[ Board.WPiece[14] ], 63-SQ64[nPawnSq], Board.SideToMove == BLACK ? 1:0, eval );
		return 1;
		}
	}
return 0;
}

int ProbePawnBitbases ( const SBoard &Board, int &eval, const int nWKingSq, const int nBKingSq )
{
int i = 0, nPawnSq = CAPTURED, nPawnSq2 = CAPTURED, nBPawnSq = CAPTURED, nWPawnSq = CAPTURED;
if (KPK.IsLoaded())
	{
	if (Board.nBPawns == 0 && Board.nWPawns == 1)
		{
		while (i < 8 && nPawnSq == CAPTURED) nPawnSq = Board.WPiece [i++];
		KingPawnProbe (SQ64[nWKingSq], SQ64[nBKingSq], SQ64[nPawnSq], Board.SideToMove == WHITE ? 1:0, eval, BITBASE_WIN);		
		return 1;
		}
	if (Board.nBPawns == 1 && Board.nWPawns == 0)
		{
		while (i < 8 && nPawnSq == CAPTURED) nPawnSq = Board.BPiece [i++];
		KingPawnProbe (63-SQ64[nBKingSq], 63-SQ64[nWKingSq], 63-SQ64[nPawnSq], Board.SideToMove == BLACK ? 1:0, eval, -BITBASE_WIN );
		return 1;
		}
	}
if (KPKP.IsLoaded())
	{
	if (Board.nBPawns == 1 && Board.nWPawns == 1)
		{
		for (i = 0; i < 8 && nBPawnSq == CAPTURED; i++) nBPawnSq = Board.BPiece [i];
		for (i = 0; i < 8 && nWPawnSq == CAPTURED; i++) nWPawnSq = Board.WPiece [i];
		KingPawnsProbe (KPKP, SQ64[nWKingSq], SQ64[nBKingSq], SQ64[nWPawnSq], SQ64[nBPawnSq], Board.SideToMove == WHITE ? 1:0, eval, BITBASE_WIN, 1);
		if (eval == 0)
			{
			KingPawnsProbe (KPKP, 63-SQ64[nBKingSq], 63-SQ64[nWKingSq], 63-SQ64[nBPawnSq], 63-SQ64[nWPawnSq], Board.SideToMove == BLACK ? 1:0, eval, -BITBASE_WIN, 1);
			}
		return 1;
		}
	}
if (KPPK.IsLoaded())
	{
	if (Board.nWPawns == 2 && Board.nBPawns == 0)
		{
		while (i < 8 && nPawnSq == CAPTURED) nPawnSq = Board.WPiece [i++];
		while (i < 8 && nPawnSq2 == CAPTURED) nPawnSq2 = Board.WPiece [i++];
		KingPawnsProbe (KPPK, SQ64[nWKingSq], SQ64[nBKingSq], SQ64[nPawnSq], SQ64[nPawnSq2], Board.SideToMove == WHITE ? 1:0, eval, BITBASE_WIN, 0);
		return 1;
		}
	if (Board.nBPawns == 2 && Board.nWPawns == 0)
		{
		while (i < 8 && nPawnSq == CAPTURED) nPawnSq = Board.BPiece [i++];
		while (i < 8 && nPawnSq2 == CAPTURED) nPawnSq2 = Board.BPiece [i++];
		KingPawnsProbe (KPPK, 63-SQ64[nBKingSq], 63-SQ64[nWKingSq], 63-SQ64[nPawnSq], 63-SQ64[nPawnSq2], Board.SideToMove == BLACK ? 1:0, eval, -BITBASE_WIN, 0);
		return 1;
		}
	}
if (KPPKP.IsLoaded())
	{
	if (Board.nWPawns == 2 && Board.nBPawns == 1)
		{
		while (i < 8 && nBPawnSq == CAPTURED) nBPawnSq = Board.BPiece [i++];
		if (Board.Sq [ nBPawnSq + 10] == WPAWN)
			{
			for (i = 0; i < 8 && (nWPawnSq == CAPTURED || nWPawnSq == nBPawnSq+10); i++ ) nWPawnSq = Board.WPiece [i];
			KingPawnsProbe (KPPKP, SQ64[nWKingSq], SQ64[nBKingSq], SQ64[nWPawnSq], SQ64[nBPawnSq], Board.SideToMove == WHITE ? 1:0, eval, 500, 0);
			return 1;
			}
		}
	if (Board.nBPawns == 2 && Board.nWPawns == 1)
		{
		while (i < 8 && nWPawnSq == CAPTURED) nWPawnSq = Board.WPiece [i++];
		if (Board.Sq [ nWPawnSq - 10] == BPAWN)
			{
			for (i = 0; i < 8 && (nBPawnSq == CAPTURED || nBPawnSq == nWPawnSq-10); i++ ) nBPawnSq = Board.BPiece [i];
			KingPawnsProbe (KPPKP, 63-SQ64[nBKingSq], 63-SQ64[nWKingSq], 63-SQ64[nBPawnSq], 63-SQ64[nWPawnSq], Board.SideToMove == BLACK ? 1:0, eval, -500, 0);
			return 1;
			}
		}
	}
return 0;
}