Chess Bin

Computer Chess Information and Resources

Move Content

In our Chess Engine we will need to describe movement as it occurs.  This will be useful to keep track move history, the best move at each search level or even the result of our Alpha Beta Search.

The Move Content class has 2 major components.  The first component describes the moving chess piece(s). The second component describes the chess piece taken or captured during the move. 

These two components described as 2 structs:

public struct PieceMoving
{
 public byte DstPosition;
 public bool Moved;
 public ChessPieceColor PieceColor;
 public ChessPieceType PieceType;
 public byte SrcPosition;

 public PieceMoving(ChessPieceColor pieceColor, ChessPieceType pieceType, bool moved,
        byte srcPosition, byte dstPosition)
 {
  PieceColor = pieceColor;
  PieceType = pieceType;
  SrcPosition = srcPosition;
  DstPosition = dstPosition;
  Moved = moved;
 }

 public PieceMoving(PieceMoving pieceMoving)
 {
  PieceColor = pieceMoving.PieceColor;
  PieceType = pieceMoving.PieceType;
  SrcPosition = pieceMoving.SrcPosition;
  DstPosition = pieceMoving.DstPosition;
  Moved = pieceMoving.Moved;
 }

 public PieceMoving(ChessPieceType pieceType)
 {
  PieceType = pieceType;
  PieceColor = ChessPieceColor.White;
  SrcPosition = 0;
  DstPosition = 0;
  Moved = false;
 }
}

 

public struct PieceTaken
{
 public bool Moved;
 public ChessPieceColor PieceColor;
 public ChessPieceType PieceType;
 public byte Position;

 public PieceTaken(ChessPieceColor pieceColor, ChessPieceType pieceType, bool moved,
       byte position)
 {
  PieceColor = pieceColor;
  PieceType = pieceType;
  Position = position;
  Moved = moved;
 }

 public PieceTaken(ChessPieceType pieceType)
 {
  PieceColor = ChessPieceColor.White;
  PieceType = pieceType;
  Position = 0;
  Moved = false;
 }
}

The Move Content class itself makes use of the above 2 structs by declaring them into 3 fields:

PieceMoving MovingPiecePrimary – The primary piece that has moved.

PieceMoving MovingPieceSecondary; - The secondary piece that has moved.  This is usually null unless a king has castled.  In this case the primary Piece would be the king and the secondary piece would be the rook.

PieceTaken TakenPiece – The chess piece that was capture or taken during the move.

The other fields like Score, Pawn Promoted and En Passant Occurred are self explanatory.  However the last method ToString requires a bit of an explanation.

The Move Content class will be used to generate Portable Game Notation (PGN) string of the game.  For this reason I overwrote the ToString() method for the class so that it will return a portion of the PGN string for the move that has occurred.

public new string ToString()
{
    string value = "";

    var srcCol = (byte) (MovingPiecePrimary.SrcPosition%8);
    var srcRow = (byte)(8 - (MovingPiecePrimary.SrcPosition / 8));
    var dstCol = (byte) (MovingPiecePrimary.DstPosition%8);
    var dstRow = (byte) (8 - (MovingPiecePrimary.DstPosition/8));

    if (MovingPieceSecondary.PieceType == ChessPieceType.Rook)
    {
        if (MovingPieceSecondary.PieceColor == ChessPieceColor.Black)
        {
            if (MovingPieceSecondary.SrcPosition == 7)
            {
                value += "O-O";
            }
            else if (MovingPieceSecondary.SrcPosition == 0)
            {
                value += "O-O-O";
            }
        }
        else if (MovingPieceSecondary.PieceColor == ChessPieceColor.White)
        {
            if (MovingPieceSecondary.SrcPosition == 63)
            {
                value += "O-O";
            }
            else if (MovingPieceSecondary.SrcPosition == 56)
            {
                value += "O-O-O";
            }
        }
    }
    else
    {
        value += GetPgnMove(MovingPiecePrimary.PieceType);

        switch (MovingPiecePrimary.PieceType)
        {
            case ChessPieceType.Knight:
                value += GetColumnFromInt(srcCol + 1);
                value += srcRow;
                break;
            case ChessPieceType.Rook:
                value += GetColumnFromInt(srcCol + 1);
                value += srcRow;
                break;
            case ChessPieceType.Pawn:
                if (srcCol != dstCol)
                {
                    value += GetColumnFromInt(srcCol + 1);
                }
                break;
        }

        if (TakenPiece.PieceType != ChessPieceType.None)
        {
            value += "x";
        }

        value += GetColumnFromInt(dstCol + 1);

        value += dstRow;

        if (PawnPromoted)
        {
            value += "=Q";
        }
    }

    return value;
}

