Completing the chess engine

Now that we have all the necessary parts for keeping track of our chess pieces, generating valid moves and searching for the best computer move, we are ready to put it all together and complete our chess engine.  We have already started to discuss the chess engine class in the previous post titled: Starting the Chess Engine.  Just to recap that post we have already:

Declared the class as:

public sealed class Engine

Declared its internal members representing our chess board and the previous chess board as well as variables representing whose move it is.

internal Board ChessBoard;
internal Board PreviousChessBoard;
public ChessPieceColor WhoseMove
{
    get { return ChessBoard.WhoseMove; }
    set { ChessBoard.WhoseMove = value; }
}

Declared a constructor that will initiate the chess board, move history, pre-calculate all possible moves from all positions, register starting positions of a new chess game and calculate all valid moves from that position.

Since we now have discussed the Chess Board Evaluation class I will modify this listing slightly to evaluate the board score as its last operation.

public Engine()
{
    ChessBoard = new Board();
    MoveHistory = new Stack<MoveContent>();
    RegisterStartingBoard();
    ChessBoard.WhoseMove = ChessPieceColor.White;    
    
    ChessPieceMoves.InitiateChessPieceMotion(); 
    PieceValidMoves.GenerateValidMoves(ChessBoard);
 Evaluation.EvaluateBoardScore(ChessBoard);
}

We also created a Move Piece method that will allow us to move chess pieces around the board.  The important fact to notice here is that if the move fails, say because it would cause an invalid position, the chess board reverts to its previous state.

public bool MovePiece(byte sourceColumn, byte sourceRow, 
         byte destinationColumn, byte destinationRow)
{
 byte srcPosition = 
          (byte)(sourceColumn + (sourceRow * 8));
 byte dstPosition =
          (byte)(destinationColumn + (destinationRow * 8));
 Piece Piece = ChessBoard.Squares[srcPosition].Piece;

 PreviousChessBoard = new Board(ChessBoard);
 
 Board.MovePiece(
          ChessBoard, srcPosition, 
               dstPosition, PromoteToPieceType);

 PieceValidMoves.GenerateValidMoves(ChessBoard);
 
 //If there is a check in place, check if this is still true;
 if (Piece.PieceColor == ChessPieceColor.White)
 {
  if (ChessBoard.WhiteCheck)
  {
   //Invalid Move
   ChessBoard = new Board(PreviousChessBoard);
   PieceValidMoves.GenerateValidMoves(ChessBoard);
   return false;
  }
 }
 else if (Piece.PieceColor == ChessPieceColor.Black)
 {
  if (ChessBoard.BlackCheck)
  {
   //Invalid Move
   ChessBoard = new Board(PreviousChessBoard);
   PieceValidMoves.GenerateValidMoves(ChessBoard);
   return false;
  }
 }

 MoveHistory.Push(ChessBoard.LastMove);

 return true;
}

That’s it for the review now onto the remainder of the code needed for our chess engine to successfully play chess.

First we will introduce a few public variables:

We want to know which side of the board contains the human player.

public ChessPieceColor HumanPlayer;

We need to know how deep to perform the AI search, how many plies. Remember each ply is a single move.  So if white moves that is one ply, if black responds that is two ply.

public byte PlyDepthSearched = 5;

We also would like to keep track of all of the moves made during the game.  This will solve two problems.  First in order to call a draw for a three move repetition we need to somehow know what moves have been made.  Second we might want to be able to display the move history to the human player as we go along.

First we need to declare the OpeningMove class:

internal class OpeningMove
{
 public string EndingFEN;
 public string StartingFEN;
 public List<MoveContent> Moves;
 public OpeningMove()
 {
  StartingFEN = String.Empty;
  EndingFEN = String.Empty;
  Moves = new List<MoveContent>();
 }
}
internal static List<OpeningMove> CurrentGameBook;

The next method will add moves to the above declared Current Game Book as they occur.  This method also searches the Game Book to see if a repeat move has occurred.  Notice the use of FEN notation to store the chess board history. 

internal static void SaveCurrentGameMove(Board currentBoard, Board previousBoard, ICollection<OpeningMove> gameBook, MoveContent bestMove)
{
 try
 {
  var move = new OpeningMove();
  move.StartingFEN = Board.Fen(true, previousBoard);
  move.EndingFEN = Board.Fen(true, currentBoard);
  move.Moves.Add(bestMove);

  gameBook.Add(move);

  foreach (OpeningMove move1 in gameBook)
  {
   byte repeatedMoves = 0;

   foreach (OpeningMove move2 in gameBook)
   {
    if (move1.EndingFEN == move2.EndingFEN)
    {
     repeatedMoves++;
    }
   }

   if (previousBoard.RepeatedMove < repeatedMoves)
   {
    previousBoard.RepeatedMove = repeatedMoves;
    currentBoard.RepeatedMove = repeatedMoves;
   }
  }
  if (currentBoard.RepeatedMove >= 3)
  {
   currentBoard.StaleMate = true;
  }
 }
 catch (Exception)
 {
  return;
 }

 return;
}
}

Now we have a mechanism for testing for 3 move repetition.  Remember our chess board class already handles the 50 move rule.  The last step is to create a method that will check for mate scenarios, check and stale.  This method takes advantage of the code we already wrote in the Move Search class that checks for all available moves and records if the king has any moves not in check.

