/*
 * genmoves.c - C source for GNU CHESS
 *
 * Copyright (c) 1988,1989,1990 John Stanback
 * Copyright (c) 1992 Free Software Foundation
 *
 * This file is part of GNU CHESS.
 *
 * GNU Chess 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 2, or (at your option)
 * any later version.
 *
 * GNU Chess 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 GNU Chess; see the file COPYING.  If not, write to
 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 */
#include "gnuchess.h"
#include "ataks.h"
SHORT *TrP, InCheck;

#define min(a,b)  (((a) >(b))?(b):(a))

#define Link(from,to,flag,s) \
{\
   node->f = from; node->t = to;\
     node->reply = 0;\
       node->flags = flag;\
	 node->score = s;\
	   ++node;\
	     (*TrP)++;\
	     }

inline int
CheckMove (register SHORT f, register SHORT t)

/*
 * See if any piece with color 'side' ataks sq.  First check pawns then
 * Queen, Bishop, Rook and King and last Knight.
 */

{
    SHORT xside;
    register SHORT sq;
    SHORT side, fsave, tsave, tcsave;
    if ((tsave = board[t]) == king) return false;
    xside = color[f];
    side = xside ^ 1;
    fsave = board[f];
    sq = ((fsave == king) ? t : PieceList[xside][0]);
    if (InCheck || fsave == king)
      {
    register UCHAR *ppos, *pdir;
    register SHORT u;
	  board[t] = fsave;
	  tcsave = color[t];
	  board[f] = no_piece;
	  color[f] = neutral;
	  color[t] = xside;
	  /* try a queen bishop capture */
	  ppos = nextpos[bishop][sq];
	  pdir = nextdir[bishop][sq];
	  u = ppos[sq];
	  do
	    {
		if (color[u] == neutral)
		    u = ppos[u];
		else if (color[u] == xside)
		    u = pdir[u];
		else if ((board[u] == bishop || board[u] == queen))
		  {
		      board[f] = fsave;
		      board[t] = tsave;
		      color[f] = xside;
		      color[t] = tcsave;
		      return (false);
		  }
		else
		    u = pdir[u];
	    }
	  while (u != sq);
	  /* try a queen rook capture */
	  ppos = nextpos[rook][sq];
	  pdir = nextdir[rook][sq];
	  u = ppos[sq];
	  do
	    {
		if (color[u] == neutral)
		    u = ppos[u];
		else if (color[u] == xside)
		    u = pdir[u];
		else if ((board[u] == rook || board[u] == queen))
		  {
		      board[f] = fsave;
		      board[t] = tsave;
		      color[f] = xside;
		      color[t] = tcsave;
		      return (false);
		  }
		else
		    u = pdir[u];
	    }
	  while (u != sq);
	  /* try a knight capture */
	  pdir = nextdir[knight][sq];
	  u = pdir[sq];
	  do
	    {
		if (color[u] == side && board[u] == knight)
		  {
		      board[f] = fsave;
		      board[t] = tsave;
		      color[f] = xside;
		      color[t] = tcsave;

		      return (false);
		  }

		u = pdir[u];
	    }
	  while (u != sq);
	  pdir = nextdir[ptype[xside][pawn]][sq];
	  u = pdir[sq];		/* follow captures thread */
	  if (u != sq)
	    {
		if (board[u] == pawn && color[u] == side)
		  {
		      board[f] = fsave;
		      board[t] = tsave;
		      color[f] = xside;
		      color[t] = tcsave;
		      return (false);
		  }
		u = pdir[u];
		if (u != sq && board[u] == pawn && color[u] == side)
		  {
		      board[f] = fsave;
		      board[t] = tsave;
		      color[f] = xside;
		      color[t] = tcsave;

		      return (false);
		  }
	    }
	  board[f] = fsave;
	  board[t] = tsave;
	  color[f] = xside;
	  color[t] = tcsave;
	  return (true);
      }
    else
      {
	  register SHORT *p, *q, i;

/* now one at a time */
/* diag */
	  if ((row (sq) - row (f)) == (column (sq) - column (f)))
	    {
		if ((row (sq) - row (t)) == (column (sq) - column (t)))
		    return true;
/* diag ++ */
		if (f > sq)
		  {
		      for (p = &board[sq + 9], q = &color[sq + 9], i = min (7 - row (sq), 7 - column (sq)); i; p += 9, q += 9, i--)
			{
			    if (*q == neutral || p == &board[f])
				continue;
			    else if (*q == xside)
				return true;
			    if (*p == bishop || *p == queen)
				return false;
			    else
				return true;
			}
		      /* diag -- */
		  }
		else
		  {
		      for (p = &board[sq - 9], q = &color[sq - 9], i = min (row (sq), column (sq)); i; p -= 9, q -= 9, i--)
			{
			    if (*q == neutral || p == &board[f])
				continue;
			    else if (*q == xside)
				return true;
			    if (*p == bishop || *p == queen)
				return false;
			    else
				return true;
			}
		  }
	    }
/* same col */
	  else if (column (sq) == column (f))
	    {
		if (column (f) == column (t))
		    return true;
		if (row (sq) < row (f))
		  {
		      /* col + */ for (p = &board[sq + 8], q = &color[sq + 8]; p <= &board[63]; p += 8, q += 8)
			{
			    if (*q == neutral || p == &board[f])
				continue;
			    else if (*q == xside)
				return true;
			    if (*p == rook || *p == queen)
				return false;
			    else
				return true;
			}
		  }
		else
		  {
		      /* col - */ for (p = &board[sq - 8], q = &color[sq - 8]; p >= &board[0]; p -= 8, q -= 8)
			{
			    if (*q == neutral || p == &board[f])
				continue;
			    else if (*q == xside)
				return true;
			    if (*p == rook || *p == queen)
				return false;
			    else
				return true;
			}
		  }
	    }
/* diag */
	  else if ((row (sq) - row (f)) == (-column (sq) + column (f)))
	    {
		if ((row (sq) - row (t)) == (-column (sq) + column (t)))
		    return true;
/* diag -+ */
		if (f < sq)
		  {
		      for (p = &board[sq - 7], q = &color[sq - 7], i = min (row (sq), 7 - column (sq)); i; p -= 7, q -= 7, i--)
			{
			    if (*q == neutral || p == &board[f])
				continue;
			    else if (*q == xside)
				return true;
			    if (*p == bishop || *p == queen)
				return false;
			    else
				return true;
			}
		      /* diag +- */
		  }
		else
		  {
		      for (p = &board[sq + 7], q = &color[sq + 7], i = min (7 - row (sq), column (sq)); i; p += 7, q += 7, i--)
			{
			    if (*q == neutral || p == &board[f])
				continue;
			    else if (*q == xside)
				return true;
			    if (*p == bishop || *p == queen)
				return false;
			    else
				return true;
			}
		  }
	    }
/* same row  + */
	  else if (row (sq) == row (f))
	    {
		if (row (sq) == row (t))
		    return true;
		if (column (sq) < column (f))
		  {
		      for (p = &board[sq + 1], q = &color[sq + 1]; p < &board[(f / 8 + 1) * 8]; p += 1, q += 1)
			{
			    if (*q == neutral || p == &board[f])
				continue;
			    else if (*q == xside)
				return true;
			    if (*p == rook || *p == queen)
				return false;
			    else
				return true;
			}
		  }
		else
		  {
		      /* same row - */ for (p = &board[sq - 1], q = &color[sq - 1]; p > &board[(f / 8) * 8]; p -= 1, q -= 1)
			{
			    if (*q == neutral || p == &board[f])
				continue;
			    else if (*q == xside)
				return true;
			    if (*p == rook || *p == queen)
				return false;
			    else
				return true;
			}
		  }
	    }
	  return true;
      }
    return true;
}


