/*
 *   Generating moves
 */

#include "phalanx.h"


unsigned char pin[120];
unsigned char tar[120];



/* enemy attacks given square */
int attacktest( int square, int ocolor )
{

/* pawn */
if(ocolor!=1)
{ if( B[square+11]==BP || B[square+9]==BP ) return 2; }
else
{ if( B[square-11]==WP || B[square-9]==WP ) return 2; }

/* knight */
{	int i, enemy = KNIGHT+ocolor;
	for( i=0; i!=8; i++ )
	if( B[square+N_moves[i]] == enemy ) return 2;
}

/* rook or queen */
{	int d, enemy1 = ROOK+ocolor, enemy2 = QUEEN+ocolor;
	for( d=0; d!=4; d++ )
	{	int i=square; int step=RB_dirs[d];
		do i += step; while( B[i] == 0 );
		if( B[i]==enemy1 || B[i]==enemy2 ) return 2;
	}
}

/* bishop or  queen */
{	int d, enemy1 = BISHOP+ocolor, enemy2 = QUEEN+ocolor;
	for( d=4; d!=8; d++ )
	{	int i=square; int step=RB_dirs[d];
		do i += step; while( B[i] == 0 );
		if( B[i]==enemy1 || B[i]==enemy2 ) return 2;
	}
}

/* king */
if( dist[ 120*square + L[ocolor].next ].max == 1 ) return 1;

return 0;

}



#undef debugsee
/* Static Exchange Evaluator */
int see( tsquare * b, int from, int square )
{

static int wl[16];
static int bl[16];
int wn, bn;
int color = color(b[from]);
int i,p;
int minwn, minbn;
unsigned static int backup;

#define ret(X) return ( color==WHITE ? (X) : -(X) )

wn=bn=1;
if( color == WHITE )
{ wl[0] = Values[ b[from] >> 4 ];
  bl[0] = Values[ b[square] >> 4 ];
}
else
{ wl[0] = Values[ b[square] >> 4 ];
  bl[0] = Values[ b[from] >> 4 ];
}

backup = b[from]; b[from] = 0;

if( b[square-9]==WP )  { wl[wn] = P_VALUE; wn++; }
if( b[square-11]==WP ) { wl[wn] = P_VALUE; wn++; }
if( b[square+9]==BP )  { bl[bn] = P_VALUE; bn++; }
if( b[square+11]==BP ) { bl[bn] = P_VALUE; bn++; }

for( i=0; i!=8; i++ ) if( ( p = b[square+N_moves[i]] ) )
switch( p )
{	case WN: wl[wn] = N_VALUE; wn++; break;
	case BN: bl[bn] = N_VALUE; bn++; break;
}

wl[ minwn=wn ] = CHECKMATE;
bl[ minbn=bn ] = CHECKMATE;

for( i=0; i!=8; i++ )
{	int step = RB_dirs[i];
	int minw = minwn;
	int minb = minbn;
	int dest = square;
	for(;;)
	{
		dest += step;
		while( b[dest] == 0 ) dest += step;
		p = b[dest];
		if( p==3 || piece(p)==KNIGHT ) break;
		if( i < 4 ) { if( piece(p) == BISHOP ) break; }
		else        { if( piece(p) == ROOK ) break; }
		if( p==WK )
		{
			if( dest-step==square )
			{ wl[wn] = CHECKMATE; wn++;
			  wl[wn] = CHECKMATE; }
			break;
		}
		else if( p==BK )
		{
			if( dest-step==square )
			{ bl[bn] = CHECKMATE; bn++;
			  bl[bn] = CHECKMATE; }
			break;
		}
		else if( p==WP )
		{
			if( dest-square == 9 || dest-square == 11 )
			continue; else break;
		}
		else if( p==BP )
		{
			if( dest-square == -9 || dest-square == -11 )
			continue; else break;
		}
		else
		{
			int j;
			int val = Values[p>>4];
			if( color(p) == WHITE )
			{
			  while( wl[minw] <= val ) minw++;
			  for( j=wn+1; j!=minw; j-- )
				wl[j] = wl[j-1];
			  wl[minw] = val;
			  wn++; wl[wn]=CHECKMATE;
			  minw++;
			}
			else
			{
			  while( bl[minb] <= val ) minb++;
			  for( j=bn+1; j!=minb; j-- )
				bl[j] = bl[j-1];
			  bl[minb] = val;
			  bn++; bl[bn]=CHECKMATE;
			  minb++;
			}
		}
	}
}
b[from] = backup;

#ifdef debugsee
printf("WHITE: "); for(i=0;i!=wn;i++) printf(" %hi",wl[i]); puts("");
printf("BLACK: "); for(i=0;i!=bn;i++) printf(" %hi",bl[i]); puts("");
#endif

/*** Let's count it ***/
{
	int wi=0; int bi=0; int result=0;
	int alpha=-CHECKMATE, beta=CHECKMATE;

	if( color == BLACK )
	{ result -= wl[wi]; wi++; alpha = result; }

	for(;;)
	{
		if( wi == wn ) ret(alpha);
		else
		{
			result += bl[bi]; bi++;
			if( result < beta )
			{ if( result < alpha ) ret(alpha); beta = result; }
		}
#ifdef debugsee
		printf("[w,%hi,%hi,%hi]",alpha,result,beta);
#endif

		if( bi == bn ) ret(beta);
		else
		{
			result -= wl[wi]; wi++;
			if( result > alpha )
			{ if( result > beta ) ret(beta); alpha = result; }
		}
#ifdef debugsee
		printf("[b,%hi,%hi,%hi]",alpha,result,beta);
#endif
	}
}

}



