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.

Want to skip to the end, download the full chess engine source code on GitHub