Forsyth–Edwards Notation

In this post I am going to discuss Forsyth-Edwards Notation (FEN) and its implementation in a chess engine. FEN is a standard way of describing a chess position, containing enough information to restart the chess game from that position. It is based on a notation developed by a Scottish journalist, David Forsyth in the 19th century.

Why is FEN useful to us?

  • We can use FEN to store game history allowing us to search for move repetitions as well as display the history of the game to the user. Furthermore if we find a FEN position that has occurred in the past, we can skip searching for the best move and use the same response we used before.
  • We can use FEN strings to implement an Opening Book. With two FEN strings I can store position pairs representing a starting position and the prescribed response.

The implementation of Forsyth–Edwards Notation

FEN notation uses only ASCII characters stored in a single line. A FEN string or record contains 6 fields. These are separated by a space.

Piece placement from white’s perspective. Each row is noted, starting from row 8 (blacks row0 and ending with row 1 (white’s row). Each piece is described from column to column h. Each piece is identified by a single letter.

  • Pawn: P
  • Knight: K
  • Bishop: B
  • Rook: R
  • Queen: Q
  • King: K

White pieces are noted using capital letters and black using lower case. So P would be a white pawn and p would signify a black pawn.

Empty squares (spaces) are described using numbers, each number representing the number of empty squares before the next chess pieces. The number 8 would describe a completely empty row. The character / describes a new row.

So for a starting position we may see: rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR

The second column in the Forsyth–Edwards Notation represents whose turn it is. A single character is used w for white and b for black.

The third column represents if castling is still allowed. If neither side can castle then the character – is used. Otherwise the following letters are used. K means white can castle King Side, Q means White can castle Queen side. Lower case k and q mean the same for black.

The fourth column represents an En Passant target square. The square that the pawn hopped to get to its row, or the position behind the pawn. If there is no En Passant square then the character – is used. So if the last move was pawn to e4, we will record e3 in this column. The fifth column contains the number of half moves since the last pawn move or capture. This is used to determine the 50 move draw scenario.

The last column contains the full move number. The number starts at 1 and is incremented after black’s move.

Examples:

FEN for the starting position: rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq – 0 1 FEN after the white pawn moved to E4: rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1 FEN after the black pawn moved to C5 rnbqkbnr/pp1ppppp/8/2p5/4P3/8/PPPP1PPP/RNBQKBNR w KQkq c6 0 2 And then after the white knight moves to F3: rnbqkbnr/pp1ppppp/8/2p5/4P3/5N2/PPPP1PPP/RNBQKB1R b KQkq – 1 2

Forsyth–Edwards Notation Code

internal static string Fen(bool boardOnly, Board board)
{
 string output = String.Empty;
 byte blankSquares = 0;
 for (byte x = 0; x < 64; x++)
 {
  byte index = x;

  if (board.Squares[index].Piece != null)
  {
   if (blankSquares > 0)
   {
    output += blankSquares.ToString();
    blankSquares = 0;
   }

   if (board.Squares[index].Piece.PieceColor == ChessPieceColor.Black)
   {
    output += Piece.GetPieceTypeShort(board.Squares[index].Piece.PieceType).ToLower();
   }
   else
   {
    output += Piece.GetPieceTypeShort(board.Squares[index].Piece.PieceType);
   }
  }
  else
  {
   blankSquares++;
  }

  if (x % 8 == 7)
  {
   if (blankSquares > 0)
   {
    output += blankSquares.ToString();
    output += "/";
    blankSquares = 0;
   }
   else
   {
    if (x > 0 && x != 63)
    {
     output += "/";
    }
   }
  }
 }

 if (board.WhoseMove == ChessPieceColor.White)
 {
  output += " w ";
 }
 else
 {
  output += " b ";
 }

 string spacer = "";

 if (board.WhiteCastled == false)
 {
  if (board.Squares[60].Piece != null)
  {
   if (board.Squares[60].Piece.Moved == false)
   {
    if (board.Squares[63].Piece != null)
    {
     if (board.Squares[63].Piece.Moved == false)
     {
      output += "K";
      spacer = " ";
     }
    }
    if (board.Squares[56].Piece != null)
    {
     if (board.Squares[56].Piece.Moved == false)
     {
      output += "Q";
      spacer = " ";
     }
    }
   }
  }
 }

 if (board.BlackCastled == false)
 {
  if (board.Squares[4].Piece != null)
  {
   if (board.Squares[4].Piece.Moved == false)
   {
    if (board.Squares[7].Piece != null)
    {
     if (board.Squares[7].Piece.Moved == false)
     {
      output += "k";
      spacer = " ";
     }
    }
    if (board.Squares[0].Piece != null)
    {
     if (board.Squares[0].Piece.Moved == false)
     {
      output += "q";
      spacer = " ";
     }
    }
   }
  }

  
 }

 if (output.EndsWith("/"))
 {
  output.TrimEnd('/');
 }


 if (board.EnPassantPosition != 0)
 {
  output += spacer + GetColumnFromByte((byte)(board.EnPassantPosition % 8)) + "" + (byte)(8 - (byte)(board.EnPassantPosition / 8)) + " ";
 }
 else
 {
  output += spacer + "- ";
 }

 if (!boardOnly)
 {
  output += board.FiftyMove + " ";
  output += board.MoveCount + 1;
 }
 return output.Trim();
}

The second method is a Board constructor that will accept a FEN string and create a Chess Board based on the content of the string. Strictly speaking you will not need this code. I only use this to allow people to enter FEN strings in the user interface. Since FEN is a standard used in many chess programs allowing users to input FEN strings will allow them to visualize chess positions they might find on the internet.

internal Board(string fen) : this()
{
 byte index = 0;
 byte spc = 0;
 WhiteCastled = true;
 BlackCastled = true;
 byte spacers = 0;

 WhoseMove = ChessPieceColor.White;

 if (fen.Contains("a3"))
 {
  EnPassantColor = ChessPieceColor.White;
  EnPassantPosition = 40;
 }
 else if (fen.Contains("b3"))
 {
  EnPassantColor = ChessPieceColor.White;
  EnPassantPosition = 41;
 }
 else if (fen.Contains("c3"))
 {
  EnPassantColor = ChessPieceColor.White;
  EnPassantPosition = 42;
 }
 else if (fen.Contains("d3"))
 {
  EnPassantColor = ChessPieceColor.White;
  EnPassantPosition = 43;
 }
 else if (fen.Contains("e3"))
 {
  EnPassantColor = ChessPieceColor.White;
  EnPassantPosition = 44;
 }
 else if (fen.Contains("f3"))
 {
  EnPassantColor = ChessPieceColor.White;
  EnPassantPosition = 45;
 }
 else if (fen.Contains("g3"))
 {
  EnPassantColor = ChessPieceColor.White;
  EnPassantPosition = 46;
 }
 else if (fen.Contains("h3"))
 {
  EnPassantColor = ChessPieceColor.White;
  EnPassantPosition = 47;
 }


 if (fen.Contains("a6"))
 {
  EnPassantColor = ChessPieceColor.White;
  EnPassantPosition = 16;
 }
 else if (fen.Contains("b6"))
 {
  EnPassantColor = ChessPieceColor.White;
  EnPassantPosition = 17;
 }
 else if (fen.Contains("c6"))
 {
  EnPassantColor = ChessPieceColor.White;
  EnPassantPosition =18;
 }
 else if (fen.Contains("d6"))
 {
  EnPassantColor = ChessPieceColor.White;
  EnPassantPosition = 19;
 }
 else if (fen.Contains("e6"))
 {
  EnPassantColor = ChessPieceColor.White;
  EnPassantPosition = 20;
 }
 else if (fen.Contains("f6"))
 {
  EnPassantColor = ChessPieceColor.White;
  EnPassantPosition = 21;
 }
 else if (fen.Contains("g6"))
 {
  EnPassantColor = ChessPieceColor.White;
  EnPassantPosition = 22;
 }
 else if (fen.Contains("h6"))
 {
  EnPassantColor = ChessPieceColor.White;
  EnPassantPosition = 23;
 }

 foreach (char c in fen)
 {

  if (index < 64 && spc == 0)
  {
   if (c == '1' && index < 63)
   {
    index++;
   }
   else if (c == '2' && index < 62)
   {
    index += 2;
   }
   else if (c == '3' && index < 61)
   {
    index += 3;
   }
   else if (c == '4' && index < 60)
   {
    index += 4;
   }
   else if (c == '5' && index < 59)
   {
    index += 5;
   }
   else if (c == '6' && index < 58)
   {
    index += 6;
   }
   else if (c == '7' && index < 57)
   {
    index += 7;
   }
   else if (c == '8' && index < 56)
   {
    index += 8;
   }
   else if (c == 'P')
   {
    Squares[index].Piece = new Piece(ChessPieceType.Pawn, ChessPieceColor.White);
    Squares[index].Piece.Moved = true;
    index++;
   }
   else if (c == 'N')
   {
    Squares[index].Piece = new Piece(ChessPieceType.Knight, ChessPieceColor.White);
    Squares[index].Piece.Moved = true;
    index++;
   }
   else if (c == 'B')
   {
    Squares[index].Piece = new Piece(ChessPieceType.Bishop, ChessPieceColor.White);
    Squares[index].Piece.Moved = true;
    index++;
   }
   else if (c == 'R')
   {
    Squares[index].Piece = new Piece(ChessPieceType.Rook, ChessPieceColor.White);
    Squares[index].Piece.Moved = true;
    index++;
   }
   else if (c == 'Q')
   {
    Squares[index].Piece = new Piece(ChessPieceType.Queen, ChessPieceColor.White);
    Squares[index].Piece.Moved = true;
    index++;
   }
   else if (c == 'K')
   {
    Squares[index].Piece = new Piece(ChessPieceType.King, ChessPieceColor.White);
    Squares[index].Piece.Moved = true;
    index++;
   }
   else if (c == 'p')
   {
    Squares[index].Piece = new Piece(ChessPieceType.Pawn, ChessPieceColor.Black);
    Squares[index].Piece.Moved = true;
    index++;
   }
   else if (c == 'n')
   {
    Squares[index].Piece = new Piece(ChessPieceType.Knight, ChessPieceColor.Black);
    Squares[index].Piece.Moved = true;
    index++;
   }
   else if (c == 'b')
   {
    Squares[index].Piece = new Piece(ChessPieceType.Bishop, ChessPieceColor.Black);
    Squares[index].Piece.Moved = true;
    index++;
   }
   else if (c == 'r')
   {
    Squares[index].Piece = new Piece(ChessPieceType.Rook, ChessPieceColor.Black);
    Squares[index].Piece.Moved = true;
    index++;
   }
   else if (c == 'q')
   {
    Squares[index].Piece = 
         new Piece(ChessPieceType.Queen, ChessPieceColor.Black);
    Squares[index].Piece.Moved = true;
    index++;
   }
   else if (c == 'k')
   {
    Squares[index].Piece = 
         new Piece(ChessPieceType.King, ChessPieceColor.Black);      
    Squares[index].Piece.Moved = true;
    index++;
   }
   else if (c == '/')
   {
    continue;
   }
   else if (c == ' ')
   {
    spc++;
   }
  }
  else
  {
   if (c == 'w')
   {
    WhoseMove = ChessPieceColor.White;
   }
   else if (c == 'b')
   {
    WhoseMove = ChessPieceColor.Black;
   }
   else if (c == 'K')
   {
    if (Squares[60].Piece != null)
    {
     if (Squares[60].Piece.PieceType == ChessPieceType.King)
     {
      Squares[60].Piece.Moved = false;
     }
    }

    if (Squares[63].Piece != null)
    {
     if (Squares[63].Piece.PieceType == ChessPieceType.Rook)
     {
      Squares[63].Piece.Moved = false;
     }
    }

    WhiteCastled = false;
   }
   else if (c == 'Q')
   {
    if (Squares[60].Piece != null)
    {
     if (Squares[60].Piece.PieceType == ChessPieceType.King)
     {
      Squares[60].Piece.Moved = false;
     }
    }

    if (Squares[56].Piece != null)
    {
     if (Squares[56].Piece.PieceType == ChessPieceType.Rook)
     {
      Squares[56].Piece.Moved = false;
     }
    }

    WhiteCastled = false;
   }
   else if (c == 'k')
   {
    if (Squares[4].Piece != null)
    {
     if (Squares[4].Piece.PieceType == ChessPieceType.King)
     {
      Squares[4].Piece.Moved = false;
     }
    }

    if (Squares[7].Piece != null)
    {
     if (Squares[7].Piece.PieceType == ChessPieceType.Rook)
     {
      Squares[7].Piece.Moved = false;
     }
    }

    BlackCastled = false;
   }
   else if (c == 'q')
   {
    if (Squares[4].Piece != null)
    {
     if (Squares[4].Piece.PieceType == ChessPieceType.King)
     {
      Squares[4].Piece.Moved = false;
     }
    }

    if (Squares[0].Piece != null)
    {
     if (Squares[0].Piece.PieceType == ChessPieceType.Rook)
     {
      Squares[0].Piece.Moved = false;
     }
    }

    BlackCastled = false;
   }
   else if (c == ' ')
   {
    spacers++;
   }
   else if (c == '1' && spacers == 4)
   {
    FiftyMove = (byte)((FiftyMove * 10) + 1);
   }
   else if (c == '2' && spacers == 4)
   {
    FiftyMove = (byte)((FiftyMove * 10) + 2);
   }
   else if (c == '3' && spacers == 4)
   {
    FiftyMove = (byte)((FiftyMove * 10) + 3);
   }
   else if (c == '4' && spacers == 4)
   {
    FiftyMove = (byte)((FiftyMove * 10) + 4);
   }
   else if (c == '5' && spacers == 4)
   {
    FiftyMove = (byte)((FiftyMove * 10) + 5);
   }
   else if (c == '6' && spacers == 4)
   {
    FiftyMove = (byte)((FiftyMove * 10) + 6);
   }
   else if (c == '7' && spacers == 4)
   {
    FiftyMove = (byte)((FiftyMove * 10) + 7);
   }
   else if (c == '8' && spacers == 4)
   {
    FiftyMove = (byte)((FiftyMove * 10) + 8);
   }
   else if (c == '9' && spacers == 4)
   {
    FiftyMove = (byte)((FiftyMove * 10) + 9);
   }
   else if (c == '0' && spacers == 4)
   {
    MoveCount = (byte)((MoveCount * 10) + 0);
   }
   else if (c == '1' && spacers == 5)
   {
    MoveCount = (byte)((MoveCount * 10) + 1);
   }
   else if (c == '2' && spacers == 5)
   {
    MoveCount = (byte)((MoveCount * 10) + 2);
   }
   else if (c == '3' && spacers == 5)
   {
    MoveCount = (byte)((MoveCount * 10) + 3);
   }
   else if (c == '4' && spacers == 5)
   {
    MoveCount = (byte)((MoveCount * 10) + 4);
   }
   else if (c == '5' && spacers == 5)
   {
    MoveCount = (byte)((MoveCount * 10) + 5);
   }
   else if (c == '6' && spacers == 5)
   {
    MoveCount = (byte)((MoveCount * 10) + 6);
   }
   else if (c == '7' && spacers == 5)
   {
    MoveCount = (byte)((MoveCount * 10) + 7);
   }
   else if (c == '8' && spacers == 5)
   {
    MoveCount = (byte)((MoveCount * 10) + 8);
   }
   else if (c == '9' && spacers == 5)
   {
    MoveCount = (byte)((MoveCount * 10) + 9);
   }
   else if (c == '0' && spacers == 5)
   {
    MoveCount = (byte)((MoveCount * 10) + 0);
   }

  }
 }

   
}

This concludes the post on Forsyth–Edwards Notation.

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