{****************************************************************************}
{*  MOVES.PAS:  This file contains the routines which generate, move, and   *}
{*  otherwise have to do (on a low level) with moves.                       *}
{****************************************************************************}

{****************************************************************************}
{*  Attacked By:  Given the row and column of a square, this routine will   *}
{*  tally the number of other pieces which attack (enemy pieces) or protect *}
{*  (friendly pieces) the piece on that square.  This is accomplished by    *}
{*  looking around the piece to all other locations from which a piece      *}
{*  could protect or protect.  This routine does not count an attack by     *}
{*  en passent.  This routine is called to see if a king is in check and    *}
{*  as part of the position strength calculation.                           *}
{****************************************************************************}
  procedure AttackedBy (row, col : RowColType; var Attacked, _Protected : integer);
    var dir, i, distance : integer;
        PosRow, PosCol, IncRow, IncCol : RowColType;
        FriendColor, EnemyColor : PieceColorType;
    begin
      {*** initialize ***}
      Attacked := 0;
      _Protected := 0;
      FriendColor := Board[row, col].color;
      if (FriendColor = C_WHITE) then begin
          dir := 1; {*** row displacement from which an enemy pawn would attack ***}
          EnemyColor := C_BLACK;
      end else begin
          dir := -1;
          EnemyColor := C_WHITE;
      end;

      {*** count number of attacking pawns ***}
      with Board[row + dir, col - 1] do
          if (image = PAWN) and (color = EnemyColor) then Attacked := Attacked + 1;
      with Board[row + dir, col + 1] do
          if (image = PAWN) and (color = EnemyColor) then Attacked := Attacked + 1;

      {*** count number of protecting pawns ***}
      with Board[row - dir, col - 1] do
          if (image = PAWN) and (color = FriendColor) then _Protected := _Protected + 1;
      with Board[row - dir, col + 1] do
          if (image = PAWN) and (color = FriendColor) then _Protected := _Protected + 1;

      {*** check for a knight in all positions from which it could attack/protect ***}
      for i := 1 to PossibleMoves[KNIGHT].NumDirections do begin
          with PossibleMoves[KNIGHT].UnitMove[i] do begin
              PosRow := row + DirRow;
              PosCol := col + DirCol;
          end;
          if (Board[PosRow, PosCol].image = KNIGHT) then begin
              {*** color determines if knight is attacking or protecting ***}
              if (Board[PosRow, PosCol].color = FriendColor) then
                  _Protected := _Protected + 1
              else
                  Attacked := Attacked + 1;
          end;
      end;

      {*** check for king, queen, bishop, and rook attacking or protecting ***}
      for i := 1 to 8 do begin
          {*** get the current search direction ***}
          with PossibleMoves[QUEEN].UnitMove[i] do begin
              IncRow := DirRow;
              IncCol := DirCol;
          end;
          {*** set distance countdown and search position ***}
          distance := 7;
          PosRow := row;
          PosCol := col;

          {*** search until something is run into ***}
          while distance > 0 do begin
              {*** get new position and check it ***}
              PosRow := PosRow + IncRow;
              PosCol := PosCol + IncCol;
              with Board[PosRow, PosCol] do begin
                  if ValidSquare then begin
                      if image = BLANK then
                          {*** continue searching if search_square is blank ***}
                          distance := distance - 1
                      else begin
                          {*** separate cases of straight or diagonal attack/protect ***}
                          if (IncRow = 0) or (IncCol = 0) then begin
                              {*** straight: check for queen, rook, or one-away king ***}
                              if (image = QUEEN) or (image = ROOK) or
                                      ((image = KING) and (distance = 7)) then
                                  if (color = FriendColor) then
                                      _Protected := _Protected + 1
                                  else
                                      Attacked := Attacked + 1;
                          end else begin
                              {*** diagonal: check for queen, bishop, or one-away king ***}
                              if (image = QUEEN) or (image = BISHOP) or
                                      ((image = KING) and (distance = 7)) then
                                  if (color = FriendColor) then
                                      _Protected := _Protected + 1
                                  else
                                      Attacked := Attacked + 1;
                          end;
                          {*** force to stop searching if piece encountered ***}
                          distance := 0;
                      end;
                  end else
                      {*** force to stop searching if border of board encountered ***}
                      distance := 0;
              end;
          end;
      end;
    end; {AttackedBy}