Notice: Since the Move Content class and all its components will be used to display information outside of the chess engine like the user interface all the Move Content components and the class itself were declared as public. 

The above ToString() method requires a few helper methods that convert objects to strings:

private static string GetColumnFromInt(int column)
{
    switch (column)
    {
        case 1:
            return "a";
        case 2:
            return "b";
        case 3:
            return "c";
        case 4:
            return "d";
        case 5:
            return "e";
        case 6:
            return "f";
        case 7:
            return "g";
        case 8:
            return "h";
        default:
            return "Unknown";
    }
}

private static string GetPgnMove(ChessPieceType pieceType)
{
    switch (pieceType)
    {
        case ChessPieceType.Bishop:
            return "B";

        case ChessPieceType.King:
            return "K";

        case ChessPieceType.Knight:
            return "N";

        case ChessPieceType.Queen:
            return "Q";

        case ChessPieceType.Rook:
            return "R";
        default:
            return "";
    }
}

This concludes the Move Content class. If you want to get started on creating your own chess engine download my C# Chess Game Starter Kit.

Chess Piece Valid Moves

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.

Chess Piece Moves

This post at one point discussed the Chess Piece Motion Class.  I have since then divided the code from this class into two separate classes. 

Piece Moves 
Piece Valid Moves

This post will discuss Piece Moves class.  This class is responsible for providing all available chess piece moves regardless of the state of the chess board.  The information stored in this class will not change throughout the game play so it is static and calculated only once before the game starts.  Having a set of possible moves for any chess piece at any position allows us to later to generate only the valid moves for each chess piece based on the current state of the board. 
 
The Chess Piece Moves listing will contain a Valid Move Set struct.  This struct will be used to store a set of moves available from a single position.


internal struct PieceMoveSet
{
    internal readonly List<byte> Moves;

    internal PieceMoveSet(List<byte> moves)
    {
        Moves = moves;
    }
}


Furthermore we will need some additional array to store all the above move sets for every position on the board.

For example KnightMoves[0]. Moves will return a Knight Moves available from position 0 or A8.  KnightMoves[63] will return all of the possible moves for position 63 or H1.

Some chess pieces can move in a single direction for an undefined number of squares until they reach the end of the board or another chess piece.  For this purpose moves sets for some pieces are divided into several arrays, each describing a move in a certain direction.  This makes it easier to manage these movements in the Chess Piece Valid Moves Class by having the ability to loop through each array until a chess piece or the end of the board is reached and no further.

One other explanation is required around the Total Moves arrays.  Example there is an array called KnightTotalMoves.  This array will hold the number of moves available for every position on the chess board.  This is a performance related addition as it allows me to replace all my foreach loops with regular for loops.  It’s a small performance gain (1%-2%) but they all add up.


internal struct MoveArrays
{
    internal static PieceMoveSet[] BishopMoves1;
    internal static byte[] BishopTotalMoves1;
   
    internal static PieceMoveSet[] BishopMoves2;
    internal static byte[] BishopTotalMoves2;

    internal static PieceMoveSet[] BishopMoves3;
    internal static byte[] BishopTotalMoves3;

    internal static PieceMoveSet[] BishopMoves4;
    internal static byte[] BishopTotalMoves4;

    internal static PieceMoveSet[] BlackPawnMoves;
    internal static byte[] BlackPawnTotalMoves;

    internal static PieceMoveSet[] WhitePawnMoves;
    internal static byte[] WhitePawnTotalMoves;

    internal static PieceMoveSet[] KnightMoves;
    internal static byte[] KnightTotalMoves;

    internal static PieceMoveSet[] QueenMoves1;
    internal static byte[] QueenTotalMoves1;
    internal static PieceMoveSet[] QueenMoves2;
    internal static byte[] QueenTotalMoves2;
    internal static PieceMoveSet[] QueenMoves3;
    internal static byte[] QueenTotalMoves3;
    internal static PieceMoveSet[] QueenMoves4;
    internal static byte[] QueenTotalMoves4;
    internal static PieceMoveSet[] QueenMoves5;
    internal static byte[] QueenTotalMoves5;
    internal static PieceMoveSet[] QueenMoves6;
    internal static byte[] QueenTotalMoves6;
    internal static PieceMoveSet[] QueenMoves7;
    internal static byte[] QueenTotalMoves7;
    internal static PieceMoveSet[] QueenMoves8;
    internal static byte[] QueenTotalMoves8;

