/* LORD OF HOSTS - moves.c --- Verarbeitung der Spielzüge */

#include "Lord.h"

extern UBYTE whatsonboard[8][8];
extern UBYTE fval[16][8][8];
extern UBYTE kval[16][8][8];
extern struct Piece ThePieces[16];
extern int status;

struct Move Moves[MAXUNDO];
int undopos,redotop,undobot,last_error;

int NumPieces[2]= { 8,8 };

int check_move(UBYTE board[][8], struct Piece *Pieces, int Num,
               UBYTE tox, UBYTE toy)
{
   int i,result=0, value = Pieces[Num].Value;
   UBYTE fromx = Pieces[Num].x ,fromy = Pieces[Num].y,
         base= (Num<8 ? 0 : 8);
   /* Stimmt die Entfernung ? */
   if (ABS(fromx-tox) + ABS(fromy-toy) < value) result = MUST_MOVE_FARTHER;
   if (ABS(fromx-tox) + ABS(fromy-toy) > value) result = DIST_EXCEEDED;
   /* Sitzt am Ende ein eigener Spielstein ? */
   if (whatsonboard[tox][toy] >= base && whatsonboard[tox][toy] <= base+7)
      result |= CANT_BEAT_BUDDY;
   /* Ist der Weg frei ? */
   result |= how_to_move(board,fromx,fromy,tox,toy);
   /* Gerät durch diesen Zug der eigene King in Gefahr ? */
   if (result == MOVE_X_FIRST || result == MOVE_Y_FIRST)
      result |= check_check(board,Pieces,Num,tox,toy);
   return result;
}

int how_to_move(UBYTE board[][8],
                UBYTE fromx, UBYTE fromy, UBYTE tox, UBYTE toy)
{
   int xdist = tox-fromx, ydist = toy-fromy,
       xstep = 0, ystep = 0, i;
   BOOL freexfirst = TRUE, freeyfirst = TRUE;
   if (xdist) xstep = xdist / ABS(xdist);
   if (ydist) ystep = ydist / ABS(ydist);
   /* Ohne Abbiegen ? */
   if (!ydist)
   {
      for (i=fromx+xstep; i!=tox; i+=xstep)
         if (board[i][fromy] != NOT_OCCUPIED) freexfirst = FALSE;
      if (freexfirst) return MOVE_X_FIRST; else return WAY_BLOCKED;
   }
   if (!xdist)
   {
      for (i=fromy+ystep; i!=toy; i+=ystep)
         if (board[fromx][i] != NOT_OCCUPIED) freeyfirst = FALSE;
      if (freeyfirst) return MOVE_Y_FIRST; else return WAY_BLOCKED;
   }
   /* Mit Abbiegen */
   /* Geht's, wenn wir zuerst in x-Richtung laufen ? */
   for (i=fromx+xstep; i!=tox+xstep; i+=xstep)
      if (board[i][fromy] != NOT_OCCUPIED) freexfirst = FALSE;
   if (freexfirst)
      for (i=fromy+ystep; i!=toy; i+=ystep)
         if (board[tox][i] != NOT_OCCUPIED) freexfirst = FALSE;
   if (!freexfirst) /* Wenn nicht, geht's dann mit y zuerst ? */
   {
      for (i=fromy+ystep; i!=toy+ystep; i+=ystep)
         if (board[fromx][i] != NOT_OCCUPIED) freeyfirst = FALSE;
      if (freeyfirst)
         for (i=fromx+xstep; i!=tox; i+=xstep)
            if (board[i][toy] != NOT_OCCUPIED) freeyfirst = FALSE;
   }

   if (freexfirst) return MOVE_X_FIRST;
   if (freeyfirst) return MOVE_Y_FIRST;
   return WAY_BLOCKED;
}

int check_check(UBYTE board[][8], struct Piece *Pieces, int Num,
               UBYTE tox, UBYTE toy)
{
   UBYTE checkboard[8][8], checkx, checky, fromx, fromy;
   int i,j,result,base = (Num<8 ? 0 : 8), value;
   for (i=0; i<=7; ++i)
      for (j=0; j<=7; ++j)
         checkboard[i][j] = board[i][j];
   checkboard[tox][toy] = Num;
   checkboard[Pieces[Num].x][Pieces[Num].y] = NOT_OCCUPIED;
   if (Num != base)
   {
      checkx = Pieces[base].x;
      checky = Pieces[base].y;
   }
   else
   {
      checkx = tox;
      checky = toy;
   }
   j = 8 - base;
   for (i=j; i<=j+7; ++i)
   {
      fromx = Pieces[i].x;
      fromy = Pieces[i].y;
      value = Pieces[i].Value;
      if (Pieces[i].StillAlive == TRUE)
         if (!(fromx == tox && fromy == toy))
         {
            if (ABS(fromx-checkx) + ABS(fromy-checky) == value)
            {
               result = how_to_move(checkboard,fromx,fromy,checkx,checky);
               if (result == MOVE_X_FIRST || result == MOVE_Y_FIRST)
                  return KING_IN_DANGER;
            }
         }
   }
   return 0;
}