void find_pins( int check )
{
int d, square;

if(check) { memset( pin+20, 1, 80*sizeof(char) ); return; }

memset( pin+20, 0, 80*sizeof(char) );

square = L[Color].next;

pin[square] = 1;

for(d=0;d!=8;d++)
{
	int step = RB_dirs[d];
	int destination = square;
	int mark = 0;
	for(;;)
	{	destination += step;
		if( B[destination] )
		{
			if( color(B[destination]) == Color )
			{
				if(mark) break;
				mark = destination;
			}
			else
			{
				if(mark)
				{
					switch( piece(B[destination]) )
					{
					  case QUEEN:
						pin[mark]=abs(step);
					  break;
					  case ROOK:
						if(d<4) pin[mark]=abs(step);
					  break;
					  case BISHOP:
						if(d>3) pin[mark]=abs(step);
					  break;
					}
				}
				break;
			}
		}
	}
}

#undef debug
#ifdef debug
printboard();
for(d=0;d!=120;d++)
{
  if(d%10==0) puts("");
  printf("%3i",pin[d]);
}
getchar();
#endif

}



void find_targets(void)
{
int d,square;

/* memset( tar, 0, 120*sizeof(char) ); */
memset( tar+20, 0, 80*sizeof(char) );

square = L[enemy(Color)].next;

for(d=0;d!=8;d++)
{
	int step = RB_dirs[d];
	int destination = square;
	for(;;)
	{	destination += step;
		if( B[destination] ) break;
		if( d<4 ) tar[destination]=ROOK;
		else tar[destination]=BISHOP;
	}

	if( B[(destination=square+N_moves[d])] == 0 ) tar[destination]=KNIGHT;
}

#undef debug
#ifdef debug
printboard();
for(d=0;d!=120;d++)
{
  if(d%10==0) puts("");
  printf("%04X ",tar[d]);
}
getchar();
#endif

}



/*
 *    LINKING GENERATED MOVE
 */

int square, destination;
#define From square
#define To destination

void lm(
	tmove *m,
	unsigned char In2a, unsigned char Special,
	int *i )
{

m->in1 = B[m->from=From];
m->in2 = B[m->to=To];
m->in2a = In2a;
m->dch = 100;
m->value = 0;
m->special = Special;
if( m->special != 0 || pin[m->from] )
{
	do_move(m);
	if( ! checktest(enemy(Color)) ) (*i)++;
	undo_move(m);
} else (*i)++;

}

# define link_move(In2a,Special) \
 { lm( moves+i, In2a, Special, &i ); }

#define link_castling(C) \
 { register tmove* m = &moves[i]; \
   m->from = A1; /* anything different from 0 */ \
   m->to = A1; \
   m->dch = 100; \
   m->special = C; \
   i++; \
 }