    internal static PieceMoveSet[] RookMoves1;
    internal static byte[] RookTotalMoves1;
    internal static PieceMoveSet[] RookMoves2;
    internal static byte[] RookTotalMoves2;
    internal static PieceMoveSet[] RookMoves3;
    internal static byte[] RookTotalMoves3;
    internal static PieceMoveSet[] RookMoves4;
    internal static byte[] RookTotalMoves4;

    internal static PieceMoveSet[] KingMoves;
    internal static byte[] KingTotalMoves;
}


To make use of the above structs we will declare a static class called Piece Moves:

internal static class PieceMoves

To make life a bit easier, we will add a helper method called Position.  This method will accept a chess board column and row, and return a single byte representing the chess board position.  Usually we would not want to use a method like this because it will slow things down.  However this method is only used when the Chess Engine starts when super fast performance is not really all that necessary.


private static byte Position(byte col, byte row)
{
    return (byte)(col + (row * 8));
}

Initiate Chess Piece Motion Class is called only once in the Chess Engine Constructor.  It will construct all of the arrays and call the methods responsible for populating the Move Set arrays will all of the moves for each position on the board for each chess piece.


internal static void InitiateChessPieceMotion()
{
    MoveArrays.WhitePawnMoves = new PieceMoveSet[64];
    MoveArrays.WhitePawnTotalMoves = new byte[64];

    MoveArrays.BlackPawnMoves = new PieceMoveSet[64];
    MoveArrays.BlackPawnTotalMoves = new byte[64];

    MoveArrays.KnightMoves = new PieceMoveSet[64];
    MoveArrays.KnightTotalMoves = new byte[64];

    MoveArrays.BishopMoves1 = new PieceMoveSet[64];
    MoveArrays.BishopTotalMoves1 = new byte[64];

    MoveArrays.BishopMoves2 = new PieceMoveSet[64];
    MoveArrays.BishopTotalMoves2 = new byte[64];

    MoveArrays.BishopMoves3 = new PieceMoveSet[64];
    MoveArrays.BishopTotalMoves3 = new byte[64];

    MoveArrays.BishopMoves4 = new PieceMoveSet[64];
    MoveArrays.BishopTotalMoves4 = new byte[64];

    MoveArrays.RookMoves1 = new PieceMoveSet[64];
    MoveArrays.RookTotalMoves1 = new byte[64];

    MoveArrays.RookMoves2 = new PieceMoveSet[64];
    MoveArrays.RookTotalMoves2 = new byte[64];

    MoveArrays.RookMoves3 = new PieceMoveSet[64];
    MoveArrays.RookTotalMoves3 = new byte[64];

    MoveArrays.RookMoves4 = new PieceMoveSet[64];
    MoveArrays.RookTotalMoves4 = new byte[64];

    MoveArrays.QueenMoves1 = new PieceMoveSet[64];
    MoveArrays.QueenTotalMoves1 = new byte[64];

    MoveArrays.QueenMoves2 = new PieceMoveSet[64];
    MoveArrays.QueenTotalMoves2 = new byte[64];

    MoveArrays.QueenMoves3 = new PieceMoveSet[64];
    MoveArrays.QueenTotalMoves3 = new byte[64];

    MoveArrays.QueenMoves4 = new PieceMoveSet[64];
    MoveArrays.QueenTotalMoves4 = new byte[64];

    MoveArrays.QueenMoves5 = new PieceMoveSet[64];
    MoveArrays.QueenTotalMoves5 = new byte[64];

    MoveArrays.QueenMoves6 = new PieceMoveSet[64];
    MoveArrays.QueenTotalMoves6 = new byte[64];

    MoveArrays.QueenMoves7 = new PieceMoveSet[64];
    MoveArrays.QueenTotalMoves7 = new byte[64];

    MoveArrays.QueenMoves8 = new PieceMoveSet[64];
    MoveArrays.QueenTotalMoves8 = new byte[64];

    MoveArrays.KingMoves = new PieceMoveSet[64];
    MoveArrays.KingTotalMoves = new byte[64];
   
    SetMovesWhitePawn();
    SetMovesBlackPawn();
    SetMovesKnight();
    SetMovesBishop();
    SetMovesRook();
    SetMovesQueen();
    SetMovesKing();
}


