Chess Piece Valid Moves

by aberent 10. September 2008 12:31

Originally the code in this post was part of the Chess Piece Motion class.  However since I posted the ordinal code I have divided that class into 2 separate classes.  Chess Piece Moves and Chess Piece Valid moves which is discussed here.

This class will be responsible for dynamically figuring out only the valid moves for the current chess board and assigning only the valid moves to each chess piece.  This class will also figure out what pieces are attacking each other, is the king in check, has en passant occurred and assign the information to each piece for the purpose of score evaluation.

The Chess Piece Valid Moves class will be declared as follows:


internal static class PieceValidMoves

To help us understand what is board squares are being attacked we will defina 2 arrays.  One for storing board squares attacked by White, and one for Black.  These arrays are crucial in figuring out things like valid moves for a king, since kings cannot move onto a square that is currently attacked by an opponent.


internal static bool[] BlackAttackBoard;
internal static bool[] WhiteAttackBoard;

Furthermore we can't correctly check for the kings valid moves until we examine all other chess pieces.  This is due to the fact that we won't know if the chess board square is attacked if we don't look at every single chess piece first.  For this reason when we come across a king during our analysis, we don't analyze its possible moves but rather store its position for later analysis.  The following 2 variables are used to store that information.


private static byte BlackKingPosition;
private static byte WhiteKingPosition;

The Analyze Move method will perform a deep analysis or examination of the move itself and its effect on the chess board.  For example this method will record an En Passant scenario as well as recording Checks and Kills.  The Analyze Move method will return true if the move is considered not blocked (not resulting in a kill or blocked by another chess piece of the same color).  Similarly it will return false if the move is blocked and movement cannot continue past this position.  This is very important since this return value will be used to end a loop of moves in a certain direction.  This method is called for all chess pieces other than pawns and kings.

private static bool AnalyzeMove(Board board, byte dstPos, Piece pcMoving)
{
    //If I am not a pawn everywhere I move I can attack
    if (pcMoving.PieceColor == ChessPieceColor.White)
    {
        WhiteAttackBoard[dstPos] = true;
    }
    else
    {
        BlackAttackBoard[dstPos] = true;
    }

    //If there no piece there I can potentialy kill just add the move and exit
    if (board.Squares[dstPos].Piece == null)
    {
        pcMoving.ValidMoves.Push(dstPos);

        return true;
    }

    Piece pcAttacked = board.Squares[dstPos].Piece;

    //if that piece is a different color
    if (pcAttacked.PieceColor != pcMoving.PieceColor)
    {
        pcAttacked.AttackedValue += pcMoving.PieceActionValue;

        //If this is a king set it in check                  
        if (pcAttacked.PieceType == ChessPieceType.King)
        {
            if (pcAttacked.PieceColor == ChessPieceColor.Black)
            {
                board.BlackCheck = true;
            }
            else
            {
                board.WhiteCheck = true;
            }
        }
        else
        {
            //Add this as a valid move
            pcMoving.ValidMoves.Push(dstPos);
        }


        //We don't continue movement past this piece
        return false;
    }
    //Same Color I am defending
    pcAttacked.DefendedValue += pcMoving.PieceActionValue;

    //Since this piece is of my kind I can't move there
    return false;
}

Pawns behave slightly differently then regular pieces in that not all of their moves can result in a kill.  A move straight ahead cannon result in a kill while a diagonal move can.  For this reason there are two separate methods to analyze pawn moves.   One Parent that loops through all available pawn moves and one child that analyzes each move at a time.

Parent:

private static void CheckValidMovesPawn(List<byte> moves, Piece pcMoving,
          byte srcPosition,
          Board board, byte count)
{
 for (byte i = 0; i < count; i++)
 {
  byte dstPos = moves[i];

  if (dstPos%8 != srcPosition%8)
  {
   //If there is a piece there I can potentialy kill
   AnalyzeMovePawn(board, dstPos, pcMoving);

   if (pcMoving.PieceColor == ChessPieceColor.White)
   {
    WhiteAttackBoard[dstPos] = true;
   }
   else
   {
    BlackAttackBoard[dstPos] = true;
   }
  }
   // if there is something if front pawns can't move there
  else if (board.Squares[dstPos].Piece != null)
  {
   return;
  }
   //if there is nothing in front of me (blocked == false)
  else
  {
   pcMoving.ValidMoves.Push(dstPos);
  }
 }
}

Child:

