It's been a while since I've built any games and so I figured I would set out to create a chess application.

I have the concept of Squares, Moves, Pieces (so far only started on Pawn), and Boards. This is the relevant code:

Chess Board
Keeps track of the squares and pieces
Code:
Namespace Board

    Public Class ChessBoard

        ' private variables
        Private ReadOnly _darkPieces(15) As ChessPiece
        Private ReadOnly _lightPieces(15) As ChessPiece
        Private ReadOnly _squares(63) As Square

        ' private methods
        Private Sub InitializeSquares()
            Dim counter As Integer = 0
            For rank As Byte = 0 To Square.MAX_RANK
                For file As Integer = 0 To Square.FILE_NAMES.Length - 1
                    _squares(counter) = New Square(rank, Convert.ToByte(file))
                    counter += 1
                Next
            Next
        End Sub

        Private Sub InitializePawns()
            Const darkRank As Integer = 6
            Const lightRank As Integer = 1

            Dim index As Integer = 0
            For fileIndex As Byte = 0 To Convert.ToByte(Square.FILE_NAMES.Length - 1)
                _darkPieces(index) = New Pawn(Shades.Dark, New Square(darkRank, fileIndex))
                _lightPieces(index) = New Pawn(Shades.Dark, New Square(lightRank, fileIndex))
            Next
        End Sub

        ' properties
        Public ReadOnly Property Squares As IReadOnlyList(Of Square)
            Get
                Return _squares
            End Get
        End Property

        ' public methods
        Public Function GetSquare(file As Byte, rank As Byte) As Square
            If (file > Square.FILE_NAMES.Length - 1) Then
                Throw New ArgumentOutOfRangeException(NameOf(file))
            End If
            If (rank > Square.MAX_RANK) Then
                Throw New ArgumentOutOfRangeException(NameOf(rank))
            End If

            Return _squares.Single(Function(square) square.File.Equals(file) AndAlso square.Rank.Equals(rank))
        End Function

        Public Function HasEnemyPiece(square As Square, shade As Shades) As Boolean
            Dim opposingPieces As ChessPiece() = If(shade = Shades.Dark, _lightPieces, _darkPieces)

            Return opposingPieces.Any(Function(piece) piece IsNot Nothing AndAlso piece.Square.Equals(square))
        End Function

        Public Function HasFriendlyPiece(square As Square, shade As Shades) As Boolean
            Dim friendlyPieces As ChessPiece() = If(shade = Shades.Dark, _darkPieces, _lightPieces)

            Return friendlyPieces.Any(Function(piece) piece IsNot Nothing AndAlso piece.Square.Equals(square))
        End Function

        Public Function IsEmpty(square As Square) As Boolean
            Return _
                _darkPieces.All(Function(piece) piece Is Nothing OrElse Not piece.Square.Equals(square)) AndAlso
                _lightPieces.All(Function(piece) piece Is Nothing OrElse Not piece.Square.Equals(square))
        End Function

        Public Sub Reset()
            ' WIP
            InitializePawns()
        End Sub

        ' constructor
        Public Sub New()
            InitializeSquares()
        End Sub

    End Class

End Namespace
Square
Represents a physical space on the chess board
Code:
Namespace Board

    Public Class Square
        Implements IEquatable(Of Square)

        ' private variables/constants
        Friend Const FILE_NAMES As String = "ABCDEFGH"

        Friend Const MAX_RANK As Integer = 7

        Private ReadOnly _file As Byte = 0
        Private ReadOnly _rank As Byte = 0

        ' properties
        Public ReadOnly Property Shade As Shades
            Get
                Dim evenRank As Boolean = _rank Mod 2 = 0
                Dim evenFile As Boolean = _file Mod 2 = 0
                Return If(
                    (evenRank AndAlso evenFile) OrElse (Not evenRank AndAlso Not evenFile),
                    Shades.Dark,
                    Shades.Light
                )
            End Get
        End Property

        Public ReadOnly Property File As Byte
            Get
                Return _file
            End Get
        End Property

        Public ReadOnly Property FileName As String
            Get
                Return FILE_NAMES(_file).ToString()
            End Get
        End Property

        Public ReadOnly Property Name As String
            Get
                Return String.Concat(FileName, RankName)
            End Get
        End Property

        Public ReadOnly Property Rank As Byte
            Get
                Return _rank
            End Get
        End Property

        Public ReadOnly Property RankName As String
            Get
                Return (_rank + 1).ToString()
            End Get
        End Property

        ' public methods
        Public Overloads Function Equals(other As Square) As Boolean Implements IEquatable(Of Square).Equals
            If (other Is Nothing) Then
                Return False
            End If
            If (ReferenceEquals(Me, other)) Then
                Return True
            End If
            Return File = other.File AndAlso Rank = other.Rank
        End Function

        Public Overrides Function Equals(obj As Object) As Boolean
            Return Equals(TryCast(obj, Square))
        End Function

        Public Overrides Function GetHashCode() As Integer
            Return HashCode.Combine(File, Rank)
        End Function

        Public Shared Operator =(square1 As Square, square2 As Square) As Boolean
            If (square1 Is Nothing) Then
                Return square2 Is Nothing
            End If
            Return square1.Equals(square2)
        End Operator

        Public Shared Operator <>(square1 As Square, square2 As Square) As Boolean
            If (square1 Is Nothing) Then
                Return square2 IsNot Nothing
            End If
            Return Not square1.Equals(square2)
        End Operator

        Public Overrides Function ToString() As String
            Return Name
        End Function

        Public Sub New(rank As Byte, file As Byte)
            _rank = rank
            _file = file
        End Sub

    End Class

