/* move.c

   GNU Chess frontend

   Copyright (C) 2001-2011 Free Software Foundation, Inc.

   GNU Chess is based on the two research programs
   Cobalt by Chua Kong-Sian and Gazebo by Stuart Cracraft.

   This program 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 3 of the License, or
   (at your option) any later version.

   This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.

   Contact Info:
     bug-gnu-chess@gnu.org
     cracraft@ai.mit.edu, cracraft@stanfordalumni.org, cracraft@earthlink.net
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "common.h"

void MakeMove (int side, int *move)
/**************************************************************************
 *
 *  To make a move on the board and update the various game information.
 *
 **************************************************************************/
{
   BitBoard *a;
   int f, t, fpiece, tpiece;
   int rookf, rookt, epsq, sq;
   int xside;
   GameRec *g;

   xside = 1^side;
   f = FROMSQ(*move);
   t = TOSQ(*move);
   fpiece = cboard[f];
   tpiece = cboard[t];
   a = &board.b[side][fpiece];
   CLEARBIT (*a, f);
   SETBIT (*a, t);
   CLEARBIT (board.blockerr90, r90[f]);
   SETBIT (board.blockerr90, r90[t]);
   CLEARBIT (board.blockerr45, r45[f]);
   SETBIT (board.blockerr45, r45[t]);
   CLEARBIT (board.blockerr315, r315[f]);
   SETBIT (board.blockerr315, r315[t]);
   cboard[f] = empty;
   cboard[t] = fpiece;
   GameCnt++;
   g = &Game[GameCnt];
   g->epsq = board.ep;
   g->bflag = board.flag;
   g->Game50 = Game50;
   g->hashkey = HashKey;
   g->phashkey = PawnHashKey;
   g->mvboard = Mvboard[t];
   g->comments = NULL;
   Mvboard[t] = Mvboard[f]+1;
   Mvboard[f] = 0;
   if (board.ep > -1)
      HashKey ^= ephash[board.ep];
   HashKey ^= hashcode[side][fpiece][f];
   HashKey ^= hashcode[side][fpiece][t];
   if (fpiece == king)
      board.king[side] = t;
   if (fpiece == pawn)
   {
      PawnHashKey ^= hashcode[side][pawn][f];
      PawnHashKey ^= hashcode[side][pawn][t];
   }

   if (tpiece != 0)		/* Capture */
   {
      ExchCnt[side]++;
      CLEARBIT (board.b[xside][tpiece], t);
      *move |= (tpiece << 15);
      HashKey ^= hashcode[xside][tpiece][t];
      if (tpiece == pawn)
         PawnHashKey ^= hashcode[xside][pawn][t];
      board.material[xside] -= Value[tpiece];
      if (tpiece != pawn)
         board.pmaterial[xside] -= Value[tpiece];
   }

   if (*move & PROMOTION) 	/* Promotion */
   {
      SETBIT (board.b[side][PROMOTEPIECE (*move)], t);
      CLEARBIT (*a, t);
      cboard[t] = PROMOTEPIECE (*move);
      HashKey ^= hashcode[side][pawn][t];
      HashKey ^= hashcode[side][cboard[t]][t];
      PawnHashKey ^= hashcode[side][pawn][t];
      board.material[side] += (Value[cboard[t]] - ValueP);
      board.pmaterial[side] += Value[cboard[t]];
   }

   if (*move & ENPASSANT)	/* En passant */
   {
      ExchCnt[side]++;
      epsq = board.ep + (side == white ? - 8 : 8);
      CLEARBIT (board.b[xside][pawn], epsq);
      CLEARBIT (board.blockerr90, r90[epsq]);
      CLEARBIT (board.blockerr45, r45[epsq]);
      CLEARBIT (board.blockerr315, r315[epsq]);
      cboard[epsq] = empty;
      HashKey ^= hashcode[xside][pawn][epsq];
      PawnHashKey ^= hashcode[xside][pawn][epsq];
      board.material[xside] -= ValueP;
   }
   if (*move & (CAPTURE | CASTLING) || fpiece == pawn)
      Game50 = GameCnt;

   if (*move & CASTLING) 	/* Castling */
   {
      if (t & 0x04)		/* King side */
      {
         rookf = t + 1;
         rookt = t - 1;
      }
      else			/* Queen side */
      {
         rookf = t - 2;
         rookt = t + 1;
      }
      a = &board.b[side][rook];
      CLEARBIT (*a, rookf);
      SETBIT (*a, rookt);
      CLEARBIT (board.blockerr90, r90[rookf]);
      SETBIT (board.blockerr90, r90[rookt]);
      CLEARBIT (board.blockerr45, r45[rookf]);
      SETBIT (board.blockerr45, r45[rookt]);
      CLEARBIT (board.blockerr315, r315[rookf]);
      SETBIT (board.blockerr315, r315[rookt]);
      cboard[rookf] = empty;
      cboard[rookt] = rook;
      Mvboard[rookf] = 0;
      Mvboard[rookt] = 1;
      HashKey ^= hashcode[side][rook][rookf];
      HashKey ^= hashcode[side][rook][rookt];
      board.castled[side] = true;
   }

   /* If king or rook move, clear castle flag. */
   if (side == white)
   {
      if (fpiece == king && board.flag & WCASTLE)
      {
	 if (board.flag & WKINGCASTLE)
            HashKey ^= WKCastlehash;
	 if (board.flag & WQUEENCASTLE)
            HashKey ^= WQCastlehash;
         board.flag &= ~WCASTLE;
      }
      else if (fpiece == rook)
      {
         if (f == H1)
	 {
	    if (board.flag & WKINGCASTLE)
               HashKey ^= WKCastlehash;
	    board.flag &= ~WKINGCASTLE;
	 }
         else if (f == A1)
	 {
	    if (board.flag & WQUEENCASTLE)
               HashKey ^= WQCastlehash;
	    board.flag &= ~WQUEENCASTLE;
	 }
      }
      if (tpiece == rook)
      {
         if (t == H8)
	 {
	    if (board.flag & BKINGCASTLE)
	       HashKey ^= BKCastlehash;
	    board.flag &= ~BKINGCASTLE;
         }
         else if (t == A8)
	 {
	    if (board.flag & BQUEENCASTLE)
	       HashKey ^= BQCastlehash;
	    board.flag &= ~BQUEENCASTLE;
	 }
      }
   }
   else
   {
      if (fpiece == king && board.flag & BCASTLE)
      {
	 if (board.flag & BKINGCASTLE)
	    HashKey ^= BKCastlehash;
	 if (board.flag & BQUEENCASTLE)
	    HashKey ^= BQCastlehash;
         board.flag &= ~BCASTLE;
      }
      else if (fpiece == rook)
      {
         if (f == H8)
	 {
	    if (board.flag & BKINGCASTLE)
	       HashKey ^= BKCastlehash;
	    board.flag &= ~BKINGCASTLE;
         }
         else if (f == A8)
	 {
	    if (board.flag & BQUEENCASTLE)
	       HashKey ^= BQCastlehash;
	    board.flag &= ~BQUEENCASTLE;
	 }
      }
      if (tpiece == rook)
      {
         if (t == H1)
	 {
	    if (board.flag & WKINGCASTLE)
               HashKey ^= WKCastlehash;
	    board.flag &= ~WKINGCASTLE;
	 }
         else if (t == A1)
	 {
	    if (board.flag & WQUEENCASTLE)
               HashKey ^= WQCastlehash;
	    board.flag &= ~WQUEENCASTLE;
	 }
      }
   }


   /* If pawn move 2 squares, set ep passant square. */
   if (fpiece == pawn && abs(f-t) == 16)
   {
      sq = (f + t) / 2;
      board.ep = sq;
      HashKey ^= ephash[sq];
   }
   else
      board.ep = -1;

   board.side = xside;
   HashKey ^= Sidehash;
   UpdateFriends ();

   /* Update game record */
   g->move = *move;
   return;
}