inline void
LinkMove (SHORT ply, SHORT f,
	  register SHORT t,
	  SHORT flag,
	  SHORT xside)

/*
 * Add a move to the tree.  Assign a bonus to order the moves as follows:
	1. Principle variation
	2. Capture of last moved piece
	3. Other captures (major pieces first)
	4. Killer moves
	5.
 */

{
    register SHORT s = 0;
#if defined HISTORY
    register SHORT z;
#endif
    register utshort mv;
    register struct leaf *node;

    node = &Tree[*TrP];
    mv = (f << 8) | t;
#ifdef KILLT
    s += killt[mv ];
#endif
#ifdef HISTORY
    z = mv;
    if (xside == white)
	z |= 0x4000;
    s += history[z];
#endif
    if (color[t] != neutral)
      {
	  /* TOsquare is the square the last piece moved moved to */
	  s += value[board[t]] - board[f] + ((t == TOsquare) ? 500 : 0);
      }
    if (board[f] == pawn)
	if (row (t) == 0 || row (t) == 7)
	  {
	      flag |= promote;
	      s += 800;
#if !defined OLDXBOARD  && !defined GNU3 && !defined CHESSTOOL
	      Link (f, t, flag | queen, s - 20000);
	      s -= 200;
	      Link (f, t, flag | knight, s - 20000);
	      s -= 50;
	      Link (f, t, flag | rook, s - 20000);
	      flag |= bishop;
	      s -= 50;
#else
	      flag |= queen;
#endif
	  }
	else if (row (t) == 1 || row (t) == 6)
	  {
	      flag |= pwnthrt;
	      s += 600;
	  }
	else if ((row (t) == ((color[f] == white) ? 5 : 2)) && (ply > MINDEPTH) && (ply < Sdepth + 3))
	  {
	      if ((mtl[white] - pmtl[white] + mtl[black] - pmtl[black]) < PTVALUE)
		{
		    flag |= pwnthrt;
		    s += 400;
		}
	  }
/*
      s += ((value[board[f]])/100);
*/
    Link (f, t, flag, s - 20000);
}