Set Moves methods are responsible for populating the Move Arrays with the moves available for each chess piece from a given position.  I am not going to explain this much further.  These methods basically add some predetermined positions to the arrays defined above.  Again performance is not really all that key here, since these methods run only once when the Chess Engine starts.
private static void SetMovesBlackPawn()
{
    for (byte index = 8; index <= 55; index++)
    {
        var moveset = new PieceMoveSet(new List<byte>());
       
        byte x = (byte)(index % 8);
        byte y = (byte)((index / 8));
       
        //Diagonal Kill
        if (y < 7 && x < 7)
        {
            moveset.Moves.Add((byte)(index + 8 + 1));
            MoveArrays.BlackPawnTotalMoves[index]++;
        }
        if (x > 0 && y < 7)
        {
            moveset.Moves.Add((byte)(index + 8 - 1));
            MoveArrays.BlackPawnTotalMoves[index]++;
        }
       
        //One Forward
        moveset.Moves.Add((byte)(index + 8));
        MoveArrays.BlackPawnTotalMoves[index]++;

        //Starting Position we can jump 2
        if (y == 1)
        {
            moveset.Moves.Add((byte)(index + 16));
            MoveArrays.BlackPawnTotalMoves[index]++;
        }

        MoveArrays.BlackPawnMoves[index] = moveset;
    }
}

private static void SetMovesWhitePawn()
{
    for (byte index = 8; index <= 55; index++)
    {
        byte x = (byte)(index % 8);
        byte y = (byte)((index / 8));

        var moveset = new PieceMoveSet(new List<byte>());
      
        //Diagonal Kill
        if (x < 7 && y > 0)
        {
            moveset.Moves.Add((byte)(index - 8 + 1));
            MoveArrays.WhitePawnTotalMoves[index]++;
        }
        if (x > 0 && y > 0)
        {
            moveset.Moves.Add((byte)(index - 8 - 1));
            MoveArrays.WhitePawnTotalMoves[index]++;
        }

        //One Forward
        moveset.Moves.Add((byte)(index - 8));
        MoveArrays.WhitePawnTotalMoves[index]++;

        //Starting Position we can jump 2
        if (y == 6)
        {
            moveset.Moves.Add((byte)(index - 16));
            MoveArrays.WhitePawnTotalMoves[index]++;
        }

        MoveArrays.WhitePawnMoves[index] = moveset;
    }
}

private static void SetMovesKnight()
{
    for (byte y = 0; y < 8; y++)
    {
        for (byte x = 0; x < 8; x++)
        {
            byte index = (byte)(y + (x * 8));

            var moveset = new PieceMoveSet(new List<byte>());
           
            byte move;

            if (y < 6 && x > 0)
            {
                move = Position((byte)(y + 2), (byte)(x - 1));

                if (move < 64)
                {
                    moveset.Moves.Add(move);
                    MoveArrays.KnightTotalMoves[index]++;
                }
            }

            if (y > 1 && x < 7)
            {
                move = Position((byte)(y - 2), (byte)(x + 1));

                if (move < 64)
                {
                    moveset.Moves.Add(move);
                    MoveArrays.KnightTotalMoves[index]++;
                }
            }

            if (y > 1 && x > 0)
            {
                move = Position((byte)(y - 2), (byte)(x - 1));

                if (move < 64)
                {
                    moveset.Moves.Add(move);
                    MoveArrays.KnightTotalMoves[index]++;
                }
            }

            if (y < 6 && x < 7)
            {
                move = Position((byte)(y + 2), (byte)(x + 1));

                if (move < 64)
                {
                    moveset.Moves.Add(move);
                    MoveArrays.KnightTotalMoves[index]++;
                }
            }

            if (y > 0 && x < 6)
            {
                move = Position((byte)(y - 1), (byte)(x + 2));

                if (move < 64)
                {
                    moveset.Moves.Add(move);
                    MoveArrays.KnightTotalMoves[index]++;
                }
            }

            if (y < 7 && x > 1)
            {
                move = Position((byte)(y + 1), (byte)(x - 2));

                if (move < 64)
                {
                    moveset.Moves.Add(move);
                    MoveArrays.KnightTotalMoves[index]++;
                }
            }

            if (y > 0 && x > 1)
            {
                move = Position((byte)(y - 1), (byte)(x - 2));

                if (move < 64)
                {
                    moveset.Moves.Add(move);
                    MoveArrays.KnightTotalMoves[index]++;
                }
            }
           
            if (y < 7 && x < 6)
            {
                move = Position((byte)(y + 1), (byte)(x + 2));

                if (move < 64)
                {
                    moveset.Moves.Add(move);
                    MoveArrays.KnightTotalMoves[index]++;
                }
            }

            MoveArrays.KnightMoves[index] = moveset;
        }
    }
}