void generate_legal_moves( tmove moves[], int *n, int check )
{

int i = 0;

find_pins( check );

for( square=L[Color].next; square!=0; square=L[square].next )
{
	unsigned pbs=piece(B[square]);
	switch ( pbs )
	{
	case PAWN:
	 if(Color==WHITE)
	 {	destination = square+10;
		if( B[destination] == 0 )
		{
			if( destination < A8 ) /* not a promotion */
			{ link_move(WP,0); }
			else /* pawn promotion */
			{
				unsigned piece;
				for( piece=WQ; piece!=WP; piece-=PAWN )
				link_move(piece,0);
			}
			if( square < A3 )
			{	destination += 10;
				if( B[destination] == 0 )
				link_move(B[square],0);
			}
		}
		for( destination = square+9;
		     destination != square+13;
		     destination += 2 )
		  if( color(B[destination]) == BLACK )
		  {
			if( destination < A8 )
			{ link_move(WP,0); }
			else
			{
				unsigned piece;
				for( piece=WQ; piece!=WP; piece-=PAWN )
				link_move(piece,0);
			}
		  }
	 }
	 else /* This is a black pawn */
	 {	destination = square-10;
		if( B[destination] == 0 )
		{
			if( destination>H1 ) /* not a promotion */
			{ link_move(BP,0); }
			else /* pawn promotion */
			{
				unsigned piece;
				for( piece=BQ; piece!=BP; piece-=PAWN )
				link_move(piece,0);
			}

			if( square>H6 )
			{	destination -= 10;
				if( B[destination] == 0 )
					link_move(BP,0);
			}
		}
		for( destination = square-9;
		     destination != square-13;
		     destination -= 2 )
		if( color(B[destination]) == WHITE )
		{
			if( destination > H1 )
			{ link_move(BP,0); }
			else
			{
				unsigned piece;
				for( piece=BQ; piece!=BP; piece-=PAWN )
				link_move(piece,0);
			}
		}
	 }
	break;
	case KNIGHT: case KING:
	 {	int j;
		int *mov = pbs==KING ? K_moves : N_moves;
		for( j=0; j!=8; j++ )
		{	destination = square+mov[j];
			if( ! ( color(B[destination]) & Color ) )
				link_move(B[square],0);
		}
	 }
	break;
	default: /* BISHOP, ROOK, QUEEN */
	 {	int d;
		int d1 = pbs==BISHOP ? 4 : 0;
		int d2 = pbs==ROOK   ? 4 : 8;
		for( d=d1; d!=d2; d++ )
		{	int step=RB_dirs[d];
			destination=square;
			for(;;)
			{	destination += step;
				if( ! ( color(B[destination]) & Color ) )
				link_move(B[square],0);
				if( B[destination] ) break;
			}
		}
	 }
	}
}

/* Special moves: en passant, castling */
/* En passant capture first */
if( Counter != 0 )
{
	int c = Counter-1;
	if( piece(G[c].m.in1) == PAWN  &&  abs(G[c].m.from-G[c].m.to) == 20 )
	/* The last move was a pawn double step */
	{
		for( square = G[c].m.to-1; square != G[c].m.to+3; square += 2 )
		if( B[square] == PAWN+Color )
		{
			int special = G[c].m.to;
			destination = Color==WHITE ? special+10 : special-10;
			link_move( B[square], special )
		}
	}
}

/* Castling */
if( ! check )
{
	if( Color == WHITE )
	{
		/****** Short ******/
		if( ( G[Counter].castling & WSHORT ) == 0 )
		 if( B[F1]==0 && B[G1]==0 )
		  if( ! attacktest(F1,BLACK) && ! attacktest(G1,BLACK) )
		   link_castling( SHORT_CASTLING );
		/****** Long ******/
		if( ( G[Counter].castling & WLONG ) == 0 )
		 if( B[B1]==0 && B[C1]==0 && B[D1]==0 )
		  if( ! attacktest(C1,BLACK) && ! attacktest(D1,BLACK) )
		   link_castling( LONG_CASTLING );
	}
	else /* Black */
	{
		/****** Short ******/
		if( ( G[Counter].castling & BSHORT ) == 0 )
		 if( B[F8]==0 && B[G8]==0 )
		  if( ! attacktest(F8,WHITE) && ! attacktest(G8,WHITE) )
		   link_castling( SHORT_CASTLING );
		/****** Long ******/
		if( ( G[Counter].castling & BLONG ) == 0 )
		 if( B[B8]==0 && B[C8]==0 && B[D8]==0 )
		  if( ! attacktest(C8,WHITE) && ! attacktest(D8,WHITE) )
		   link_castling( LONG_CASTLING );
	}
}

*n = i;

}



void lc( tmove *m, int *i )
{

if( Depth < -100 )
{
if( Values[ B[From]>>4 ] > Values[ B[To]>>4 ] )
{
  if( attacktest(To,enemy(Color)) ) return;
}
}
else
if( Values[ B[From]>>4 ] > Values[ B[To]>>4 ] )
{
	if( see(B,From,To) < 0 ) return;
}

m->in1 = m->in2a = B[m->from=From];
m->in2 = B[m->to=To];
m->dch = 100;
m->value = 0;
m->special = 0;
if( pin[m->from] )
{
	do_move(m);
	if( ! checktest(enemy(Color)) ) (*i)++;
	undo_move(m);
} else (*i)++;

}


# define link_capture { lc( moves+i, &i ); }