void UnmakeMove (int side, int *move)
/****************************************************************************
 *
 *  To unmake a move on the board and update the various game information.
 *  Note that if side is black, then black is about to move, but we will be
 *  undoing a move by white, not black.
 *
 ****************************************************************************/
{
   BitBoard *a;
   int f, t, fpiece, cpiece;
   int rookf, rookt, epsq;
   int xside;
   GameRec *g;

   side = 1^side;
   xside = 1^side;
   f = FROMSQ(*move);
   t = TOSQ(*move);
   fpiece = cboard[t];
   cpiece = CAPTUREPIECE (*move);
   a = &board.b[side][fpiece];
   CLEARBIT (*a, t);
   SETBIT (*a, f);
   CLEARBIT (board.blockerr90, r90[t]);
   SETBIT (board.blockerr90, r90[f]);
   CLEARBIT (board.blockerr45, r45[t]);
   SETBIT (board.blockerr45, r45[f]);
   CLEARBIT (board.blockerr315, r315[t]);
   SETBIT (board.blockerr315, r315[f]);
   cboard[f] = cboard[t];
   cboard[t] = empty;
   g = &Game[GameCnt];
   Mvboard[f] = Mvboard[t]-1;
   Mvboard[t] = g->mvboard;
   if (fpiece == king)
      board.king[side] = f;

   /* if capture, put back the captured piece */
   if (*move & CAPTURE)
   {
      ExchCnt[side]--;
      SETBIT (board.b[xside][cpiece], t);
      SETBIT (board.blockerr90, r90[t]);
      SETBIT (board.blockerr45, r45[t]);
      SETBIT (board.blockerr315, r315[t]);
      cboard[t] = cpiece;
      board.material[xside] += Value[cpiece];
      if (cpiece != pawn)
         board.pmaterial[xside] += Value[cpiece];
   }

   /* Undo promotion */
   if (*move & PROMOTION)
   {
      CLEARBIT (*a, f);
      SETBIT (board.b[side][pawn], f);
      cboard[f] = pawn;
      board.material[side] += (ValueP - Value[PROMOTEPIECE (*move)]);
      board.pmaterial[side] -= Value[PROMOTEPIECE (*move)];
   }

   /* Undo enpassant */
   if (*move & ENPASSANT)
   {
      ExchCnt[side]--;
      epsq = (side == white ? g->epsq - 8 : g->epsq + 8);
      SETBIT (board.b[xside][pawn], epsq);
      SETBIT (board.blockerr90, r90[epsq]);
      SETBIT (board.blockerr45, r45[epsq]);
      SETBIT (board.blockerr315, r315[epsq]);
      cboard[epsq] = pawn;
      board.material[xside] += ValueP;
   }

   /* if castling, undo rook move */
   if (*move & CASTLING)
   {
      if (t & 0x04)		/* King side */
      {
         rookf = t + 1;
         rookt = t - 1;
      }
      else			/* Queen side */
      {
         rookf = t - 2;
         rookt = t + 1;
      }
      a = &board.b[side][rook];
      CLEARBIT (*a, rookt);
      SETBIT (*a, rookf);
      CLEARBIT (board.blockerr90, r90[rookt]);
      SETBIT (board.blockerr90, r90[rookf]);
      CLEARBIT (board.blockerr45, r45[rookt]);
      SETBIT (board.blockerr45, r45[rookf]);
      CLEARBIT (board.blockerr315, r315[rookt]);
      SETBIT (board.blockerr315, r315[rookf]);
      cboard[rookf] = rook;
      cboard[rookt] = empty;
      Mvboard[rookf] = 0;
      Mvboard[rookt] = 0;
      board.castled[side] = false;
   }

   UpdateFriends ();
   board.side = side;
   board.ep = g->epsq;
   board.flag = g->bflag;
   HashKey = g->hashkey;
   PawnHashKey = g->phashkey;
   Game50 = g->Game50;
   GameCnt--;
   return;
}