inline
void
GenMoves (register SHORT ply, register SHORT sq, SHORT side, SHORT xside)

/*
 * Generate moves for a piece. The moves are taken from the precalulated
 * array nextpos/nextdir. If the board is free, next move is choosen from
 * nextpos else from nextdir.
 */

{
    register SHORT u, piece;
    register UCHAR *ppos, *pdir;


    TrP = &TrPnt[ply + 1];
    piece = board[sq];
    ppos = nextpos[ptype[side][piece]][sq];
    pdir = nextdir[ptype[side][piece]][sq];
    if (piece == pawn)
      {
	  u = ppos[sq];		/* follow no captures thread */
	  if (color[u] == neutral)
	    {
		LinkMove (ply, sq, u, 0, xside);
		u = ppos[u];
		if (color[u] == neutral)
			  LinkMove (ply, sq, u, 0, xside);
	    }
	  u = pdir[sq];		/* follow captures thread */
	  if (color[u] == xside && board[u] != king)
	    {
		      LinkMove (ply, sq, u, capture, xside);
	    }
	  u = pdir[u];
	  if (color[u] == xside && board[u] != king)
	    {
		      LinkMove (ply, sq, u, capture, xside);
	    }
      }
    else
      {
	  u = ppos[sq];
	  do
	    {
		if (color[u] == neutral)
		  {
			    LinkMove (ply, sq, u, 0, xside);
		      u = ppos[u];
		  }
		else
		  {
		      if (color[u] == xside && board[u] != king)
			{
				  LinkMove (ply, sq, u, capture, xside);
			}
		      u = pdir[u];
		  }
	    }
	  while (u != sq);
      }
}

void
MoveList (SHORT side, register SHORT ply)

/*
 * Fill the array Tree[] with all available moves for side to play. Array
 * TrPnt[ply] contains the index into Tree[] of the first move at a ply.
 */

