Originally the code in this post was part of the Chess Piece Motion class. However since I posted the original 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 30 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 define 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 than 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.
Want to skip to the end, download the full chess engine source code on GitHub