void SANMove (int move, int ply)
/****************************************************************************
 *
 *  Convert the move to a SAN format.  GenMoves (ply) needs to be called
 *  by the calling routine for this to work.
 *
 ****************************************************************************/
{
   int side;
   int piece, ambiguous;
   int f, t;
   BitBoard b;
   leaf *node1;
   char *s;

   side = board.side;
   s = SANmv;
   f = FROMSQ(move);
   t = TOSQ(move);
   /* Check some special moves like castling */
   if (move & CASTLING)
   {
      if (t == 6 || t == 62)
         strcpy (s, "O-O");
      else
	 strcpy (s, "O-O-O");
      return;
   }

/****************************************************************************
 *
 *  Here split the code into 2 parts for clarity sake.  First part deals
 *  with pawn moves only, 2nd part only piece moves.  However before doing
 *  that, see if the move is ambiguous.
 *
 ****************************************************************************/

   /*  AMBIGUITY CHECK  */
   piece = cboard[f];
   side = board.side;
   b = board.b[side][piece];
   ambiguous = false;
   node1 = TreePtr[ply];
   if (nbits (b) > 1)
   {
      /*
       *  Scan the movelist to see if another same-type piece is
       *  also moving to that particular to-square.
       */
      for (node1 = TreePtr[ply]; node1 < TreePtr[ply + 1]; node1++)
      {
         if (FROMSQ(node1->move) == f)
            continue;                   /* original piece, skip */
         if (TOSQ(node1->move) != t)
            continue;                   /* diff to-square, skip */
         if (cboard[FROMSQ(node1->move)] != piece)
            continue;                   /* diff piece   */
         ambiguous = true;
	 break;
      }
   }

   if (piece == pawn)
   {
      /* Capture or enpassant */
      if (cboard[t] != 0 || board.ep == t)
      {
         *s++ = algbrfile [ROW (f)];
	 *s++ = 'x';
      }
      strcpy (s, algbr[t]);
      s += 2;

      /* Promotion */
      if (move & PROMOTION)
      {
         *s++ = '=';
         *s++ = notation[PROMOTEPIECE (move)];
      }
   }
   else	/* its not pawn */
   {
      *s++ = notation[piece];
      if (ambiguous)
      {
         if (ROW (f) == ROW (FROMSQ(node1->move)))
            *s++ = algbrrank[RANK (f)];
	 else
	    *s++ = algbrfile[ROW (f)];
      }
      if (cboard[t] != 0)		/* capture */
         *s++ = 'x';
      strcpy (s, algbr[t]);
      s += 2;
   }

   /* See if it is a checking or mating move */
   MakeMove (side, &move);
   if (SqAtakd (board.king[1^side], side))
   {
      TreePtr[ply+2] = TreePtr[ply+1];
      GenCheckEscapes (ply+1);
      if (TreePtr[ply+1] == TreePtr[ply+2])
         *s++ = '#';
      else
         *s++ = '+';
      GenCnt -= TreePtr[ply+2] - TreePtr[ply+1];
   }
   UnmakeMove (1^side, &move);

   *s = '\0';
   return;
}