private static void AnalyzeMovePawn(Board board, byte dstPos, Piece pcMoving)
{
    //Because Pawns only kill diagonaly we handle the En Passant scenario specialy
    if (board.EnPassantPosition > 0)
    {
        if (pcMoving.PieceColor != board.EnPassantColor)
        {
            if (board.EnPassantPosition == dstPos)
            {
                //We have an En Passant Possible
                pcMoving.ValidMoves.Push(dstPos);

                if (pcMoving.PieceColor == ChessPieceColor.White)
                {
                    WhiteAttackBoard[dstPos] = true;
                }
                else
                {
                    BlackAttackBoard[dstPos] = true;
                }
            }
        }
    }

    Piece pcAttacked = board.Squares[dstPos].Piece;

    //If there no piece there I can potentialy kill
    if (pcAttacked == null)
        return;

    //Regardless of what is there I am attacking this square
    if (pcMoving.PieceColor == ChessPieceColor.White)
    {
        WhiteAttackBoard[dstPos] = true;

        //if that piece is the same color
        if (pcAttacked.PieceColor == pcMoving.PieceColor)
        {
            pcAttacked.DefendedValue += pcMoving.PieceActionValue;
            return;
        }
        //else piece is different so we are attacking
        pcAttacked.AttackedValue += pcMoving.PieceActionValue;

        //If this is a king set it in check                  
        if (pcAttacked.PieceType == ChessPieceType.King)
        {
            board.BlackCheck = true;
        }
        else
        {
            //Add this as a valid move
            pcMoving.ValidMoves.Push(dstPos);
        }
    }
    else
    {
        BlackAttackBoard[dstPos] = true;

        //if that piece is the same color
        if (pcAttacked.PieceColor == pcMoving.PieceColor)
        {
            return;
        }

        //If this is a king set it in check                  
        if (pcAttacked.PieceType == ChessPieceType.King)
        {
            board.WhiteCheck = true;
        }
        else
        {
            //Add this as a valid move
            pcMoving.ValidMoves.Push(dstPos);
        }
    }

    return;
}

Check Valid Moves King Castle Method handles the complicated castling scenarios by examining the chess board squares between the king and the rook to make sure they are empty, not attacked and that the rook and king are both in their starting positions.

private static void GenerateValidMovesKingCastle(Board board, Piece king)
{
 if (king == null)
 {
  return;
 }

 if (king.Moved)
 {
  return;
 }
 if (king.PieceColor == ChessPieceColor.White &&
  board.WhiteCastled)
 {
  return;
 }
 if (king.PieceColor == ChessPieceColor.Black &&
  board.BlackCastled)
 {
  return;
 }
 if (king.PieceColor == ChessPieceColor.Black &&
  board.BlackCheck)
 {
  return;
 }
 if (king.PieceColor == ChessPieceColor.White &&
  board.WhiteCheck)
 {
  return;
 }


 //This code will add the castleling move to the pieces available moves
 if (king.PieceColor == ChessPieceColor.White)
 {
  if (board.WhiteCheck)
  {
   return;
  }

  if (board.Squares[63].Piece != null)
  {
   //Check if the Right Rook is still in the correct position
   if (board.Squares[63].Piece.PieceType == ChessPieceType.Rook)
   {
    if (board.Squares[63].Piece.PieceColor == king.PieceColor)
    {
     //Move one column to right see if its empty
     if (board.Squares[62].Piece == null)
     {
      if (board.Squares[61].Piece == null)
      {
       if (BlackAttackBoard[61] == false &&
        BlackAttackBoard[62] == false)
       {
        //Ok looks like move is valid lets add it
        king.ValidMoves.Push(62);
        WhiteAttackBoard[62] = true;
       }
      }
     }
    }
   }
  }

  if (board.Squares[56].Piece != null)
  {
   //Check if the Left Rook is still in the correct position
   if (board.Squares[56].Piece.PieceType == ChessPieceType.Rook)
   {
    if (board.Squares[56].Piece.PieceColor == king.PieceColor)
    {
     //Move one column to right see if its empty
     if (board.Squares[57].Piece == null)
     {
      if (board.Squares[58].Piece == null)
      {
       if (board.Squares[59].Piece == null)
       {
        if (BlackAttackBoard[58] == false &&
         BlackAttackBoard[59] == false)
        {
         //Ok looks like move is valid lets add it
         king.ValidMoves.Push(58);
         WhiteAttackBoard[58] = true;
        }
       }
      }
     }
    }
   }
  }
 }
 else if (king.PieceColor == ChessPieceColor.Black)
 {
  if (board.BlackCheck)
  {
   return;
  }

  //There are two ways to castle, scenario 1:
  if (board.Squares[7].Piece != null)
  {
   //Check if the Right Rook is still in the correct position
   if (board.Squares[7].Piece.PieceType == ChessPieceType.Rook
    && !board.Squares[7].Piece.Moved)
   {
    if (board.Squares[7].Piece.PieceColor == king.PieceColor)
    {
     //Move one column to right see if its empty

     if (board.Squares[6].Piece == null)
     {
      if (board.Squares[5].Piece == null)
      {
       if (WhiteAttackBoard[5] == false && WhiteAttackBoard[6] == false)
       {
        //Ok looks like move is valid lets add it
        king.ValidMoves.Push(6);
        BlackAttackBoard[6] = true;
       }
      }
     }
    }
   }
  }
  //There are two ways to castle, scenario 2:
  if (board.Squares[0].Piece != null)
  {
   //Check if the Left Rook is still in the correct position
   if (board.Squares[0].Piece.PieceType == ChessPieceType.Rook &&
    !board.Squares[0].Piece.Moved)
   {
    if (board.Squares[0].Piece.PieceColor ==
     king.PieceColor)
    {
     //Move one column to right see if its empty
     if (board.Squares[1].Piece == null)
     {
      if (board.Squares[2].Piece == null)
      {
       if (board.Squares[3].Piece == null)
       {
        if (WhiteAttackBoard[2] == false &&
         WhiteAttackBoard[3] == false)
        {
         //Ok looks like move is valid lets add it
         king.ValidMoves.Push(2);
         BlackAttackBoard[2] = true;
        }
       }
      }
     }
    }
   }
  }
 }
}