private static void SetMovesBishop()
{
    for (byte y = 0; y < 8; y++)
    {
        for (byte x = 0; x < 8; x++)
        {
            byte index = (byte)(y + (x * 8));

            var moveset = new PieceMoveSet(new List<byte>());
            byte move;

            byte row = x;
            byte col = y;

            while (row < 7 && col < 7)
            {
                row++;
                col++;

                move = Position(col, row);
                moveset.Moves.Add(move);
                MoveArrays.BishopTotalMoves1[index]++;
            }

            MoveArrays.BishopMoves1[index] = moveset;
            moveset = new PieceMoveSet(new List<byte>());

            row = x;
            col = y;

            while (row < 7 && col > 0)
            {
                row++;
                col--;

                move = Position(col, row);
                moveset.Moves.Add(move);
                MoveArrays.BishopTotalMoves2[index]++;
            }

            MoveArrays.BishopMoves2[index] = moveset;
            moveset = new PieceMoveSet(new List<byte>());

            row = x;
            col = y;

            while (row > 0 && col < 7)
            {
                row--;
                col++;

                move = Position(col, row);
                moveset.Moves.Add(move);
                MoveArrays.BishopTotalMoves3[index]++;
            }

            MoveArrays.BishopMoves3[index] = moveset;
            moveset = new PieceMoveSet(new List<byte>());

            row = x;
            col = y;

            while (row > 0 && col > 0)
            {
                row--;
                col--;

                move = Position(col, row);
                moveset.Moves.Add(move);
                MoveArrays.BishopTotalMoves4[index]++;
            }

            MoveArrays.BishopMoves4[index] = moveset;
        }
    }
}

private static void SetMovesRook()
{
    for (byte y = 0; y < 8; y++)
    {
        for (byte x = 0; x < 8; x++)
        {
            byte index = (byte)(y + (x * 8));

            var moveset = new PieceMoveSet(new List<byte>());
            byte move;

            byte row = x;
            byte col = y;

            while (row < 7)
            {
                row++;

                move = Position(col, row);
                moveset.Moves.Add(move);
                MoveArrays.RookTotalMoves1[index]++;
            }

            MoveArrays.RookMoves1[index] = moveset;

            moveset = new PieceMoveSet(new List<byte>());
            row = x;
            col = y;

            while (row > 0)
            {
                row--;

                move = Position(col, row);
                moveset.Moves.Add(move);
                MoveArrays.RookTotalMoves2[index]++;
            }

            MoveArrays.RookMoves2[index] = moveset;

            moveset = new PieceMoveSet(new List<byte>());
            row = x;
            col = y;

            while (col > 0)
            {
                col--;

                move = Position(col, row);
                moveset.Moves.Add(move);
                MoveArrays.RookTotalMoves3[index]++;
            }

            MoveArrays.RookMoves3[index] = moveset;

            moveset = new PieceMoveSet(new List<byte>());
            row = x;
            col = y;

            while (col < 7)
            {
                col++;

                move = Position(col, row);
                moveset.Moves.Add(move);
                MoveArrays.RookTotalMoves4[index]++;
            }

            MoveArrays.RookMoves4[index] = moveset;
        }
    }
}