End Namespace
ChessPiece
Parent class representing a chess piece; must be inherited
Code:
Namespace Pieces

    Public MustInherit Class ChessPiece

        Public MustOverride ReadOnly Property PieceType As PieceTypes
        Public ReadOnly Property Shade As Shades
        Public Property Square As Square

        Public MustOverride Function GetMoves(board As ChessBoard) As IEnumerable(Of Move)

        Public Sub New(shade As Shades)
            _Shade = shade
        End Sub

        Public Sub New(shade As Shades, square As Square)
            _Shade = shade
            _Square = square
        End Sub

    End Class

End Namespace
Pawn
Child chess piece for a pawn
Code:
Namespace Pieces

    Public Class Pawn
        Inherits ChessPiece

        ' private variables
        Private Shared ReadOnly PromotionPieceTypes As PieceTypes() = {
            PieceTypes.Queen,
            PieceTypes.Rook,
            PieceTypes.Bishop,
            PieceTypes.Knight
        }

        ' private methods
        Private Sub AddForwardMoves(board As ChessBoard, moves As List(Of Move), targetRank As Byte)
            Dim targetSquare As Square = board.GetSquare(Square.File, targetRank)

            If (Not board.IsEmpty(targetSquare)) Then
                Return
            End If

            If (IsPromotionRank(targetRank)) Then
                AddPromotionMoves(moves, Square, targetSquare, MoveTypes.Promotion)
            Else
                Dim nextMove As New Move(Square, targetSquare, MoveTypes.Normal)
                moves.Add(nextMove)

                If (IsStartingRank(Square.Rank)) Then
                    Dim skipRankNumber As Byte = Convert.ToByte(Square.Rank + If(Shade.Equals(Shades.Dark), 2, -2))
                    Dim skipSquare As Square = board.GetSquare(Square.File, skipRankNumber)

                    If (board.IsEmpty(skipSquare)) Then
                        moves.Add(New Move(Square, skipSquare, MoveTypes.Normal))
                    End If
                End If
            End If
        End Sub

        Private Sub AddPawnCaptureMove(board As ChessBoard, moves As List(Of Move), targetFile As Byte, targetRank As Byte)
            Dim targetSquare As Square = board.GetSquare(targetFile, targetRank)
            If (Not board.HasEnemyPiece(targetSquare, Shade)) Then
                Return
            End If

            If IsPromotionRank(targetRank) Then
                AddPromotionMoves(moves, Square, targetSquare, MoveTypes.PromotionCapture)
            Else
                moves.Add(New Move(Square, targetSquare, MoveTypes.Capture))
            End If
        End Sub

        Private Shared Sub AddPromotionMoves(moves As List(Of Move), fromSquare As Square, toSquare As Square, moveType As MoveTypes)
            For Each promotionPieceType As PieceTypes In PromotionPieceTypes
                moves.Add(New Move(fromSquare, toSquare, moveType, promotionPieceType))
            Next
        End Sub

        Private Function IsStartingRank(rank As Byte) As Boolean
            Return If(Shade.Equals(Shades.Dark), rank = 1, rank = 6)
        End Function

        Private Function IsPromotionRank(rank As Byte) As Boolean
            Return If(Shade.Equals(Shades.Dark), rank = 7, rank = 0)
        End Function

        ' properties
        Public Overrides ReadOnly Property PieceType As PieceTypes = PieceTypes.Pawn

        ' public methods
        Public Overrides Function GetMoves(board As ChessBoard) As IEnumerable(Of Move)
            If (Square Is Nothing) Then
                Return Enumerable.Empty(Of Move)()
            End If

            Dim nextRankNumber As Byte = Convert.ToByte(Square.Rank + If(Shade.Equals(Shades.Dark), -1, 1))
            Dim moves As New List(Of Move)

            AddForwardMoves(board, moves, nextRankNumber)
            If (Square.File > 0) Then
                AddPawnCaptureMove(board, moves, CByte(Square.File - 1), nextRankNumber)
            End If
            If (Square.File < 7) Then
                AddPawnCaptureMove(board, moves, CByte(Square.File + 1), nextRankNumber)
            End If

            Return moves
        End Function

        ' constructor
        Public Sub New(shade As Shades)
            MyBase.New(shade)
        End Sub

        Public Sub New(shade As Shades, square As Square)
            MyBase.New(shade, square)
        End Sub

    End Class

End Namespace
The pawn works for the most part, except I cannot figure out how to handle en passant.

The rules for en passant are that the capturing pawn can take an opponent's pawn if:
  1. The opponent's previously moved piece was a pawn
  2. The opponent's pawn moved forward 2 spaces (which can only happen the first time its moved)
  3. The opponent's pawn falls on the same rank as the capturing pawn


This is essentially game logic, but as you can tell from my setup, I'm trying to lay out all the potential moves of a given piece inside the Pawn class. My issue is that I'm not quite sure how to square those two concepts.