int do_move(UBYTE board[][8], struct Piece *Pieces, int Num,
            UBYTE tox, UBYTE toy, BOOL x_first)
{
   UBYTE fromx = Pieces[Num].x, fromy = Pieces[Num].y,checkvalue;
   int result=0,color,base,p,i,j,r2,beaten;
   SetPiece(Pieces[Num], FALSE, TRUE);/* nicht einblenden, Ausblendfarbe an */
   if (x_first)
   {
      MovePiece(fromx,fromy,tox-fromx,0,TRUE);
      if (toy != fromy) MovePiece(tox,fromy,0,toy-fromy,FALSE);
   }
   else
   {
      MovePiece(fromx,fromy,0,toy-fromy,TRUE);
      if (tox != fromx) MovePiece(fromx,toy,tox-fromx,0,FALSE);
   }
   board[fromx][fromy] = NOT_OCCUPIED;
   beaten = NOBODY;
   if (board[tox][toy] != NOT_OCCUPIED)
   {
      beaten = board[tox][toy];
      result |= ENEMY_KNIGHT_BEATEN;
      Pieces[board[tox][toy]].StillAlive = FALSE;
      color = (Num < 8 ? 1 : 0); /* color = Farbe des Gegners */
      --NumPieces[color];
      if (NumPieces[color] < 3)
         result |= ENEMY_OUT_OF_KNIGHTS;    /* Spiel gewonnen */
   }
   board[tox][toy] = Num;
   Pieces[Num].x = tox;
   Pieces[Num].y = toy;
   Pieces[Num].Value = fval[Num][tox][toy];
   SetPiece(Pieces[Num], TRUE, FALSE);   /* einblenden, keine Ausblendfarbe */
   /* Moves[] für Undo/Redo setzen */
   Moves[undopos].fromx = fromx;
   Moves[undopos].fromy = fromy;
   Moves[undopos].tox   = tox;
   Moves[undopos].toy   = toy;
   Moves[undopos].Beaten= beaten;
   Moves[undopos].x_first = x_first;
   Moves[undopos].status = status | WAITING_FOR_PICK;
   Moves[undopos].status &= (~(WAITING_FOR_DEST|WAITING_FOR_CONF));
   /* undopos : Position im Undo/Redo-Puffer
    * redotop : wenn undopos == redotop, dann ist kein Redo möglich
    * undobot : wenn undopos == undobot, dann ist kein Undo möglich */
   undopos = (undopos+1) % MAXUNDO;
   if (undopos == undobot) undobot = (undobot+1) % MAXUNDO;
   redotop = undopos;
   /* Check, ob Check */
   base = (Num < 8 ? 0 : 8);
   tox = Pieces[8-base].x;
   toy = Pieces[8-base].y;
   for (i=base; i<=base+7; ++i)
   {
      fromx=Pieces[i].x;
      fromy=Pieces[i].y;
      if (Pieces[i].StillAlive == TRUE)
      {
         checkvalue = Pieces[i].Value;
         if (checkvalue == 0) checkvalue =kval[i][Pieces[i].x][Pieces[i].y];
         if (ABS(fromx-tox) + ABS(fromy-toy) == checkvalue)
         {                    /* Pieces[i].Value geht nicht, da er mit
                                 kval[i][x][y] ausgetauscht sein könnte ! */
            r2 = how_to_move(board,fromx,fromy,tox,toy);
            if (r2 == MOVE_X_FIRST || r2 == MOVE_Y_FIRST)
               result |= ENEMY_KING_BEATABLE;
         }
      }
   }
   /* Check, ob Matt */
   if (result & ENEMY_KING_BEATABLE)
   {
      r2=1;
      for (p = 8-base; p<=15-base && r2 > 0; ++p)  /* gegnerische Spielsteine */
      {
         if (Pieces[p].StillAlive)
         {
            for (i=-Pieces[p].Value; i<=Pieces[p].Value && r2 > 0; ++i)
            {
               j=Pieces[p].Value-ABS(i); /* ABS(i)+ABS(j) = Value */
               if (Pieces[p].x+i >=0 && Pieces[p].x+i <=7
                && Pieces[p].y+j >=0 && Pieces[p].y+j <=7)
               {
                  r2 = check_move(board,Pieces,p,Pieces[p].x+i,Pieces[p].y+j);
                  if (r2 == MOVE_X_FIRST || r2 == MOVE_Y_FIRST)
                     r2=-1;
               }
               if (r2 > 0)
               {
                  j=-Pieces[p].Value+ABS(i); /* ABS(i)+ABS(j) = Value */
                  if (Pieces[p].x+i >=0 && Pieces[p].x+i <=7
                   && Pieces[p].y+j >=0 && Pieces[p].y+j <=7)
                  {
                     r2 = check_move(board,Pieces,p,Pieces[p].x+i,Pieces[p].y+j);
                     if (r2 == MOVE_X_FIRST || r2 == MOVE_Y_FIRST)
                        r2=-1;
                  }
               }
            }
         }
      }
      if (r2 != -1)
         result |= ENEMY_MATED;
   }
   if (result & ENEMY_OUT_OF_KNIGHTS || result & ENEMY_MATED)
      Moves[undopos].GameOver = TRUE;
   else
      Moves[undopos].GameOver = FALSE;
   return result;
}

