// Slow Chess 2.96
// by Jonathan Kreuzer
// copyright 2004
#include "win32compat.h"
#include <stdio.h>
#include <time.h>

#include "data.h"
#include "gui.h"
#include "moves.h"
#include "transcript.h"
#include "oBook.h"
#include "search.h"
#include "evaluate.h"

// ---------------------------------
// Globals
//
int SearchDepth = 1, thinking = 0, bAcceptDraw = FALSE, g_bAnalyze = FALSE;
unsigned char PlayerCOLOR = WHITE;
clock_t starttime, endtime, MoveTm;
float Thinktime = 10, fPanicTime = 1;
unsigned int nodes, nodes2;
int g_MaxDepth, stopthinking;
unsigned char upgrade = 0;
int DRAWSCORE = 0;

#define NOVALUE -19999

int *KillerMoves = (int *) calloc (MAXDEPTH+3, sizeof (int) );
unsigned long *History = (unsigned long *) calloc ( 128 * 128 * 4, 4);

Movestruct *MoveStack = (Movestruct *) calloc (MAXDEPTH+3, sizeof(Movestruct));
SBoard *BoardStack  = (SBoard *) calloc (MAXDEPTH+3, sizeof(SBoard));
SBoard SearchBoard;
// Search Info Tracking 

int CurrentBest, CurrentMove = 0, CurrentMoveNum = 0, CurrentEval = 0;
int PV [MAXDEPTH+3][MAXDEPTH+3];
float g_fMoveTm = 0.0f;
int g_nFail = 0, g_SearchColor = WHITE;
unsigned char g_ucAge = 0;
int g_lasteval, g_LastBest;
int g_nNoChange, g_RecapSquare;
int g_bDoNull, g_bPanic;
float g_fTimeDisplay;

extern int bDoNull;

#include "HashNC.cpp"

/* ===============================================================
 * Store Move in a string
 ================================================================ */
void GetMoveString (char sBuffer[], int src, int dst, SBoard const &Board)
{
	  int i = 0;
	  if (PieceChars [Board.Sq[src]&7] !='p') 
		  sBuffer [i++] = PieceChars [ Board.Sq[src]&7 ];
      sBuffer [i++] = ((src - 22) % 10) + 'a';
      sBuffer [i++] = '8' - ((src - 22) / 10);
      sBuffer [i++] = (Board.Sq [dst] == EMPTY) ? '-':'x';
      sBuffer [i++] = ((dst - 22) % 10) + 'a';
      sBuffer [i++] = '8' - ((dst - 22) / 10);
	  sBuffer [i++] = NULL;

	  if (src == 124 || src == 126) strcpy (sBuffer, "O-O  ");
      if (src == 125 || src == 127) strcpy (sBuffer, "O-O-O");
}