private static void SetMovesQueen()
{
    for (byte y = 0; y < 8; y++)
    {
        for (byte x = 0; x < 8; x++)
        {
            byte index = (byte)(y + (x * 8));

            var moveset = new PieceMoveSet(new List<byte>());
            byte move;

            byte row = x;
            byte col = y;

            while (row < 7)
            {
                row++;

                move = Position(col, row);
                moveset.Moves.Add(move);
                MoveArrays.QueenTotalMoves1[index]++;
            }

            MoveArrays.QueenMoves1[index] = moveset;

            moveset = new PieceMoveSet(new List<byte>());
            row = x;
            col = y;

            while (row > 0)
            {
                row--;

                move = Position(col, row);
                moveset.Moves.Add(move);
                MoveArrays.QueenTotalMoves2[index]++;
            }

            MoveArrays.QueenMoves2[index] = moveset;

            moveset = new PieceMoveSet(new List<byte>());
            row = x;
            col = y;

            while (col > 0)
            {
                col--;

                move = Position(col, row);
                moveset.Moves.Add(move);
                MoveArrays.QueenTotalMoves3[index]++;
            }

            MoveArrays.QueenMoves3[index] = moveset;

            moveset = new PieceMoveSet(new List<byte>());
            row = x;
            col = y;

            while (col < 7)
            {
                col++;

                move = Position(col, row);
                moveset.Moves.Add(move);
                MoveArrays.QueenTotalMoves4[index]++;
            }

            MoveArrays.QueenMoves4[index] = moveset;

            moveset = new PieceMoveSet(new List<byte>());
            row = x;
            col = y;

            while (row < 7 && col < 7)
            {
                row++;
                col++;

                move = Position(col, row);
                moveset.Moves.Add(move);
                MoveArrays.QueenTotalMoves5[index]++;
            }

            MoveArrays.QueenMoves5[index] = moveset;

            moveset = new PieceMoveSet(new List<byte>());
            row = x;
            col = y;

            while (row < 7 && col > 0)
            {
                row++;
                col--;

                move = Position(col, row);
                moveset.Moves.Add(move);
                MoveArrays.QueenTotalMoves6[index]++;
            }

            MoveArrays.QueenMoves6[index] = moveset;

            moveset = new PieceMoveSet(new List<byte>());
            row = x;
            col = y;

            while (row > 0 && col < 7)
            {
                row--;
                col++;

                move = Position(col, row);
                moveset.Moves.Add(move);
                MoveArrays.QueenTotalMoves7[index]++;
            }

            MoveArrays.QueenMoves7[index] = moveset;

            moveset = new PieceMoveSet(new List<byte>());
            row = x;
            col = y;

            while (row > 0 && col > 0)
            {
                row--;
                col--;

                move = Position(col, row);
                moveset.Moves.Add(move);
                MoveArrays.QueenTotalMoves8[index]++;
            }

            MoveArrays.QueenMoves8[index] = moveset;
        }
    }
}

private static void SetMovesKing()
{
    for (byte y = 0; y < 8; y++)
    {
        for (byte x = 0; x < 8; x++)
        {
            byte index = (byte)(y + (x * 8));

            var moveset = new PieceMoveSet(new List<byte>());
            byte move;

            byte row = x;
            byte col = y;

            if (row < 7)
            {
                row++;

                move = Position(col, row);
                moveset.Moves.Add(move);
                MoveArrays.KingTotalMoves[index]++;
            }

            row = x;
            col = y;

            if (row > 0)
            {
                row--;

                move = Position(col, row);
                moveset.Moves.Add(move);
                MoveArrays.KingTotalMoves[index]++;
            }

            row = x;
            col = y;

            if (col > 0)
            {
                col--;

                move = Position(col, row);
                moveset.Moves.Add(move);
                MoveArrays.KingTotalMoves[index]++;
            }

            row = x;
            col = y;

            if (col < 7)
            {
                col++;

                move = Position(col, row);
                moveset.Moves.Add(move);
                MoveArrays.KingTotalMoves[index]++;
            }

            row = x;
            col = y;

            if (row < 7 && col < 7)
            {
                row++;
                col++;

                move = Position(col, row);
                moveset.Moves.Add(move);
                MoveArrays.KingTotalMoves[index]++;
            }

            row = x;
            col = y;

            if (row < 7 && col > 0)
            {
                row++;
                col--;

                move = Position(col, row);
                moveset.Moves.Add(move);
                MoveArrays.KingTotalMoves[index]++;
            }

            row = x;
            col = y;

            if (row > 0 && col < 7)
            {
                row--;
                col++;

                move = Position(col, row);
                moveset.Moves.Add(move);
                MoveArrays.KingTotalMoves[index]++;
            }


            row = x;
            col = y;

            if (row > 0 && col > 0)
            {
                row--;
                col--;

                move = Position(col, row);
                moveset.Moves.Add(move);
                MoveArrays.KingTotalMoves[index]++;
            }

            MoveArrays.KingMoves[index] = moveset;
        }
    }
}


This concludes the Chess Piece Moves class.  To continue reading the chess piece motion series follow this link to the Chess Piece Valid Move class, that will discuss how my chess engine generates valid moves based on the current state of the chess board.

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