{****************************************************************************}
{*  Gen Move List:  Returns the list of all possible moves for the given    *}
{*  player.  Searches the board for all pieces of the given color, and      *}
{*  adds all of the possible moves for that piece to the move list to be    *}
{*  returned.                                                               *}
{****************************************************************************}
  procedure GenMoveList (Turn : PieceColorType; var Movelist : MoveListType);
    var row, col : RowColType;

{----------------------------------------------------------------------------}
{  Gen Piece Move List:  Generates all of the moves for the given piece at   }
{  the given board position, and adds them to the move list for the player.  }
{  If the piece is a pawn then all of the moves are checked by brute force.  }
{  If the piece is a king, then the castling move is checked by brute force. }
{  For all other moves, each allowed direction for the piece is checked, and }
{  each square off in that direction from the piece, up to the piece's move  }
{  distance limit, is added to the list, until the edge of the board is      }
{  encountered or another piece is encountered.  If the piece encountered    }
{  is an enemy piece, then taking that enemy piece is added to the player's  }
{  move list as well.                                                        }
{----------------------------------------------------------------------------}
  procedure GenPieceMoveList (Piece : PieceType; row, col : integer);
    var dir : integer;
        PosRow, PosCol : RowColType;    {*** current scanning position ***}
        OrigRow, OrigCol : RowColType;  {*** location to search from ***}
        IncRow, IncCol : integer;       {*** displacement to add to scanning position ***}
        DirectionNum : 1..8;
        distance : 0..7;
        EPPossible : boolean;
        EnemyColor : PieceColorType;
        Attacked, _Protected : integer;

{----------------------------------------------------------------------------}
{  Gen Add Move:  Adds the move to be generated to the player's move list.   }
{  Given the location and displacement to move the piece, takes PieceTaken   }
{  and PieceMoved from the current board configuration.                      }
{----------------------------------------------------------------------------}
  procedure GenAddMove (row, col : RowColType; drow, dcol : integer);
    begin
      MoveList.NumMoves := MoveList.NumMoves + 1;
      with MoveList.Move[MoveList.NumMoves] do begin
          FromRow := row;
          FromCol := col;
          ToRow := row + drow;
          ToCol := col + dcol;
          PieceTaken := Board [ToRow, ToCol];
          PieceMoved := Board [FromRow, FromCol];
          {*** get image of piece after it is moved... ***}
          {*** same as before the movement except in the case of pawn promotion ***}
          MovedImage := Board [FromRow, FromCol].image;
      end;
    end; {GenAddMove}

{----------------------------------------------------------------------------}
{  Gen Add Pawn Move: Adds the move to be generated for a pawn to the        }
{  player's move list and checks for pawn promotion.                         }
{----------------------------------------------------------------------------}
  procedure GenAddPawnMove (row, col : RowColType; drow, dcol : integer);
    begin
      GenAddMove (row, col, drow, dcol);
      {*** if pawn will move to an end row ***}
      if (row + drow = 1) or (row + drow = 8) then
          {*** assume the pawn will be promoted to a queen ***}
          MoveList.Move[MoveList.NumMoves].MovedImage := QUEEN;
    end; {GenAddPawnMove}

{----------------------------------------------------------------------------}
    begin {GenPieceMoveList}
      OrigRow := row;
      OrigCol := col;

      {*** pawn movement is a special case ***}
      if Piece.image = PAWN then begin
          {*** check for En Passent ***}
          if Piece.color = C_WHITE then begin
              dir := 1;
              EPPossible := (row = 5);
              EnemyColor := C_BLACK;
          end else begin
              dir := -1;
              EPPossible := (row = 4);
              EnemyColor := C_WHITE;
          end;
          {*** make sure enemy's last move was push pawn two, beside player's pawn ***}
          with Player[EnemyColor].LastMove do begin
              if EPPossible and Game.EnPassentAllowed and (FromRow <> NULL_MOVE) then
                  if (abs (FromRow - ToRow) = 2) then
                      if (abs (ToCol - col) = 1) and (PieceMoved.image = PAWN) then
                          GenAddPawnMove (row, col, dir, ToCol - col);
          end;

          {*** square pawn is moving to (1 or 2 ahead) is guaranteed to be valid ***}
          if (Board [row + dir, col].image = BLANK) then begin
              GenAddPawnMove (row, col, dir, 0);
              {*** see if pawn can move two ***}
              if (not Piece.HasMoved) and (Board [row + 2*dir, col].image = BLANK) then
                  GenAddPawnMove (row, col, 2*dir, 0);
          end;

          {*** check for pawn takes to left ***}
          with Board[row + dir, col - 1] do begin
              if (image <> BLANK) and (color = EnemyColor) and ValidSquare then
                  GenAddPawnMove (row, col, dir, -1);
          end;

          {*** check for pawn takes to right ***}
          with Board[row + dir, col + 1] do begin
              if (image <> BLANK) and (color = EnemyColor) and ValidSquare then
                  GenAddPawnMove (row, col, dir, +1);
          end;
      end else begin
          {*** check for the king castling ***}
          if (Piece.image = KING) and (not Piece.HasMoved) and (not Player[Turn].InCheck) then begin
              {*** check for castling to left ***}
              if (Board [row, 1].image = ROOK) and (not Board [row, 1].HasMoved) then
                  if (Board [row, 2].image = BLANK) and (Board [row, 3].image = BLANK)
                            and (Board [row, 4].image = BLANK) then begin
                      {*** make sure not moving through check ***}
                      Board [row, 4].color := Turn;
                      AttackedBy (row, 4, Attacked, _Protected);
                      if (Attacked = 0) then
                          GenAddMove (row, 5, 0, -2);
                  end;

              {*** check for castling to right ***}
              if (Board [row, 8].image = ROOK) and (not Board [row, 8].HasMoved) then
                  if (Board [row, 6].image = BLANK) and (Board [row, 7].image = BLANK) then begin
                      {*** make sure not moving through check ***}
                      Board [row, 6].color := Turn;
                      AttackedBy (row, 6, Attacked, _Protected);
                      if (Attacked = 0) then
                          GenAddMove (row, 5, 0, 2);
                  end;
          end;

          {*** Normal moves: for each allowed direction of the piece... ***}
          for DirectionNum := 1 to PossibleMoves [Piece.image].NumDirections do begin
              {*** initialize search ***}
              distance := PossibleMoves [Piece.image].MaxDistance;
              PosRow := OrigRow;
              PosCol := OrigCol;
              with PossibleMoves[Piece.image].UnitMove[DirectionNum] do begin
                  IncRow := DirRow;
                  IncCol := DirCol;
              end;

              {*** search until something is run into ***}
              while distance > 0 do begin
                  PosRow := PosRow + IncRow;
                  PosCol := PosCol + IncCol;
                  with Board [PosRow, PosCol] do begin
                      if (not ValidSquare) then
                          distance := 0
                      else begin
                          if image = BLANK then begin
                              {*** sqaure is empty: can move there; keep searching ***}
                              GenAddMove (OrigRow, OrigCol, PosRow - OrigRow, PosCol - OrigCol);
                              distance := distance - 1;
                          end else begin
                              {*** piece is there: can take if enemy; stop searching ***}
                              distance := 0;
                              if color <> Turn then
                                  GenAddMove (OrigRow, OrigCol, PosRow - OrigRow, PosCol - OrigCol);
                          end;
                      end;
                  end;
              end;
          end;
      end;
    end; {GenPieceMoveList}

{----------------------------------------------------------------------------}
    begin {GenMoveList}
      {*** empty out player's move list ***}
      MoveList.NumMoves := 0;

      {*** search for player's pieces on board ***}
      for row := 1 to BOARD_SIZE do
          for col := 1 to BOARD_SIZE do
              with Board [row, col] do begin
                  {*** if player's piece, add its moves ***}
                  if (image <> BLANK) and (color = Turn) then
                      GenPieceMoveList (Board [row, col], row, col);
              end;
    end; {GenMoveList}

{****************************************************************************}
{*  Make Move:  Update the Board to reflect making the given movement.  If  *}
{*  given is a PermanentMove, then the end of game pointers is set to the   *}
{*  current move.  Returns the score for the move, which is given by the    *}
{*  number of points for the piece taken.                                   *}
{****************************************************************************}
  procedure MakeMove (Movement : MoveType; PermanentMove : boolean; var Score : integer);
    var Row: RowColType;
        Attacked, _Protected : integer;

  begin
    {$ifdef KCCHESS_VERBOSE}
    //WriteLn(Format('[Moves.pas.MakeMove] Movement.PieceTaken.image=%d', [Integer(Movement.PieceTaken.image)]));
    {$endif}

      Score := 0;
      {*** update board for most moves ***}
      with Movement do begin
          {*** pick piece up ***}
          Board[FromRow, FromCol].image := BLANK;
          {*** put piece down ***}
          Board[ToRow, ToCol] := PieceMoved;
      end;

      {*** check for en passent, pawn promotion, and castling ***}
      case Movement.PieceMoved.image of
          PAWN: begin
              {*** en passent: blank out square taken; get points ***}
              if (Movement.FromCol <> Movement.ToCol) and (Movement.PieceTaken.image = BLANK) then begin
                  Board[Movement.FromRow, Movement.ToCol].image := BLANK;
                  Score := Score + CapturePoints[PAWN];
              end;

              {*** pawn promotion: use the after-moved image; get trade-up points ***}
              if Movement.PieceMoved.color = C_WHITE then Row := 8 else Row := 1;
              if Movement.ToRow = Row then begin
                  Board[Movement.ToRow, Movement.ToCol].image := Movement.MovedImage;
                  Score := Score + CapturePoints[Movement.MovedImage] - CapturePoints[PAWN];
              end;
          end;

          KING: begin
              {*** update king position in player record ***}
              with Player[Movement.PieceMoved.color] do begin
                  KingRow := Movement.ToRow;
                  KingCol := Movement.ToCol;
              end;

              {*** castling left/right: move the rook too ***}
              if abs (Movement.FromCol - Movement.ToCol) > 1 then begin
                  if (Movement.ToCol < Movement.FromCol) then begin
                      Board[Movement.FromRow, 4] := Board[Movement.FromRow, 1];
                      Board[Movement.FromRow, 4].HasMoved := true;
                      Board[Movement.FromRow, 1].image := BLANK;
                  end else begin
                      Board[Movement.FromRow, 6] := Board[Movement.FromRow, 8];
                      Board[Movement.FromRow, 6].HasMoved := true;
                      Board[Movement.FromRow, 8].image := BLANK;
                  end;
              end;
          end;
      end;

      {*** update player attributes ***}
      Player[Movement.PieceMoved.color].LastMove := Movement;
      Player[Movement.PieceMoved.color].InCheck := false;
      with Player[EnemyColor[Movement.PieceMoved.color]] do begin
          AttackedBy (KingRow, KingCol, Attacked, _Protected);
          InCheck := Attacked <> 0;
      end;

      {*** remember that piece has been moved, get score ***}
      Board[Movement.ToRow, Movement.ToCol].HasMoved := true;
      Score := Score + CapturePoints[Movement.PieceTaken.image];

      {*** update game attributes ***}
      Game.MoveNum := Game.MoveNum + 1;
      Game.MovesPointer := Game.MovesPointer + 1;
      Game.Move[Game.MovesPointer] := Movement;
      if PermanentMove then
          Game.MovesStored := Game.MovesPointer;
      Game.InCheck[Game.MovesPointer] := Attacked <> 0;

      {*** update nondevelopmental moves counter ***}
      if (Movement.PieceMoved.image = PAWN) or (Movement.PieceTaken.image <> BLANK) then begin
          Game.NonDevMoveCount[Game.MovesPointer] := 0;
      end else begin
          {*** if 50 nondevelopmental moves in a row: stalemate ***}
          Game.NonDevMoveCount[Game.MovesPointer] := Game.NonDevMoveCount[Game.MovesPointer-1] + 1;
          if (Game.NonDevMoveCount[Game.MovesPointer] >= NON_DEV_MOVE_LIMIT) then
              Score := STALE_SCORE;
      end;
    end; {MakeMove}

{****************************************************************************}
{*  Un Make Move:  Updates the board for taking back the last made move.    *}
{*  This routine returns (is not given) the last made movement (so it may   *}
{*  be passed to DisplayUnMadeMove).                                        *}
{****************************************************************************}
  procedure UnMakeMove (var Movement: Movetype);
    begin
      {*** make sure there is a move to un-make ***}
      if (Game.MovesPointer > 0) then begin
          Movement := Game.Move[Game.MovesPointer];
          {*** restore whether player who made move was in check ***}
          Player[Movement.PieceMoved.color].InCheck := Game.InCheck[Game.MovesPointer - 1];
          {*** the enemy could not have been in check ***}
          Player[EnemyColor[Movement.PieceMoved.color]].InCheck := false;
          {*** restore the From and To squares ***}
          Board[Movement.FromRow, Movement.FromCol] := Movement.PieceMoved;
          Board[Movement.ToRow, Movement.ToCol] := Movement.PieceTaken;

          {*** special cases ***}
          case Movement.PieceMoved.image of
              KING: begin
                  {*** restore position of king in player attributes ***}
                  with Player[Movement.PieceMoved.color] do begin
                      KingRow := Movement.FromRow;
                      KingCol := Movement.FromCol;
                  end;
                  {*** un-castle left/right if applicable ***}
                  if abs (Movement.FromCol - Movement.ToCol) > 1 then begin
                      if (Movement.FromCol < Movement.ToCol) then begin
                          Board[Movement.FromRow, 8] := Board[Movement.FromRow, 6];
                          Board[Movement.FromRow, 6].image := BLANK;
                          Board[Movement.FromRow, 8].HasMoved := false;
                      end else begin
                          Board[Movement.FromRow, 1] := Board[Movement.FromRow, 4];
                          Board[Movement.FromRow, 4].image := BLANK;
                          Board[Movement.FromRow, 1].HasMoved := false;
                      end;
                  end;
              end;

              PAWN:
                  {*** un-en passent: restore the pawn that was taken ***}
                  if (Movement.FromCol <> Movement.ToCol) and (Movement.PieceTaken.image = BLANK) then
                      with Board[Movement.FromRow, Movement.ToCol] do begin
                          image := PAWN;
                          if Movement.PieceMoved.color = C_WHITE then
                              color := C_BLACK
                          else
                              color := C_WHITE;
                          HasMoved := true;
                      end;
          end;

          {*** roll back move number and pointer ***}
          Game.MoveNum := Game.MoveNum - 1;
          Game.MovesPointer := Game.MovesPointer - 1;

          {*** restore player previous move attributes ***}
          if (Game.MovesPointer > 1) then
              Player[Movement.PieceMoved.color].LastMove := Game.Move[Game.MovesPointer - 1]
          else
              Player[Movement.PieceMoved.color].LastMove.FromRow := NULL_MOVE;
      end else
          {*** indicate no move to take back ***}
          Movement.FromRow := NULL_MOVE;
    end; {UnMakeMove}

{****************************************************************************}
{*  Trim Checks:  Given a move list, remove all of the moves which would    *}
{*  result in the given player moving into check.  This is not done         *}
{*  directly by GenMoveList because it is an expensive operation, since     *}
{*  each move will have to be made and then unmade.  This routine is called *}
{*  by the Human Movement routine.  The Computer Movement routine           *}
{*  incorporates this code into its code, since the moves have to be made   *}
{*  and unmade there anyway.                                                *}
{****************************************************************************}
  procedure TrimChecks (Turn : PieceColorType; var MoveList : MoveListType);
    var DummyMove : MoveType;
        DummyScore, Attacked, _Protected : integer;
        ValidMoves, i : integer;

    begin
      ValidMoves := 0;
      for i := 1 to MoveList.NumMoves do begin
          MakeMove (MoveList.Move[i], false, DummyScore);
          {*** check if the king is attacked (in check) ***}
          AttackedBy (Player[Turn].KingRow, Player[Turn].KingCol, Attacked, _Protected);
          if Attacked = 0 then begin
              {*** if not in check, then re-put valid move ***}
              ValidMoves := ValidMoves + 1;
              MoveList.Move[ValidMoves] := MoveList.Move[i];
          end;
          UnMakeMove (DummyMove);
      end;
      MoveList.NumMoves := ValidMoves;
    end; {TrimChecks}

{****************************************************************************}
{*  Randomize Move List:  Scrambles the order of the given move list.  This *}
{*  is done to make choosing between tie scores random.  It is necassary    *}
{*  to scramble the moves here because the move list will always be         *}
{*  generated in the same order.                                            *}
{****************************************************************************}
  procedure RandomizeMoveList (var MoveList : MoveListType);
    var i, Exch : integer;
        ExchMove : MoveType;
    begin
      for i := 1 to MoveList.NumMoves do begin
          {*** swap the current move with some random other one ***}
          Exch := Random (MoveList.NumMoves) + 1;
          ExchMove := MoveList.Move[Exch];
          MoveList.Move[Exch] := MoveList.Move[i];
          MoveList.Move[i] := ExchMove;
      end;
    end; {RandomizeMoveList}

{****************************************************************************}
{*  Goto Move:  Rolls back/forth the game to the given move number.  Each   *}
{*  move between the current move and the destination is made/retracted to  *}
{*  update the board and other game status information.  If the given move  *}
{*  number is too small or too large, the game is rolled to the first/last  *}
{*  move.                                                                   *}
{****************************************************************************}
  procedure GotoMove (GivenMoveTo : integer);
    var Movement : MoveType;
        DummyScore : integer;
        MoveTo, BeginMoveNum, EndMoveNum : integer;
    begin
      {*** check the bounds of given move number ***}
      MoveTo := GivenMoveTo;
      BeginMoveNum := Game.MoveNum - Game.MovesPointer;
      EndMoveNum := Game.MoveNum + (Game.MovesStored - Game.MovesPointer);
      if (MoveTo < BeginMoveNum) then MoveTo := BeginMoveNum;
      if (MoveTo > EndMoveNum) then MoveTo := EndMoveNum;

      {*** make / retract moves of the game until the destination is reached ***}
      while (Game.MoveNum <> MoveTo) do begin
          if MoveTo > Game.MoveNum then begin
              Movement := Game.Move[Game.MovesPointer + 1];
              MakeMove (Movement, false, DummyScore);
          end else
              UnMakeMove (Movement);
      end;
      Game.GameFinished := false;
    end;

{*** end of the MOVES.PAS include file ***}