// ----------------
// Turn a move from the PV into a string
// ---------------- 
int GetPVMoveString (char* buffer, int nMove, int format)
{
char mid, check;
char c1, c2, c3, c4;
char sPromo[5] = { 0 };
int j = 0, bRealMove = FALSE;
int nPVType = (nMove &255);

	if ( ((nMove>>28)&7)> 3) check = '+'; else check = ' '; 

	if ( nPVType == 250)
		{
		j+= sprintf (buffer + j, "<mt>");
		}
	else if ( nPVType == 253)
		{
		j+= sprintf (buffer + j, "<tt>");
		}
	else if (nPVType == 255)
		{
		j+= sprintf (buffer + j, "#mate");
		}
	else if (nPVType == 254)
		{
		}
	else if (nPVType == 252)
		{
		j+= sprintf (buffer + j, "<draw>");
		}
	else if ( MSrc(nMove) == 124 || MSrc(nMove) == 126 )
		{
		bRealMove = TRUE;
		if (format == 2)
			{
			if (MSrc(nMove) == 124 ) j+= sprintf (buffer + j, "e1g1 ");
			if (MSrc(nMove) == 126 ) j+= sprintf (buffer + j, "e8g8 ");
			}
		else
			{
			j+= sprintf (buffer + j, "o-o%c ", check);
			if (format == 1) j+= sprintf (buffer + j, "   ");
			}
		}
	else if ( MSrc(nMove ) == 125 || MSrc(nMove) == 127 )
		{
		bRealMove = TRUE;
		if (format == 2)
			{
			if (MSrc(nMove) == 125 ) j+= sprintf (buffer + j, "e1c1 ");
			if (MSrc(nMove) == 127 ) j+= sprintf (buffer + j, "e8c8 ");
			}
		else
			j+= sprintf (buffer + j, "o-o-o%c  ", check);
		}
	else
		{
		bRealMove = TRUE;
		c1 = (( MSrc( nMove ) - 22) % 10) + 'a';
		c2 = '8' - ((MSrc( nMove ) - 22) / 10);
		c3 = ((MDst( nMove ) - 22) % 10) + 'a';
		c4 = '8' - ((MDst( nMove ) - 22) / 10);

		if (MPromo ( nMove ) != 0 && c4 == '8' || c4 == '1') 
			{
			if (MPromo ( nMove ) == WQUEEN)  strcpy (sPromo, "Q");
			if (MPromo ( nMove ) == WKNIGHT) strcpy (sPromo, "N");
			if (MPromo ( nMove ) == WROOK)   strcpy (sPromo, "R");
			}

		if ( ((nMove>>28)&1) == 1)
			mid = 'x'; else mid = '-';
		
		if (format == 2) j += sprintf ( buffer + j, "%c%c%c%c%s ", c1 , c2, c3 , c4, sPromo);
		else if (format == 1) j += sprintf ( buffer + j, "%c%c%c%c%c%s%c  ", c1 , c2, mid, c3 , c4, sPromo, check );
		else 
			{
			j += sprintf ( buffer + j, "%c%c", c1 , c2 );
			if ( mid!= '-' ) j += sprintf ( buffer + j, "x");
			j += sprintf ( buffer + j, "%c%c", c3 , c4 );
			j += sprintf ( buffer + j, "%s", sPromo);
			if (check != ' ') j += sprintf ( buffer + j, "+");
			j += sprintf ( buffer + j, "  ");
			}

		}
	if ( format == 2 && bRealMove == FALSE) {strcpy (buffer, ""); j = 0;}
return j;
}

// ----------------
// Turn the Entire PV into a string
// ---------------- 
void GetPVString ( char *buffer, int format )
{
int j = 0;
int nMoves = PV[0][0];
buffer[0] = NULL;
if (nMoves > MAXDEPTH) nMoves = MAXDEPTH;

for (int i = 1; i <= nMoves; i++)
	{
	j += GetPVMoveString (buffer + j, PV[0][i], format);
	if ( format == 1 && (i&1) == 0 ) j += sprintf ( buffer + j, "\n"); // Spacing for PV in Slow GUI
	}
}
// ----------------
//
// Add a Move list to the stored PV for depth ahead
//
// ----------------
void inline AddPV (const int &ahead, const int &MMove, const int &nSearch, const int &bCheck, const SBoard &Board )
	{
	PV[ahead-1][1] = MMove;
	
	if ( Board.Sq [ MDst( MMove ) ]!= EMPTY ) 	PV[ahead -1][1] |= (1<<28); 
	if (bCheck != 0) PV[ahead-1][1] |= (4 << 28);
	
	if (nSearch == 0) 
		{
		int nLength = PV[ ahead ][0];
		if (nLength > MAXDEPTH) nLength = MAXDEPTH;
		for (int i = 0; i < nLength; i++)
			{ PV[ahead-1][i+2] = PV[ahead][i+1]; }
		PV[ahead -1][0] = nLength + 1;
		}
	else 
		{
		PV[ahead-1][2] = 254 - nSearch; // HT
		PV[ahead-1][0] = 2;
		}
	}