The last method in this class is the only non private method in this listing.  The Generate Valid Moves method will be called directly by the chess engine to create valid moves for each chess board it examines.  This method will loop through all chess pieces on the chess board and examine each possible move based on the chess piece’s position.  Please note that we have already calculated each pieces move in the Chess Piece Moves class for every single position on the chess board.  We now just have to figure out which one of those moves is valid and what their results will be.  While we are looping through all of the chess pieces we also collect some information such as how many pawns are in each file, how many pawns are isolated.  At the end of this method we will sum up this information and store it on the Chess Board.

internal static void GenerateValidMoves(Board board)
{
 // Reset Board
 board.BlackCheck = false;
 board.WhiteCheck = false;

 WhiteAttackBoard = new bool[64];
 BlackAttackBoard = new bool[64];

 //Generate Moves
 for (byte x = 0; x < 64; x++)
 {
  Square sqr = board.Squares[x];

  if (sqr.Piece == null)
   continue;

  sqr.Piece.ValidMoves = new Stack<byte>(sqr.Piece.LastValidMoveCount);

  switch (sqr.Piece.PieceType)
  {
   case ChessPieceType.Pawn:
    {
     if (sqr.Piece.PieceColor == ChessPieceColor.White)
     {
      CheckValidMovesPawn(MoveArrays.WhitePawnMoves[x].Moves, sqr.Piece, x,
           board,
           MoveArrays.WhitePawnTotalMoves[x]);
      break;
     }
     if (sqr.Piece.PieceColor == ChessPieceColor.Black)
     {
      CheckValidMovesPawn(MoveArrays.BlackPawnMoves[x].Moves, sqr.Piece, x,
           board,
           MoveArrays.BlackPawnTotalMoves[x]);
      break;
     }

     break;
    }
   case ChessPieceType.Knight:
    {
     for (byte i = 0; i < MoveArrays.KnightTotalMoves[x]; i++)
     {
      AnalyzeMove(board, MoveArrays.KnightMoves[x].Moves[i], sqr.Piece);
     }

     break;
    }
   case ChessPieceType.Bishop:
    {
     for (byte i = 0; i < MoveArrays.BishopTotalMoves1[x]; i++)
     {
      if (
       AnalyzeMove(board, MoveArrays.BishopMoves1[x].Moves[i],
          sqr.Piece) ==
       false)
      {
       break;
      }
     }
     for (byte i = 0; i < MoveArrays.BishopTotalMoves2[x]; i++)
     {
      if (
       AnalyzeMove(board, MoveArrays.BishopMoves2[x].Moves[i],
          sqr.Piece) ==
       false)
      {
       break;
      }
     }
     for (byte i = 0; i < MoveArrays.BishopTotalMoves3[x]; i++)
     {
      if (
       AnalyzeMove(board, MoveArrays.BishopMoves3[x].Moves[i],
          sqr.Piece) ==
       false)
      {
       break;
      }
     }
     for (byte i = 0; i < MoveArrays.BishopTotalMoves4[x]; i++)
     {
      if (
       AnalyzeMove(board, MoveArrays.BishopMoves4[x].Moves[i],
          sqr.Piece) ==
       false)
      {
       break;
      }
     }

     break;
    }
   case ChessPieceType.Rook:
    {
     for (byte i = 0; i < MoveArrays.RookTotalMoves1[x]; i++)
     {
      if (
       AnalyzeMove(board, MoveArrays.RookMoves1[x].Moves[i], sqr.Piece) ==
       false)
      {
       break;
      }
     }
     for (byte i = 0; i < MoveArrays.RookTotalMoves2[x]; i++)
     {
      if (
       AnalyzeMove(board, MoveArrays.RookMoves2[x].Moves[i], sqr.Piece) ==
       false)
      {
       break;
      }
     }
     for (byte i = 0; i < MoveArrays.RookTotalMoves3[x]; i++)
     {
      if (
       AnalyzeMove(board, MoveArrays.RookMoves3[x].Moves[i], sqr.Piece) ==
       false)
      {
       break;
      }
     }
     for (byte i = 0; i < MoveArrays.RookTotalMoves4[x]; i++)
     {
      if (
       AnalyzeMove(board, MoveArrays.RookMoves4[x].Moves[i], sqr.Piece) ==
       false)
      {
       break;
      }
     }

     break;
    }
   case ChessPieceType.Queen:
    {
     for (byte i = 0; i < MoveArrays.QueenTotalMoves1[x]; i++)
     {
      if (
       AnalyzeMove(board, MoveArrays.QueenMoves1[x].Moves[i], sqr.Piece) ==
       false)
      {
       break;
      }
     }
     for (byte i = 0; i < MoveArrays.QueenTotalMoves2[x]; i++)
     {
      if (
       AnalyzeMove(board, MoveArrays.QueenMoves2[x].Moves[i], sqr.Piece) ==
       false)
      {
       break;
      }
     }
     for (byte i = 0; i < MoveArrays.QueenTotalMoves3[x]; i++)
     {
      if (
       AnalyzeMove(board, MoveArrays.QueenMoves3[x].Moves[i], sqr.Piece) ==
       false)
      {
       break;
      }
     }
     for (byte i = 0; i < MoveArrays.QueenTotalMoves4[x]; i++)
     {
      if (
       AnalyzeMove(board, MoveArrays.QueenMoves4[x].Moves[i], sqr.Piece) ==
       false)
      {
       break;
      }
     }

     for (byte i = 0; i < MoveArrays.QueenTotalMoves5[x]; i++)
     {
      if (
       AnalyzeMove(board, MoveArrays.QueenMoves5[x].Moves[i], sqr.Piece) ==
       false)
      {
       break;
      }
     }
     for (byte i = 0; i < MoveArrays.QueenTotalMoves6[x]; i++)
     {
      if (
       AnalyzeMove(board, MoveArrays.QueenMoves6[x].Moves[i], sqr.Piece) ==
       false)
      {
       break;
      }
     }
     for (byte i = 0; i < MoveArrays.QueenTotalMoves7[x]; i++)
     {
      if (
       AnalyzeMove(board, MoveArrays.QueenMoves7[x].Moves[i], sqr.Piece) ==
       false)
      {
       break;
      }
     }
     for (byte i = 0; i < MoveArrays.QueenTotalMoves8[x]; i++)
     {
      if (
       AnalyzeMove(board, MoveArrays.QueenMoves8[x].Moves[i], sqr.Piece) ==
       false)
      {
       break;
      }
     }

     break;
    }
   case ChessPieceType.King:
    {
     if (sqr.Piece.PieceColor == ChessPieceColor.White)
     {
      WhiteKingPosition = x;
     }
     else
     {
      BlackKingPosition = x;
     }

     break;
    }
  }
 }


 if (board.WhoseMove == ChessPieceColor.White)
 {
  GenerateValidMovesKing(board.Squares[BlackKingPosition].Piece, board,
          BlackKingPosition);
  GenerateValidMovesKing(board.Squares[WhiteKingPosition].Piece, board,
          WhiteKingPosition);
 }
 else
 {
  GenerateValidMovesKing(board.Squares[WhiteKingPosition].Piece, board,
          WhiteKingPosition);
  GenerateValidMovesKing(board.Squares[BlackKingPosition].Piece, board,
          BlackKingPosition);
 }


 //Now that all the pieces were examined we know if the king is in check
 GenerateValidMovesKingCastle(board, board.Squares[WhiteKingPosition].Piece);
 GenerateValidMovesKingCastle(board, board.Squares[BlackKingPosition].Piece);
}

This concludes the Chess Piece Valid Moves class. 

If you want to get started on creating your own chess engine download my C# Chess Game Starter Kit.