#define ASCIITOFILE(a) ((a) - 'a')
#define ASCIITORANK(a) ((a) - '1')
#define ASCIITOSQ(a,b) (ASCIITOFILE(a)) + (ASCIITORANK(b)) * 8
#define ATOH(a) ((a) >= 'a' && (a) <= 'h')
#define ITO8(a) ((a) >= '1' && (a) <= '8')

inline int piece_id(const char c)
{
/* Given c, what is the piece id.  This only takes one char, which
 * isn't enough to handle two-character names (common in Russian text
 * and old English notation that used Kt), but we're not supposed to
 * see such text here anyway.  This will
 * accept "P" for pawn, and it used to accept many lowercase chars
 * (but not "b" for Bishop). However, lowercase chars are no longer
 * accepted, since they are not allowed by the Fruit engine. */
   switch (c)
   {
      case 'N':
         return knight;
      case 'B':
         return bishop;
      case 'R':
         return rook;
      case 'Q':
         return queen;
      case 'K':
         return king;
      case 'P':
         return pawn;
   }
   return empty;
}



leaf * ValidateMove (char *s)
/*************************************************************************
 *
 *  This routine takes a string and check to see if it is a legal move.
 *  Note.  At the moment, we accept 2 types of moves notation.
 *  1.  e2e4 format.   2. SAN format. (e4)
 *
 **************************************************************************/
{
   short f, t, side, rank, file, fileto;
   short piece, piece2, kount;
   char promote;
   char mvstr[MAXSTR], *p;
   BitBoard b, b2;
   leaf *n1, *n2;

   TreePtr[2] = TreePtr[1];
   GenMoves (1);
   FilterIllegalMoves (1);
   side = board.side;

   /************************************************************************
    * The thing to do now is to clean up the move string.  This
    * gets rid of things like 'x', '+', '=' and so forth.
    ************************************************************************/
   p = mvstr;
   do
   {
      if (*s != 'x' && *s != '+' && *s != '=' && !isspace(*s))
         *p++ = *s;
   } while (*s++ != '\0' );

   /* Flush castles that check */
   if (mvstr[strlen(mvstr)-1] == '+' || mvstr[strlen(mvstr)-1] == '#' ||
       mvstr[strlen(mvstr)-1] == '=') mvstr[strlen(mvstr)-1] = '\000';

   /* Check for castling */
   if (strcmp (mvstr, "O-O") == 0 || strcmp (mvstr, "o-o") == 0 ||
       strcmp (mvstr, "0-0") == 0)
   {
      if (side == white)
      {
         f = 4; t = 6;
      }
      else
      {
	 f = 60; t = 62;
      }
      return (IsInMoveList (1, f, t, ' '));
   }

   if (strcmp (mvstr, "O-O-O") == 0 || strcmp (mvstr, "o-o-o") == 0 ||
       strcmp (mvstr, "0-0-0") == 0)
   {
      if (side == white)
      {
         f = 4; t = 2;
      }
      else
      {
         f = 60; t = 58;
      }
      return (IsInMoveList (1, f, t, ' '));
   }

   /*  Test to see if it is e2e4 type notation */
   if (ATOH (mvstr[0]) && ITO8 (mvstr[1]) && ATOH (mvstr[2]) &&
	ITO8 (mvstr[3]))
   {
      f = ASCIITOSQ (mvstr[0], mvstr[1]);
      t = ASCIITOSQ (mvstr[2], mvstr[3]);
      piece = (strlen (mvstr) == 5 ? mvstr[4] : ' ');
      return (IsInMoveList (1, f, t, piece));
   }


   /***********************************************************************
    *  Its a SAN notation move.  More headache!
    *  We generate all the legal moves and start comparing them with
    *  the input move.
    ***********************************************************************/
   if (ATOH (mvstr[0]))	/* pawn move */
   {
      if (ITO8 (mvstr[1]))					/* e4 type */
      {
	 t = ASCIITOSQ (mvstr[0], mvstr[1]);
         f = t + (side == white ? -8 : 8);
         /* Add Sanity Check */
         if ( f > 0 && f < 64 ) {
	   if (BitPosArray[f] & board.b[side][pawn])
           {
              if (mvstr[2] != '\0')
                 return (IsInMoveList (1, f, t, mvstr[2]));
              else
                 return (IsInMoveList (1, f, t, ' '));
           }
           f = t + (side == white ? -16 : 16);
           if ( f > 0 && f < 64 ) {
	     if (BitPosArray[f] & board.b[side][pawn])
                return (IsInMoveList (1, f, t, ' '));
	   } /* End bound check +/- 16 */
	 } /* End bound check +/- 8 */
        }
      else if (ATOH (mvstr[1]) && ITO8 (mvstr[2]))		/* ed4 type */
      {
	 t = ASCIITOSQ (mvstr[1], mvstr[2]);
	 rank = ASCIITORANK (mvstr[2]) + (side == white ? -1 : 1);
         f = rank * 8 + ASCIITOFILE (mvstr[0]);
         piece = (strlen (mvstr) == 4 ? mvstr[3] : ' ');
         return (IsInMoveList (1, f, t, piece));
      }
      else if (ATOH (mvstr[1]))					/* ed type */
      {
         file = ASCIITOFILE (mvstr[0]);
         fileto = ASCIITOFILE (mvstr[1]);
	 b = board.b[side][pawn] & FileBit[file];
	 if (side == white)
	    b = b >> (fileto < file ? 7 : 9);
         else
	    b = b << (fileto < file ? 9 : 7);
         if (board.ep > -1)
	    b = b & (board.friends[1^side] | BitPosArray[board.ep]);
         else
	    b = b & (board.friends[1^side]);
         switch (nbits (b))
	 {
	    case 0  : return ((leaf *) NULL);
	    case 1  : t = leadz (b);
	    	      f = t - (side == white ? 8 : -8) + (file - fileto);
         	      piece = (strlen (mvstr) == 3 ? mvstr[2] : ' ');
	    	      return (IsInMoveList (1, f, t, piece));
	    default :
		      printf ("Ambiguous move: %s %s\n",s,mvstr);
		      ShowBoard();
/*
		      getchar();
*/
	    	      return ((leaf *) NULL);
	 }
      }

   }
   else	if ((piece = piece_id(mvstr[0])) != empty &&
            (piece_id(mvstr[1]) == empty))	/* Is a piece move */
   {
      /* Since piece_id accepts P as pawns, this will correctly
       * handle malformed commands like Pe4 */

      b = board.b[side][piece];
      t = -1;
      if (ITO8 (mvstr[1]))				/* N1d2 type move */
      {
         rank = ASCIITORANK (mvstr[1]);
	 b &= RankBit[rank];
	 t = ASCIITOSQ (mvstr[2], mvstr[3]);
      }
      else if (ATOH (mvstr[1]) && ATOH (mvstr[2]))	/* Nbd2 type move */
      {
         file = ASCIITOFILE (mvstr[1]);
	 b &= FileBit[file];
	 t = ASCIITOSQ (mvstr[2], mvstr[3]);
      }
      else if (ATOH (mvstr[1]) && ITO8 (mvstr[2]))	/* Nd2 type move */
      {
	 t = ASCIITOSQ (mvstr[1], mvstr[2]);
      }

      kount = 0;
      n1 = n2 = (leaf *) NULL;
      while (b)
      {
         f = leadz (b);
 	 CLEARBIT (b, f);
	 if ((n1 = IsInMoveList (1, f, t, ' ')) != (leaf *) NULL )
	 {
	    n2 = n1;
	    kount++;
	 }
      }
      if (kount > 1)
      {
	 printf ("Ambiguous move: %s %s\n",s,mvstr);
	 ShowBoard();
/*
	 getchar();
*/
   	 return ((leaf *) NULL);
      }
      else if (kount == 0)
   	 return ((leaf *) NULL);
      else
         return (n2);
   }
   else	if (((piece = piece_id(mvstr[0])) != empty) &&
            ((piece2 = piece_id(mvstr[1])) != empty) &&
	    ( (mvstr[2] == '\0') ||
	      ((piece_id(mvstr[2]) != empty) && mvstr[3] == '\0')))
   { /* KxP format */
      promote = ' ';
      if (piece_id(mvstr[2] != empty)) {
          promote = mvstr[2];
      }
      kount = 0;
      n1 = n2 = (leaf *) NULL;
      b = board.b[side][piece];
      while (b)
      {
         f = leadz (b);
 	 CLEARBIT (b, f);
         b2 = board.b[1^side][piece2];
	 while (b2)
	 {
           t = leadz (b2);
 	   CLEARBIT (b2, t);
	   printf("Trying %s: ", AlgbrMove(MOVE(f,t)));
	   if ((n1 = IsInMoveList (1, f, t, promote)) != (leaf *) NULL)
	   {
	     n2 = n1;
	     kount++;
	     printf("Y  ");
	   }
	   else printf("N  ");
	 }
      }
      if (kount > 1)
      {
	 printf ("Ambiguous move: %s %s\n",s,mvstr);
	 ShowBoard();
/*
	 getchar();
*/
   	 return ((leaf *) NULL);
      }
      else if (kount == 0)
   	 return ((leaf *) NULL);
      else
         return (n2);

   }

   /* Fall through.  Nothing worked, return that no move was performed. */
   return ((leaf *) NULL);
}


leaf * IsInMoveList (int ply, int f, int t, char piece)
/**************************************************************************
 *
 *  Checks to see if from and to square can be found in the movelist
 *  and is legal.
 *
 **************************************************************************/
{
   leaf *node;

   for (node = TreePtr[ply]; node < TreePtr[ply + 1]; node++)
   {
      if ((int) (node->move & 0x0FFF) == MOVE(f,t)  &&
	toupper(piece) == notation[PROMOTEPIECE (node->move)])
         return (node);
   }
   return ((leaf *) NULL);
}


char *AlgbrMove (int move)
/*****************************************************************************
 *
 *  Convert an int move format to algebraic format of g1f3.
 *
 *****************************************************************************/
{
   int f, t;
   static char s[6];

   f = FROMSQ(move);
   t = TOSQ(move);
   strcpy (s, algbr[f]);
   strcpy (s+2, algbr[t]);
   if (move & PROMOTION)
   {
      if (flags & XBOARD)
        s[4] = lnotation[PROMOTEPIECE (move)];
      else
        s[4] = notation[PROMOTEPIECE (move)];
      s[5] = '\0';
   }
   else
      s[4] = '\0';
   return (s);
}