// ----------------
// History Table
// ----------------
void inline AddHistory (const int &nMove, const int &depth, const int &ahead, const int &color)
    {
	if (SearchBoard.Sq [ MDst (nMove) ] != EMPTY) return; // Captures are already sorted differently, so ignore on history
	KillerMoves[ ahead ] = nMove;
	if (depth <= 2) return;
	History [ (nMove&16383) + ((color-1)<<14) ] += depth*depth+1;
	}

void inline ClearHistory ( )
    {
	ZeroMemory ( History, 128 * 128 * sizeof (long) * 2 );
	}


//
//  Sort non Attacks by history heuristic 
// 
void SortByHistory (Movestruct &PMoves, const int color)
    {
    int tMove;
	unsigned long iVal;
    int i, d, start = PMoves.numAttackmoves;
    static unsigned long SortVals [200];

	// Fill in Sort Values
	 for (i = start; i < PMoves.nummoves; i++)
		{
		 int nMove = PMoves.Moves[i];
		 SortVals[i] = History [(nMove&16383) + ((color-1)<<14)  ];
		}

	// Insertion Sort
    for (d = start+1; d < PMoves.nummoves; d++ )
		{
        i = d; 
        iVal = SortVals[i];
		tMove = PMoves.Moves [i];
        while ( i > start && iVal > SortVals[i-1])
			{
			 PMoves.Moves [i] = PMoves.Moves [ i-1 ];
             SortVals[i] = SortVals[i-1];
             i--;
            }
		PMoves.Moves[i] = tMove;
        SortVals[i] = iVal;
        }
    }

int inline GetMoveNum (const int BestMove, const Movestruct &PMoves)
        {
        for (int i = 0; i < PMoves.nummoves; i++)
          if ((PMoves.Moves[i]&65535) == (BestMove&65535))
             return i;

        return NO_BEST;
        }

int inline GetMoveNum2 (const int BestMove, const Movestruct &PMoves)
        {
		if (BestMove == NO_BEST) return NO_BEST;
        int i;
        for (i = 1; i <= PMoves.numAttackmoves; i++)
          if ((PMoves.Moves[i]&65535) == (BestMove&65535) )
             return i;

        for (i = 51; i <= (50+PMoves.nummoves); i++)
          if ((PMoves.Moves[i]&65535) == (BestMove&65535) )
             return i;

        return NO_BEST;
        }

int inline GetMoveNum3 (const int BestMove, const Movestruct &PMoves)
        {
		if (BestMove == NO_BEST) return NO_BEST;
        for (int i = 51; i <= (50+PMoves.nummoves); i++)
          if ((PMoves.Moves[i]&65535) == (BestMove&65535) )
             return i;

        return NO_BEST;
        }

void MoveToFront (const int BestMove, Movestruct &PMoves, int nDepth)
        {
		int nStart = GetMoveNum ( BestMove, PMoves);
		int nTemp, i;
		if (nStart == NO_BEST) 
			{return;
			}
		for (i = nStart; i > 0 ; i--)
			{
			PMoves.Moves[ i ] = PMoves.Moves [ i- 1];
			}
		PMoves.Moves [0] = BestMove;
		// Leave capture moves... is this good? I don't know, helps on test suites since the key is usually a capture.
		if (SearchBoard.Sq [ MDst ( PMoves.Moves[1]) ] == EMPTY)
			{
			for (i = 1;  i < (PMoves.nummoves-1); i++)
				if (SearchBoard.Sq [ MDst ( PMoves.Moves[i+1]) ] != EMPTY )
					{
					nTemp = PMoves.Moves [i];
					PMoves.Moves[i] = PMoves.Moves [i+1];
					PMoves.Moves[i+1] = nTemp;
					}
				else i = 1000;
			}
		if (nStart >= PMoves.numAttackmoves) PMoves.numAttackmoves++; 
		if (PMoves.numAttackmoves < 1) PMoves.numAttackmoves = 1;
        }