{
    register SHORT i, xside, f;

    xside = side ^ 1;
    TrP = &TrPnt[ply + 1];
    *TrP = TrPnt[ply];
    if (!PV)
	Swag0 = killr0[ply];
    else
	Swag0 = PV;
    Swag1 = killr1[ply];
    Swag2 = killr2[ply];
    Swag3 = killr3[ply];
    if (ply > 2)
	Swag4 = killr1[ply - 2];
    else
	Swag4 = 0;
#ifdef KILLT
    killt[SwagHt ] += 5000;
    killt[Swag0 ] += 2000;
    killt[Swag1 ] += 60;
    killt[Swag2 ] += 50;
    killt[Swag3 ] += 40;
    killt[Swag4 ] += 30;
#endif
#ifdef OLDHISTORY
    i = (side == black) ? 0x4000 : 0;
    history[SwagHt | i] += 5000;
    history[Swag0 | i] += 2000;
    history[Swag1 | i] += 60;
    history[Swag2 | i] += 50;
    history[Swag3 | i] += 40;
    history[Swag4 | i] += 30;
#endif
    for (i = PieceCnt[side]; i >= 0; i--)
	GenMoves (ply, PieceList[side][i], side, xside);
    if (!castld[side])
      {
	  f = PieceList[side][0];
	  if (castle (side, f, f + 2, 0))
	    {
		LinkMove (ply, f, f + 2, cstlmask, xside);
	    }
	  if (castle (side, f, f - 2, 0))
	    {
		LinkMove (ply, f, f - 2, cstlmask, xside);
	    }
      }
    if (epsquare > 0)
      {
	  register SHORT l;

	  f = epmove1[epsquare];
	  if (color[f] == side && board[f] == pawn)
	    {
		l = epsquare + ((epsquare > f) ? -8 : 8);
		board[l] = no_piece;
		color[l] = neutral;
		      LinkMove (ply, f, epsquare, capture | epmask, xside);
		board[l] = pawn;
		color[l] = xside;
	    }
	  f = epmove2[epsquare];
	  if (color[f] == side && board[f] == pawn)
	    {
		l = epsquare + ((epsquare > f) ? -8 : 8);
		board[l] = no_piece;
		color[l] = neutral;
		      LinkMove (ply, f, epsquare, capture | epmask, xside);
		board[l] = pawn;
		color[l] = xside;
	    }
      }
#ifdef KILLT
    killt[SwagHt ] -= 5000;
    killt[Swag0 ] -= 2000;
    killt[Swag1 ] -= 60;
    killt[Swag2 ] -= 50;
    killt[Swag3 ] -= 40;
    killt[Swag4 ] -= 30;
#endif
#ifdef OLDHISTORY
    i = (side == black) ? 0x4000 : 0;
    history[SwagHt | i] -= 5000;
    history[Swag0 | i] -= 2000;
    history[Swag1 | i] -= 60;
    history[Swag2 | i] -= 50;
    history[Swag3 | i] -= 40;
    history[Swag4 | i] -= 30;
#endif
    SwagHt = 0;			/* SwagHt is only used once */
    GenCnt += (TrPnt[ply + 1] - TrPnt[ply]);
}

void
CaptureList (register SHORT side, SHORT ply)

/*
 * Fill the array Tree[] with all available cature and promote moves for side
 * to play. Array TrPnt[ply] contains the index into Tree[] of the first move
 * at a ply.
 */