void generate_legal_captures( tmove moves[], int *n, int minval )
/* we are sure we are not in check */
{

int i = 0;

find_pins( 0 );

for( square=L[Color].next; square!=0; square=L[square].next )
{
	unsigned pbs=piece(B[square]);
	switch ( pbs )
	{
	case PAWN:
	 if(Color==WHITE)
	 {	destination = square+10;
		if( B[destination] == 0 && destination>=A8 && Depth>-300 )
		{ link_move(WQ,0); }
		for( destination = square+9;
		     destination != square+13;
		     destination += 2 )
		if( color(B[destination]) == BLACK )
		{
			if( destination < A8 )
			{
				if( Values[ B[destination] >> 4 ] >= minval )
					link_move(WP,0);
			}
			else
			{
				link_move(WQ,0);
			}
		}
	 }
	 else /* This is a black pawn */
	 {	destination = square-10;
		if( B[destination] == 0 && destination<=H1 && Depth>-300 )
		{ link_move(BQ,0); }
		for( destination = square-9;
		     destination != square-13;
		     destination -= 2 )
		if( color(B[destination]) == WHITE )
		{
			if( destination > H1 )
			{
				if( Values[ B[destination] >> 4 ] >= minval )
					link_move(BP,0);
			}
			else
			{
				link_move(BQ,0);
			}
		}
	 }
	break;
	case KNIGHT: case KING:
	 {	int j;
		int *mov = pbs==KING ? K_moves : N_moves;
		for( j=0; j!=8; j++ )
		{	destination = square+mov[j];
			if( color(B[destination]) == enemy(Color) )
			if( Values[ B[destination] >> 4 ] >= minval ) link_capture;
		}
	 }
	break;
	default: /* BISHOP, ROOK, QUEEN */
	 {	int d;
		int d1 = pbs==BISHOP ? 4 : 0;
		int d2 = pbs==ROOK   ? 4 : 8;
		for( d=d1; d!=d2; d++ )
		{	int step=RB_dirs[d];
			destination=square;
			for(;;)
			{	destination += step;
				if( color(B[destination]) == enemy(Color) )
				if( Values[ B[destination] >> 4 ] >= minval )
					link_capture;
				if( B[destination] ) break;
			}
		}
	 }
	}
}

*n = i;

}



#ifndef QCAPSONLY
/* generating checks AND captures */
void generate_legal_checks( tmove moves[], int *n )
/* we are sure we are not in check */
/* pawn checks should be added */
{

int i = 0;

find_pins( 0 );
find_targets();

for( square=L[Color].next; square!=0; square=L[square].next )
{
	unsigned pbs=piece(B[square]);
	switch ( pbs )
	{
	case PAWN:
	 if(Color==WHITE)
	 {	destination = square+10;
		if( B[destination] == 0 && ( destination>=A8 ) )
		{
			link_move(WQ,0);
			link_move(WN,0);
		}
		for( destination = square+9;
		     destination != square+13;
		     destination += 2 )
		if( color(B[destination]) == BLACK )
		{
			if( destination < A8 )
			{ link_move(WP,0); }
			else
			{
				link_move(WQ,0);
				link_move(WN,0);
			}
		}
	 }
	 else /* This is a black pawn */
	 {	destination = square-10;
		if( B[destination] == 0 && destination<=H1 )
		{
			link_move(BQ,0);
			link_move(BN,0);
		}
		for( destination = square-9;
		     destination != square-13;
		     destination -= 2 )
		if( color(B[destination]) == WHITE )
		{
			if( destination > H1 )
			{ link_move(BP,0); }
			else
			{
				link_move(BQ,0);
				link_move(BN,0);
			}
		}
	 }
	break;
	case KNIGHT: case KING:
	 {	int j;
		int *mov = pbs==KING ? K_moves : N_moves;
		for( j=0; j!=8; j++ )
		{	destination = square+mov[j];
			if( color(B[destination]) == enemy(Color)
			  || ( B[destination]==0 && tar[destination] == pbs ) )
			link_capture;
		}
	 }
	break;
	default: /* BISHOP, ROOK, QUEEN */
	 {	int d;
		int d1 = pbs==BISHOP ? 4 : 0;
		int d2 = pbs==ROOK   ? 4 : 8;
		for( d=d1; d!=d2; d++ )
		{	int step=RB_dirs[d];
			destination=square;
			for(;;)
			{	destination += step;
				if( color(B[destination]) == enemy(Color)
				  || ( B[destination]==0 && tar[destination]
				       &&
				       ( tar[destination]==pbs
				         || ( tar[destination]!=KNIGHT
				            && pbs==QUEEN )
				       )
				     )
				  )
				link_capture;
				if( B[destination] ) break;
			}
		}
	 }
	}
}

*n = i;

}

#endif /* QCAPSONLY */