// Compress movelist
void SortMoveList (const int BestMove, Movestruct &PMoves, const int KillerMove)
        {
	    int index = 0, i;

		int nBest = GetMoveNum2 ( BestMove, PMoves);
		if (nBest!=NO_BEST)
			{
			PMoves.Moves [index++] = PMoves.Moves [nBest];
			}

		for (i = 1; i <= PMoves.numAttackmoves; i++)
			{
			if (i!=nBest) 
				PMoves.Moves [index++] = PMoves.Moves [i];
			}

		int nKiller = GetMoveNum3 ( KillerMove, PMoves);
		if (nKiller!=NO_BEST && nKiller != nBest )
			{
			PMoves.Moves [index++] = PMoves.Moves [nKiller];
			}		
		PMoves.numAttackmoves = index;

		i= 51;
		if (i==nBest || i==nKiller) i++;
        while ( i <= (50+PMoves.nummoves))
			{
			PMoves.Moves [index++] = PMoves.Moves [i];
			i++;
			if (i==nBest || i==nKiller) i++;
			}
		PMoves.nummoves = index;
        }

//
//  Sort Attacks by Most valuable capture, least valuable attacker
// 
void SortAttacks (Movestruct &PMoves, SBoard &Board)
	{
    int i, d, iVal, temp;
	static int SortVals [52];
	int nMove;

	for (i = 1; i <= PMoves.numAttackmoves; i++)
		{
		nMove = PMoves.Moves[i];
		SortVals[i] = PieceValues[ Board.Sq [ MDst(nMove) ] &7]*8 - PieceValues[ Board.Sq [ MSrc(nMove) ] &7];
		}

	// Insertion Sort
    for (d = 2; d <= PMoves.numAttackmoves ; d++ )
           {
            i = d; 
            iVal = SortVals[i];
			temp = PMoves.Moves[i];
            while ( i >= 2 && iVal > SortVals[i-1])
                    {
					PMoves.Moves[i] = PMoves.Moves [i-1];
                    SortVals[i] = SortVals[i-1];
                    i--;
                    }
 		   PMoves.Moves[i] = temp;
           SortVals[i] = iVal;
           }
	}

//-------------
//
//In Check Move List
//
//-------------
void AddMoves ( Movestruct &Moves, const int BestMove )
{
int nLimit = 51+Moves.nummoves;
int i = 1;
Moves.nummoves = 0;

int nBest = GetMoveNum2 ( BestMove, Moves);
if (nBest != NO_BEST) 
	{
	Moves.Moves [Moves.nummoves++] = Moves.Moves[ nBest ];
	}

if (i > Moves.numAttackmoves) i=51;
while (i < nLimit )
	{
	 if (i != nBest)  Moves.Moves [Moves.nummoves++] = Moves.Moves[i];
	 if (i == Moves.numAttackmoves) i=51; else i++;
	}
}

// 
// Add underpromoting moves to the movelist
//
void inline AddUnderPromos ( Movestruct &PMoves, const SBoard &Board)
{
	for (int i = 1; i <= PMoves.numAttackmoves; i++)
		{
		int nMove = PMoves.Moves[i];
		if (Board.Sq [ MSrc(nMove) ] == WPAWN && SQRANK[ MSrc(nMove)] == 7) 
			{PMoves.nummoves++; PMoves.Moves [  PMoves.nummoves+50 ] = nMove - (2 << 14);
			 /*PMoves.nummoves++; PMoves.Moves [  PMoves.nummoves+50 ] = nMove - (3 << 14);
			 PMoves.nummoves++; PMoves.Moves [  PMoves.nummoves+50 ] = nMove - (1 << 14);*/}
		if (Board.Sq [ MSrc(nMove) ] == BPAWN && SQRANK[ MSrc(nMove)] == 2)
			{PMoves.nummoves++; PMoves.Moves [  PMoves.nummoves+50 ] = nMove - (2 << 14);
			 /*PMoves.nummoves++; PMoves.Moves [  PMoves.nummoves+50 ] = nMove - (3 << 14);
			 PMoves.nummoves++; PMoves.Moves [  PMoves.nummoves+50 ] = nMove - (1 << 14);*/}
		}
}