BOOL undo(void)
{
   int Num;
   UBYTE fromx,fromy,tox,toy;
   if (undobot == undopos) return FALSE;  /* konnte Undo nicht durchführen */
   --undopos;
   if (undopos < 0) undopos += MAXUNDO;    /* wraparound buffer */

   fromx = Moves[undopos].tox;   /* Undo vertauscht hin und zurück */
   fromy = Moves[undopos].toy;
   tox   = Moves[undopos].fromx;
   toy   = Moves[undopos].fromy;
   Num = whatsonboard[fromx][fromy];
   SetPiece(ThePieces[Num], FALSE, TRUE);/* nicht einblenden, Ausblendfarbe an */
   if (!Moves[undopos].x_first)
   {
      if (tox != fromx)
      {
         MovePiece(fromx,fromy,tox-fromx,0,TRUE);
         MovePiece(tox,fromy,0,toy-fromy,FALSE);
      }
      else
         MovePiece(fromx,fromy,0,toy-fromy,TRUE);
   }
   else   /* x_first == TRUE */
   {
      if (toy != fromy)
      {
         MovePiece(fromx,fromy,0,toy-fromy,TRUE);
         MovePiece(fromx,toy,tox-fromx,0,FALSE);
      }
      else
         MovePiece(fromx,fromy,tox-fromx,0,TRUE);
   }
   ThePieces[Num].x = tox;
   ThePieces[Num].y = toy;
   ThePieces[Num].Value = fval[Num][tox][toy];
   whatsonboard[tox][toy] = Num;
   whatsonboard[fromx][fromy] = NOT_OCCUPIED;
   SetPiece(ThePieces[Num], TRUE, FALSE); /* einblenden, keine Ausblendfarbe */
   if (Moves[undopos].Beaten != NOBODY)
   {
      ThePieces[Moves[undopos].Beaten].StillAlive = TRUE;  /* Revival */
      SetPiece(ThePieces[Moves[undopos].Beaten], TRUE, FALSE);
      whatsonboard[fromx][fromy] = Moves[undopos].Beaten;
      NumPieces[(Moves[undopos].Beaten > 7)]++;
   }
   return TRUE;
}

BOOL redo(void)
{
   int Num;
   UBYTE fromx,fromy,tox,toy;
   if (redotop == undopos) return FALSE;  /* konnte Redo nicht durchführen */

   fromx = Moves[undopos].fromx;
   fromy = Moves[undopos].fromy;
   tox   = Moves[undopos].tox;
   toy   = Moves[undopos].toy;
   Num = whatsonboard[fromx][fromy];
   SetPiece(ThePieces[Num], FALSE, TRUE);/* nicht einblenden, Ausblendfarbe an */
   if (Moves[undopos].x_first)
   {
      MovePiece(fromx,fromy,tox-fromx,0,TRUE);
      if (toy != fromy) MovePiece(tox,fromy,0,toy-fromy,FALSE);
   }
   else
   {
      MovePiece(fromx,fromy,0,toy-fromy,TRUE);
      if (tox != fromx) MovePiece(fromx,toy,tox-fromx,0,FALSE);
   }
   ThePieces[Num].x = tox;
   ThePieces[Num].y = toy;
   ThePieces[Num].Value = fval[Num][tox][toy];
   whatsonboard[tox][toy] = Num;
   whatsonboard[fromx][fromy] = NOT_OCCUPIED;
   SetPiece(ThePieces[Num], TRUE, FALSE); /* einblenden, keine Ausblendfarbe */
   if (Moves[undopos].Beaten != NOBODY)
   {
      ThePieces[Moves[undopos].Beaten].StillAlive = FALSE;  /* Wieder tot */
      NumPieces[(Moves[undopos].Beaten>7)]--;
   }
   undopos = (undopos+1) % MAXUNDO;       /* wraparound buffer */
   return TRUE;
}