{
    register SHORT u, sq, xside;
    register struct leaf *node;
    register UCHAR *ppos, *pdir;
    SHORT i, piece, *PL, r7;

    xside = side ^ 1;
    TrP = &TrPnt[ply + 1];
    *TrP = TrPnt[ply];
    node = &Tree[*TrP];
    r7 = rank7[side];
    PL = PieceList[side];
#ifdef notdef
    sidebit = ((side == white) ? 0 : 0x80);
    killt[SwagHt | sidebit] += 5000;
    killt[Swag0 | sidebit] += 2000;
    killt[Swag1 | sidebit] += 60;
    killt[Swag2 | sidebit] += 50;
    killt[Swag3 | sidebit] += 40;
    killt[Swag4 | sidebit] += 30;
#endif

    for (i = 0; i <= PieceCnt[side]; i++)
      {
	  SHORT r = row(PieceList[xside][0]);
	  SHORT c = column(PieceList[xside][0]);
	  sq = PL[i];
	  piece = board[sq];
	  if (sweep[piece])
	    {
		ppos = nextpos[piece][sq];
		pdir = nextdir[piece][sq];
		u = ppos[sq];
		do
		  {
		      if (color[u] == neutral) {
			    if (piece == rook | piece == queen) {
				if(r == row(sq) || c == column(sq)) Link (sq, u, 0, -999);}
				else if(abs(r - row(sq)) == abs(c - column(sq)))Link (sq, u, 0, -999);
			u = ppos[u];}
		      else
			{
			    if (color[u] == xside) Link (sq, u, capture, value[board[u]] + svalue[u] - piece);
			    u = pdir[u];
			}
		  }
		while (u != sq);
	    }
	  else
	    {
		pdir = nextdir[ptype[side][piece]][sq];
		if (piece == pawn && row (sq) == r7)
		  {
		      u = pdir[sq];
		      if (color[u] == xside){
			  Link (sq, u, capture | promote | queen, valueQ);
#if !defined OLDXBOARD  && !defined GNU3 && !defined CHESSTOOL
			    Link (sq, u, capture | promote | knight, valueN);
			    Link (sq, u, capture | promote | rook, valueR);
			    Link (sq, u, capture | promote | bishop, valueB);
#endif
		      }
		      u = pdir[u];
		      if (color[u] == xside)
			{
			    Link (sq, u, capture | promote | queen, valueQ);
#if !defined OLDXBOARD  && !defined GNU3 && !defined CHESSTOOL
			    Link (sq, u, capture | promote | knight, valueN);
			    Link (sq, u, capture | promote | rook, valueR);
			    Link (sq, u, capture | promote | bishop, valueB);
#endif
			}
		      ppos = nextpos[ptype[side][piece]][sq];
		      u = ppos[sq];	/* also generate non capture promote */
		      if (color[u] == neutral)
			{
			    Link (sq, u, promote | queen, valueQ);
#if !defined OLDXBOARD  && !defined GNU3 && !defined CHESSTOOL
			    Link (sq, u, promote | knight, valueN);
			    Link (sq, u, promote | rook, valueR);
			    Link (sq, u, promote | bishop, valueB);
#endif
			}
		  }
		else
		  {
		      u = pdir[sq];
		      do
			{
			    if (color[u] == xside)
				Link (sq, u, capture, value[board[u]] + svalue[u] - piece);
			    u = pdir[u];
			}
		      while (u != sq);
		  }
	    }
      }
#ifdef notdef
    sidebit = ((side == white) ? 0 : 0x80);
    killt[SwagHt | sidebit] -= 5000;
    killt[Swag0 | sidebit] -= 2000;
    killt[Swag1 | sidebit] -= 60;
    killt[Swag2 | sidebit] -= 50;
    killt[Swag3 | sidebit] -= 40;
    killt[Swag4 | sidebit] -= 30;
#endif
    SwagHt = 0;			/* SwagHt is only used once */
}
inline
void
VGenMoves (register SHORT ply, register SHORT sq, SHORT side, SHORT xside)

/*
 * Generate moves for a piece. The moves are taken from the precalulated
 * array nextpos/nextdir. If the board is free, next move is choosen from
 * nextpos else from nextdir.
 */

{
    register SHORT u, piece;
    register UCHAR *ppos, *pdir;


    TrP = &TrPnt[ply + 1];
    piece = board[sq];
    ppos = nextpos[ptype[side][piece]][sq];
    pdir = nextdir[ptype[side][piece]][sq];
    if (piece == pawn)
      {
	  u = ppos[sq];		/* follow no captures thread */
	  if (color[u] == neutral)
	    {
		if (CheckMove (sq, u))
		  {
		      LinkMove (ply, sq, u, 0, xside);
		  }
		u = ppos[u];
		if (color[u] == neutral)
		    if (CheckMove (sq, u))
		      {
			  LinkMove (ply, sq, u, 0, xside);
		      }
	    }
	  u = pdir[sq];		/* follow captures thread */
	  if (color[u] == xside && board[u] != king)
	    {
		if (CheckMove (sq, u))
		  {
		      LinkMove (ply, sq, u, capture, xside);
		  }
	    }
	  u = pdir[u];
	  if (color[u] == xside && board[u] != king)
	    {
		if (CheckMove (sq, u))
		  {
		      LinkMove (ply, sq, u, capture, xside);
		  }
	    }
      }
    else
      {
	  u = ppos[sq];
	  do
	    {
		if (color[u] == neutral)
		  {
		      if (CheckMove (sq, u))
			{
			    LinkMove (ply, sq, u, 0, xside);
			}
		      u = ppos[u];
		  }
		else
		  {
		      if (color[u] == xside && board[u] != king)
			{
			    if (CheckMove (sq, u))
			      {
				  LinkMove (ply, sq, u, capture, xside);
			      }
			}
		      u = pdir[u];
		  }
	    }
	  while (u != sq);
      }
}