//
// ID, ALPHA BETA SEARCH, QSEARCH, AND CLOSELY RELATED FUNCTIONS included in searchmain.cpp
//
#include "searchMainNC.cpp"

// ---------------------------------
//  Resign Lost Games
// ---------------------------------
int ResignOrDraw ( int nEval, int color )
{
 static int Resign[4] = {0, 0, 0, 0};
 if (color == WHITE)
	 if (nEval < - 680) Resign[color]++; else Resign[color] = 0;
 if (color == BLACK)
	 if (nEval > 680)  Resign[color]++; else Resign[color] = 0;
 if (Resign[color] > 3 && SCsettings.bResign == TRUE)
	{
	// only resign if we are down pieces already so we don't resign against a human for something he/she may not see
	// also there needs to be at least one major piece (BN mates are hard so this way we don't resign them)
	if ( (color == WHITE && (GameBoard.BMat - GameBoard.WMat) > 450 && (GameBoard.nBP > GameBoard.nBMinors || GameBoard.nBPawns > 1) && (GameBoard.nBP > GameBoard.nWP || GameBoard.nBPawns > 0)) 
		 || (color == BLACK && (GameBoard.WMat - GameBoard.BMat) > 450 && (GameBoard.nWP > GameBoard.nWMinors || GameBoard.nWPawns > 1) && (GameBoard.nWP > GameBoard.nBP || GameBoard.nWPawns > 0) )		) 
		{
		printf("resign\n");
		fflush(stdout);
		Resign[color] = -2;
		return TRUE;
		}
	}

 // Also Offer Draws (after 4 moves with a draw score, then every 7 moves after that if the drawscore remains)
 static int Draw = 0;
 if ( (nEval == DRAWSCORE || abs(nEval) <= 2) && (GameBoard.GameTime > 84 || (GameBoard.WMat <= 1400 && GameBoard.BMat<=1400)) )
	{
	 Draw++;
	 if (Draw > 4 && SCsettings.bDraw == TRUE )
		{
		bAcceptDraw = TRUE;
		Draw = -3; 
		printf("offer draw\n");
		fflush(stdout);
	    return TRUE;
		}
	}
 else Draw = 0;

 // Will only Accept Draws in the Endgame 
 bAcceptDraw = FALSE;
 static int AcceptDraw = 0;
 if ( (PlayerCOLOR == BLACK && nEval <= DRAWSCORE) || (PlayerCOLOR == WHITE && nEval >= DRAWSCORE) && SCsettings.bDraw == TRUE )
	{
	 AcceptDraw++;
	 if (AcceptDraw > 5 && (GameBoard.GameTime > 84 || (GameBoard.WMat <= 1100 && GameBoard.BMat<=1100)) && GameBoard.nWP <=2 && GameBoard.nBP <=2) bAcceptDraw = TRUE;
	}
 else AcceptDraw = 0;

 return FALSE;
}

/* ===============================================================
//
// SEARCH INFO DISPLAYING 
//
 ================================================================ */
// Set Globals used to diplay bestmove, PV, move being searched, etc.
void UpdateDisplayInfo ( int i, int MMove, int Max, int bestmove )
	{
	 g_nFail = 0;
	 CurrentMoveNum = i + 1; 
	 CurrentMove = MMove;

	if ( (SearchDepth > 3 && abs(CurrentEval) > 19000 && abs(Max) < 19000) || ( SearchDepth > 5 && bestmove!=CurrentBest && abs(Max) < 19000) ) 
		{
		wbSendThinking (SearchDepth, Max, (100*(clock() - starttime))/( CLOCKS_PER_SEC ), nodes, TRUE, FALSE, FALSE);
		}
	if (g_bUCI == TRUE) 
		{
		UCISendThinking (FALSE);
		}

	 if (SearchDepth > 4 && abs (CurrentEval) > 19000 && abs(Max) < 19000)
	 g_fMoveTm = (float)(clock()-MoveTm)/( CLOCKS_PER_SEC );
     CurrentEval = Max;
     if (bestmove!=NO_BEST) CurrentBest = bestmove;
}