private static bool CheckForMate(
                ChessPieceColor whosTurn, ref Board chessBoard)
{
 Search.SearchForMate(
       whosTurn, chessBoard, ref chessBoard.BlackMate,
       ref chessBoard.WhiteMate, ref chessBoard.StaleMate);
 if (chessBoard.BlackMate || 
           chessBoard.WhiteMate || chessBoard.StaleMate)
 {
  return true;
 }

 return false;
}

The last method will make the chess move for the computer as well as check for mate, and save current game moves.  This is the method our external user interface calls when we want to get the computer to make the move.  Otherwise if the human player is moving you would just call the move method.  Notice that the check for mate method is called two times.  Once before the move is made and once after.  This is because the previous human move might have caused a mate (first call) or the computer move might have caused a mate (second call).

public void AIPonderMove()
{
    if (CheckForMate(WhoseMove, ref ChessBoard))
    {
        return;
    }
    MoveContent bestMove = new MoveContent();
 
    //If there is no playbook move search for the best move
    bestMove = AlphaBetaRoot(ChessBoard, PlyDepthSearched);
   
    //Make the move 
    PreviousChessBoard = new Board(ChessBoard); 
   
    Board.MovePiece(ChessBoard,     
         bestMove.MovingPiecePrimary.SrcPosition,  
         bestMove.MovingPiecePrimary.DstPosition,             
         ChessPieceType.Queen);
   
    SaveCurrentGameMove(bestBoard, ChessBoard, CurrentGameBook);
    PieceValidMoves.GenerateValidMoves(ChessBoard);
    Evaluation.EvaluateBoardScore(ChessBoard);

    if (CheckForMate(WhoseMove, ref ChessBoard))
    {
        return;
    }
}

The above methods are all you need to start coding your chess user interface.  However because we have declared most of our variables as private or internal if your user interface is in another assembly you might need a few additional methods that will expose certain properties of your chess board and chess engine.  I have included some of these below.

public bool GetBlackMate()
{
    return ChessBoard.BlackMate;
}

public bool GetWhiteMate()
{
    return ChessBoard.WhiteMate;
}

public bool GetBlackCheck()
{
    return ChessBoard.BlackCheck;
}
public bool GetWhiteCheck()
{
    return ChessBoard.WhiteCheck;
}

public byte GetRepeatedMove()
{
    return ChessBoard.RepeatedMove;
}

public byte GetFiftyMoveCount()
{
    return ChessBoard.FiftyMove;
}

public bool IsValidMove(byte sourceColumn, byte sourceRow, byte destinationColumn, byte destinationRow)
{
 if (ChessBoard == null)
 {
  return false;
 }
 if (ChessBoard.Squares == null)
 {
  return false;
 }

 byte index = GetBoardIndex(sourceColumn, sourceRow);

 if (ChessBoard.Squares[index].Piece == null)
 {
  return false;
 }

 foreach (byte bs in ChessBoard.Squares[index].Piece.ValidMoves)
 {
  if (bs % 8 == destinationColumn)
  {
   if ((byte)(bs / 8) == destinationRow)
   {
    return true;
   }
  }
 }

 index = GetBoardIndex(destinationColumn, destinationRow);

 if (index == ChessBoard.EnPassantPosition)
 {
  return true;
 }

 return false;
}


public ChessPieceType GetPieceTypeAt(byte boardColumn, byte boardRow)
{
 byte index = GetBoardIndex(boardColumn, boardRow);
 if (ChessBoard.Squares[index].Piece == null)
 {
  return ChessPieceType.None;
 }

 return ChessBoard.Squares[index].Piece.PieceType;
}

public ChessPieceType GetPieceTypeAt(byte index)
{
 if (ChessBoard.Squares[index].Piece == null)
 {
  return ChessPieceType.None;
 }

 return ChessBoard.Squares[index].Piece.PieceType;
}

public ChessPieceColor GetPieceColorAt(byte boardColumn, byte boardRow)
{
 byte index = GetBoardIndex(boardColumn, boardRow);

 if (ChessBoard.Squares[index].Piece == null)
 {
  return ChessPieceColor.White;
 }
 return ChessBoard.Squares[index].Piece.PieceColor;
}

public ChessPieceColor GetPieceColorAt(byte index)
{
 if (ChessBoard.Squares[index].Piece == null)
 {
  return ChessPieceColor.White;
 }
 return ChessBoard.Squares[index].Piece.PieceColor;
}

Notice this method will check for all game ending scenarios including 50 move and 3 move repetition.

public bool IsGameOver()
{
 if (ChessBoard.StaleMate)
 {
  return true;
 }
 if (ChessBoard.WhiteMate || ChessBoard.BlackMate)
 {
  return true;
 }
 if (ChessBoard.FiftyMove >= 50)
 {
  return true;
 }
 if (ChessBoard.RepeatedMove >= 3)
 {
  return true;
 }
 if (ChessBoard.InsufficientMaterial)
 {
  return true;
 }
 return false;
}

This post is a bit of a milestone as it wraps up the bulk of the chess engine source code.  There are still a few points I have not discussed such as an opening book or some more advanced search features such as Quiescence, FEN, Pondering, Iterative Deepening or Principle Variation Search.   However the sum of the code posted thus far will provide you will a working chess engine that will play fairly good chess.

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