void
VMoveList (SHORT side, register SHORT ply)

/*
 * Fill the array Tree[] with all available moves for side to play. Array
 * TrPnt[ply] contains the index into Tree[] of the first move at a ply.
 */

{
    register SHORT i, xside, f;

    xside = side ^ 1;
    TrP = &TrPnt[ply + 1];
    *TrP = TrPnt[ply];
    InCheck = SqAtakd (PieceList[side][0], side ^ 1);
#ifdef notdef
    if (!PV)
	Swag0 = killr0[ply];
    else
	Swag0 = PV;
    Swag1 = killr1[ply];
    Swag2 = killr2[ply];
    Swag3 = killr3[ply];
    if (ply > 2)
	Swag4 = killr1[ply - 2];
    else
	Swag4 = 0;
#ifdef KILLT
    sidebit = ((side == white) ? 0 : 0x80);
    killt[SwagHt | sidebit] += 5000;
    killt[Swag0 | sidebit] += 2000;
    killt[Swag1 | sidebit] += 60;
    killt[Swag2 | sidebit] += 50;
    killt[Swag3 | sidebit] += 40;
    killt[Swag4 | sidebit] += 30;
#endif
#ifdef HISTORY
    i = (side == black) ? 0x4000 : 0;
    history[SwagHt | i] += 5000;
    history[Swag0 | i] += 2000;
    history[Swag1 | i] += 60;
    history[Swag2 | i] += 50;
    history[Swag3 | i] += 40;
    history[Swag4 | i] += 30;
#endif
#endif
    for (i = PieceCnt[side]; i >= 0; i--)
	VGenMoves (ply, PieceList[side][i], side, xside);
    if (!castld[side])
      {
	  f = PieceList[side][0];
	  if (castle (side, f, f + 2, 0))
	    {
		LinkMove (ply, f, f + 2, cstlmask, xside);
	    }
	  if (castle (side, f, f - 2, 0))
	    {
		LinkMove (ply, f, f - 2, cstlmask, xside);
	    }
      }
    if (epsquare > 0)
      {
	  register SHORT l;

	  f = epmove1[epsquare];
	  if (color[f] == side && board[f] == pawn)
	    {
		l = epsquare + ((epsquare > f) ? -8 : 8);
		board[l] = no_piece;
		color[l] = neutral;
		if (CheckMove (f, epsquare))
		  {
		      LinkMove (ply, f, epsquare, capture | epmask, xside);
		  }
		board[l] = pawn;
		color[l] = xside;
	    }
	  f = epmove2[epsquare];
	  if (color[f] == side && board[f] == pawn)
	    {
		l = epsquare + ((epsquare > f) ? -8 : 8);
		board[l] = no_piece;
		color[l] = neutral;
		if (CheckMove (f, epsquare))
		  {
		      LinkMove (ply, f, epsquare, capture | epmask, xside);
		  }
		board[l] = pawn;
		color[l] = xside;
	    }
      }
#ifdef notdef
#ifdef KILLT
    killt[SwagHt | sidebit] -= 5000;
    killt[Swag0 | sidebit] -= 2000;
    killt[Swag1 | sidebit] -= 60;
    killt[Swag2 | sidebit] -= 50;
    killt[Swag3 | sidebit] -= 40;
    killt[Swag4 | sidebit] -= 30;
#endif
#ifdef HISTORY
    i = (side == black) ? 0x4000 : 0;
    history[SwagHt | i] -= 5000;
    history[Swag0 | i] -= 2000;
    history[Swag1 | i] -= 60;
    history[Swag2 | i] -= 50;
    history[Swag3 | i] -= 40;
    history[Swag4 | i] -= 30;
#endif
    SwagHt = 0;			/* SwagHt is only used once */
    GenCnt += (TrPnt[ply + 1] - TrPnt[ply]);
#endif
}