/* -----------------
 * Send Winboard Analyzing Updates
   ----------------- */
void  wbSendThinking2( int bFinishedDepth )
{
	if (g_bAnalyze == FALSE || thinking == 0) return;
	int tm = (100*(clock() - starttime))/( CLOCKS_PER_SEC );
	int nDepth;
	char sCurMove[20];
	GetMoveString (sCurMove, MSrc (CurrentMove ), MDst ( CurrentMove ), GameBoard );
	// If we finished at this depth, by the time the user sees this we'll be searching the next
	if (bFinishedDepth == TRUE) nDepth = SearchDepth+1; else nDepth = SearchDepth;

	printf("stat01: %d %u %d %d %d %s\n", tm, nodes, nDepth, MoveStack[ 1 ].nummoves-CurrentMoveNum, MoveStack[ 1 ].nummoves, sCurMove);
	fflush(stdout);
}

/* -----------------
 * Send UCI thinking updates
   ----------------- */
void  UCISendThinking( int bFinishedDepth )
{
	// If we finished at this depth, by the time the user sees this we'll be searching the next
	int nDepth = SearchDepth;
	if (bFinishedDepth == TRUE) nDepth = SearchDepth+1; 

	int tm = (1000*(clock() - starttime))/( CLOCKS_PER_SEC );
	char sCurMove[20];
	GetPVMoveString (sCurMove, CurrentMove, 2);
	unsigned int nps;
	if (tm <= 0) nps = 0;
		else nps = (unsigned int)((double)nodes*1000.0f/(double)tm);

	if (tm > 1000) 
		{
		printf("info currmove %s currmovenumber %d depth %d nodes %u time %d nps %u \n", sCurMove, CurrentMoveNum, nDepth, nodes, tm, nps );
		fflush(stdout);
		}
}

// ----------------------------------
//
// Check For Mate or Stalemate
//
// ----------------------------------
int IsMate ( SBoard &Board, char Color)
{
int u1, u2, i;
Movestruct Moves;
SBoard TempBoard;

if (Color == WHITE)	{FindMovesWhiteCh ( Board, Moves);}
if (Color == BLACK)	{FindMovesBlackCh ( Board, Moves);}

for (i = 1; i<= Moves.nummoves + 50; i++)
	{
	TempBoard = Board;
	if (i == Moves.numAttackmoves + 1) i = 51;
	if ( i > Moves.nummoves + 50) break;
	Do_Move ( TempBoard, Moves.Moves[i], u1, u2, WQUEEN );
	if ( IsCheck (Color, TempBoard) == 0) return 0;
	}

if ( IsCheck (Color, Board) == 0) return 2;
	else return 1;
}

// ----------------------------------
//
// Check For Game Over for 2 Players
//
// ----------------------------------
int CheckGameOver ( SBoard &Board, int bPopUps, int &GameResult )
{
int mate = IsMate (Board, Board.SideToMove );
if (g_bWinBoard == TRUE) bPopUps = FALSE;

	if (mate == 2) 
		{GameResult = DRAW;
		 return TRUE;
		}
    if (mate == 1) 
		{
		if (Board.SideToMove == BLACK) GameResult = WHITEWINS;
		if (Board.SideToMove == WHITE) GameResult = BLACKWINS;
		return TRUE;
		}
    if ( DrawByRepetition (Board)  > 1 ) 
		{
		 GameResult = DRAW;
		 return TRUE;
		}
	if (  (Board.GameTime - Board.LastReversible) >= 100)
		{
		 GameResult = DRAW;
		 return TRUE;
		}
	if ((Board.nWPawns == 0 && Board.nBPawns == 0) 
		&& (Board.nBP == 0 || Board.nWP == 0) && (Board.nBP <= 1 && Board.nWP <= 1) 
		&& (Board.nWMinors == Board.nWP && Board.nBMinors == Board.nBP ) )
			{
			GameResult = DRAW;
			return TRUE;
			}
	return FALSE;
}
