Example #1
0
        private void DrawBookMoves()
        {
            ClearArrows();
            arrowIndex = 0;

            if (book.HasPosition(board.ZobristKey))
            {
                var bookPosition = book.GetBookPosition(board.ZobristKey);
                var mostPlayed   = 0;
                var leastPlayed  = int.MaxValue;
                foreach (var moveInfo in bookPosition.numTimesMovePlayed)
                {
                    var numTimesPlayed = moveInfo.Value;
                    mostPlayed  = Math.Max(mostPlayed, numTimesPlayed);
                    leastPlayed = Math.Min(leastPlayed, numTimesPlayed);
                }

                foreach (var moveInfo in bookPosition.numTimesMovePlayed)
                {
                    var     move           = new Move(moveInfo.Key);
                    var     numTimesPlayed = moveInfo.Value;
                    Vector2 startPos       = boardUI.PositionFromCoord(BoardRepresentation.CoordFromIndex(move.StartSquare));
                    Vector2 endPos         = boardUI.PositionFromCoord(BoardRepresentation.CoordFromIndex(move.TargetSquare));
                    var     t = Mathf.InverseLerp(leastPlayed, mostPlayed, numTimesPlayed);
                    if (mostPlayed == leastPlayed)
                    {
                        t = 1;
                    }

                    var col = Color.Lerp(rarestCol, mostCommonCol, t);
                    DrawArrow2D(startPos, endPos, arrowWidth, arrowHeadSize, col, zPos: -1 - t);
                }
            }
        }
Example #2
0
        // Initialize lookup data
        static PrecomputedMoveData()
        {
            pawnAttacksWhite = new int[64][];
            pawnAttacksBlack = new int[64][];
            numSquaresToEdge = new int[8][];
            knightMoves      = new byte[64][];
            kingMoves        = new byte[64][];
            numSquaresToEdge = new int[64][];

            rookMoves   = new ulong[64];
            bishopMoves = new ulong[64];
            queenMoves  = new ulong[64];

            // Calculate knight jumps and available squares for each square on the board.
            // See comments by variable definitions for more info.
            int[] allKnightJumps = { 15, 17, -17, -15, 10, -6, 6, -10 };
            knightAttackBitboards = new ulong[64];
            kingAttackBitboards   = new ulong[64];
            pawnAttackBitboards   = new ulong[64][];

            for (var squareIndex = 0; squareIndex < 64; squareIndex++)
            {
                var y = squareIndex / 8;
                var x = squareIndex - y * 8;

                var north = 7 - y;
                var south = y;
                var west  = x;
                var east  = 7 - x;
                numSquaresToEdge[squareIndex]    = new int[8];
                numSquaresToEdge[squareIndex][0] = north;
                numSquaresToEdge[squareIndex][1] = south;
                numSquaresToEdge[squareIndex][2] = west;
                numSquaresToEdge[squareIndex][3] = east;
                numSquaresToEdge[squareIndex][4] = Min(north, west);
                numSquaresToEdge[squareIndex][5] = Min(south, east);
                numSquaresToEdge[squareIndex][6] = Min(north, east);
                numSquaresToEdge[squareIndex][7] = Min(south, west);

                // Calculate all squares knight can jump to from current square
                var   legalKnightJumps = new List <byte>();
                ulong knightBitboard   = 0;
                foreach (var knightJumpDelta in allKnightJumps)
                {
                    var knightJumpSquare = squareIndex + knightJumpDelta;
                    if (knightJumpSquare >= 0 && knightJumpSquare < 64)
                    {
                        var knightSquareY = knightJumpSquare / 8;
                        var knightSquareX = knightJumpSquare - knightSquareY * 8;
                        // Ensure knight has moved max of 2 squares on x/y axis (to reject indices that have wrapped around side of board)
                        var maxCoordMoveDst = Max(Abs(x - knightSquareX), Abs(y - knightSquareY));
                        if (maxCoordMoveDst == 2)
                        {
                            legalKnightJumps.Add((byte)knightJumpSquare);
                            knightBitboard |= 1ul << knightJumpSquare;
                        }
                    }
                }

                knightMoves[squareIndex]           = legalKnightJumps.ToArray();
                knightAttackBitboards[squareIndex] = knightBitboard;

                // Calculate all squares king can move to from current square (not including castling)
                var legalKingMoves = new List <byte>();
                foreach (var kingMoveDelta in directionOffsets)
                {
                    var kingMoveSquare = squareIndex + kingMoveDelta;
                    if (kingMoveSquare >= 0 && kingMoveSquare < 64)
                    {
                        var kingSquareY = kingMoveSquare / 8;
                        var kingSquareX = kingMoveSquare - kingSquareY * 8;
                        // Ensure king has moved max of 1 square on x/y axis (to reject indices that have wrapped around side of board)
                        var maxCoordMoveDst = Max(Abs(x - kingSquareX), Abs(y - kingSquareY));
                        if (maxCoordMoveDst == 1)
                        {
                            legalKingMoves.Add((byte)kingMoveSquare);
                            kingAttackBitboards[squareIndex] |= 1ul << kingMoveSquare;
                        }
                    }
                }

                kingMoves[squareIndex] = legalKingMoves.ToArray();

                // Calculate legal pawn captures for white and black
                var pawnCapturesWhite = new List <int>();
                var pawnCapturesBlack = new List <int>();
                pawnAttackBitboards[squareIndex] = new ulong[2];
                if (x > 0)
                {
                    if (y < 7)
                    {
                        pawnCapturesWhite.Add(squareIndex + 7);
                        pawnAttackBitboards[squareIndex][Board.WhiteIndex] |= 1ul << (squareIndex + 7);
                    }

                    if (y > 0)
                    {
                        pawnCapturesBlack.Add(squareIndex - 9);
                        pawnAttackBitboards[squareIndex][Board.BlackIndex] |= 1ul << (squareIndex - 9);
                    }
                }

                if (x < 7)
                {
                    if (y < 7)
                    {
                        pawnCapturesWhite.Add(squareIndex + 9);
                        pawnAttackBitboards[squareIndex][Board.WhiteIndex] |= 1ul << (squareIndex + 9);
                    }

                    if (y > 0)
                    {
                        pawnCapturesBlack.Add(squareIndex - 7);
                        pawnAttackBitboards[squareIndex][Board.BlackIndex] |= 1ul << (squareIndex - 7);
                    }
                }

                pawnAttacksWhite[squareIndex] = pawnCapturesWhite.ToArray();
                pawnAttacksBlack[squareIndex] = pawnCapturesBlack.ToArray();

                // Rook moves
                for (var directionIndex = 0; directionIndex < 4; directionIndex++)
                {
                    var currentDirOffset = directionOffsets[directionIndex];
                    for (var n = 0; n < numSquaresToEdge[squareIndex][directionIndex]; n++)
                    {
                        var targetSquare = squareIndex + currentDirOffset * (n + 1);
                        rookMoves[squareIndex] |= 1ul << targetSquare;
                    }
                }

                // Bishop moves
                for (var directionIndex = 4; directionIndex < 8; directionIndex++)
                {
                    var currentDirOffset = directionOffsets[directionIndex];
                    for (var n = 0; n < numSquaresToEdge[squareIndex][directionIndex]; n++)
                    {
                        var targetSquare = squareIndex + currentDirOffset * (n + 1);
                        bishopMoves[squareIndex] |= 1ul << targetSquare;
                    }
                }

                queenMoves[squareIndex] = rookMoves[squareIndex] | bishopMoves[squareIndex];
            }

            directionLookup = new int[127];
            for (var i = 0; i < 127; i++)
            {
                var offset    = i - 63;
                var absOffset = Abs(offset);
                var absDir    = 1;
                if (absOffset % 9 == 0)
                {
                    absDir = 9;
                }
                else if (absOffset % 8 == 0)
                {
                    absDir = 8;
                }
                else if (absOffset % 7 == 0)
                {
                    absDir = 7;
                }

                directionLookup[i] = absDir * Sign(offset);
            }

            // Distance lookup
            orthogonalDistance      = new int[64, 64];
            kingDistance            = new int[64, 64];
            centreManhattanDistance = new int[64];
            for (var squareA = 0; squareA < 64; squareA++)
            {
                var coordA            = BoardRepresentation.CoordFromIndex(squareA);
                var fileDstFromCentre = Max(3 - coordA.fileIndex, coordA.fileIndex - 4);
                var rankDstFromCentre = Max(3 - coordA.rankIndex, coordA.rankIndex - 4);
                centreManhattanDistance[squareA] = fileDstFromCentre + rankDstFromCentre;

                for (var squareB = 0; squareB < 64; squareB++)
                {
                    var coordB       = BoardRepresentation.CoordFromIndex(squareB);
                    var rankDistance = Abs(coordA.rankIndex - coordB.rankIndex);
                    var fileDistance = Abs(coordA.fileIndex - coordB.fileIndex);
                    orthogonalDistance[squareA, squareB] = fileDistance + rankDistance;
                    kingDistance[squareA, squareB]       = Max(fileDistance, rankDistance);
                }
            }
        }
Example #3
0
        // Make a move on the board
        // The inSearch parameter controls whether this move should be recorded in the game history (for detecting three-fold repetition)
        public void MakeMove(Move move, bool inSearch = false)
        {
            uint oldEnPassantFile    = (currentGameState >> 4) & 15;
            uint originalCastleState = currentGameState & 15;
            uint newCastleState      = originalCastleState;

            currentGameState = 0;

            int opponentColourIndex = 1 - ColourToMoveIndex;
            int moveFrom            = move.StartSquare;
            int moveTo = move.TargetSquare;

            int capturedPieceType = Piece.PieceType(Square[moveTo]);
            int movePiece         = Square[moveFrom];
            int movePieceType     = Piece.PieceType(movePiece);

            int  moveFlag    = move.MoveFlag;
            bool isPromotion = move.IsPromotion;
            bool isEnPassant = moveFlag == Move.Flag.EnPassantCapture;

            // Handle captures
            currentGameState |= (ushort)(capturedPieceType << 8);
            if (capturedPieceType != 0 && !isEnPassant)
            {
                ZobristKey ^= Zobrist.piecesArray[capturedPieceType, opponentColourIndex, moveTo];
                GetPieceList(capturedPieceType, opponentColourIndex).RemovePieceAtSquare(moveTo);
            }

            // Move pieces in piece lists
            if (movePieceType == Piece.King)
            {
                KingSquare[ColourToMoveIndex] = moveTo;
                newCastleState &= (WhiteToMove) ? whiteCastleMask : blackCastleMask;
            }
            else
            {
                GetPieceList(movePieceType, ColourToMoveIndex).MovePiece(moveFrom, moveTo);
            }

            int pieceOnTargetSquare = movePiece;

            // Handle promotion
            if (isPromotion)
            {
                int promoteType = 0;
                switch (moveFlag)
                {
                case Move.Flag.PromoteToQueen:
                    promoteType = Piece.Queen;
                    queens[ColourToMoveIndex].AddPieceAtSquare(moveTo);
                    break;

                case Move.Flag.PromoteToRook:
                    promoteType = Piece.Rook;
                    rooks[ColourToMoveIndex].AddPieceAtSquare(moveTo);
                    break;

                case Move.Flag.PromoteToBishop:
                    promoteType = Piece.Bishop;
                    bishops[ColourToMoveIndex].AddPieceAtSquare(moveTo);
                    break;

                case Move.Flag.PromoteToKnight:
                    promoteType = Piece.Knight;
                    knights[ColourToMoveIndex].AddPieceAtSquare(moveTo);
                    break;
                }
                pieceOnTargetSquare = promoteType | ColourToMove;
                pawns[ColourToMoveIndex].RemovePieceAtSquare(moveTo);
            }
            else
            {
                // Handle other special moves (en-passant, and castling)
                switch (moveFlag)
                {
                case Move.Flag.EnPassantCapture:
                    int epPawnSquare = moveTo + ((ColourToMove == Piece.White) ? -8 : 8);
                    currentGameState    |= (ushort)(Square[epPawnSquare] << 8); // add pawn as capture type
                    Square[epPawnSquare] = 0;                                   // clear ep capture square
                    pawns[opponentColourIndex].RemovePieceAtSquare(epPawnSquare);
                    ZobristKey ^= Zobrist.piecesArray[Piece.Pawn, opponentColourIndex, epPawnSquare];
                    break;

                case Move.Flag.Castling:
                    bool kingside = moveTo == BoardRepresentation.g1 || moveTo == BoardRepresentation.g8;
                    int  castlingRookFromIndex = (kingside) ? moveTo + 1 : moveTo - 2;
                    int  castlingRookToIndex   = (kingside) ? moveTo - 1 : moveTo + 1;

                    Square[castlingRookFromIndex] = Piece.None;
                    Square[castlingRookToIndex]   = Piece.Rook | ColourToMove;

                    rooks[ColourToMoveIndex].MovePiece(castlingRookFromIndex, castlingRookToIndex);
                    ZobristKey ^= Zobrist.piecesArray[Piece.Rook, ColourToMoveIndex, castlingRookFromIndex];
                    ZobristKey ^= Zobrist.piecesArray[Piece.Rook, ColourToMoveIndex, castlingRookToIndex];
                    break;
                }
            }

            // Update the board representation:
            Square[moveTo]   = pieceOnTargetSquare;
            Square[moveFrom] = 0;

            // Pawn has moved two forwards, mark file with en-passant flag
            if (moveFlag == Move.Flag.PawnTwoForward)
            {
                int file = BoardRepresentation.FileIndex(moveFrom) + 1;
                currentGameState |= (ushort)(file << 4);
                ZobristKey       ^= Zobrist.enPassantFile[file];
            }

            // Piece moving to/from rook square removes castling right for that side
            if (originalCastleState != 0)
            {
                if (moveTo == BoardRepresentation.h1 || moveFrom == BoardRepresentation.h1)
                {
                    newCastleState &= whiteCastleKingsideMask;
                }
                else if (moveTo == BoardRepresentation.a1 || moveFrom == BoardRepresentation.a1)
                {
                    newCastleState &= whiteCastleQueensideMask;
                }
                if (moveTo == BoardRepresentation.h8 || moveFrom == BoardRepresentation.h8)
                {
                    newCastleState &= blackCastleKingsideMask;
                }
                else if (moveTo == BoardRepresentation.a8 || moveFrom == BoardRepresentation.a8)
                {
                    newCastleState &= blackCastleQueensideMask;
                }
            }

            // Update zobrist key with new piece position and side to move
            ZobristKey ^= Zobrist.sideToMove;
            ZobristKey ^= Zobrist.piecesArray[movePieceType, ColourToMoveIndex, moveFrom];
            ZobristKey ^= Zobrist.piecesArray[Piece.PieceType(pieceOnTargetSquare), ColourToMoveIndex, moveTo];

            if (oldEnPassantFile != 0)
            {
                ZobristKey ^= Zobrist.enPassantFile[oldEnPassantFile];
            }

            if (newCastleState != originalCastleState)
            {
                ZobristKey ^= Zobrist.castlingRights[originalCastleState];            // remove old castling rights state
                ZobristKey ^= Zobrist.castlingRights[newCastleState];                 // add new castling rights state
            }
            currentGameState |= newCastleState;
            currentGameState |= (uint)fiftyMoveCounter << 14;
            gameStateHistory.Push(currentGameState);

            // Change side to move
            WhiteToMove       = !WhiteToMove;
            ColourToMove      = (WhiteToMove) ? Piece.White : Piece.Black;
            OpponentColour    = (WhiteToMove) ? Piece.Black : Piece.White;
            ColourToMoveIndex = 1 - ColourToMoveIndex;
            plyCount++;
            fiftyMoveCounter++;

            if (!inSearch)
            {
                if (movePieceType == Piece.Pawn || capturedPieceType != Piece.None)
                {
                    RepetitionPositionHistory.Clear();
                    fiftyMoveCounter = 0;
                }
                else
                {
                    RepetitionPositionHistory.Push(ZobristKey);
                }
            }
        }
        static PrecomputedMoveData()
        {
            pawnAttacksWhite = new int[64][];
            pawnAttacksBlack = new int[64][];
            numSquaresToEdge = new int[8][];
            knightMoves      = new byte[64][];
            kingMoves        = new byte[64][];
            numSquaresToEdge = new int[64][];

            rookMoves   = new ulong[64];
            bishopMoves = new ulong[64];
            queenMoves  = new ulong[64];

            int[] allKnightJumps = { 15, 17, -17, -15, 10, -6, 6, -10 };
            knightAttackBitboards = new ulong[64];
            kingAttackBitboards   = new ulong[64];
            pawnAttackBitboards   = new ulong[64][];

            for (int squareIndex = 0; squareIndex < 64; squareIndex++)
            {
                int y = squareIndex / 8;
                int x = squareIndex - y * 8;

                int north = 7 - y;
                int south = y;
                int west  = x;
                int east  = 7 - x;
                numSquaresToEdge[squareIndex]    = new int[8];
                numSquaresToEdge[squareIndex][0] = north;
                numSquaresToEdge[squareIndex][1] = south;
                numSquaresToEdge[squareIndex][2] = west;
                numSquaresToEdge[squareIndex][3] = east;
                numSquaresToEdge[squareIndex][4] = System.Math.Min(north, west);
                numSquaresToEdge[squareIndex][5] = System.Math.Min(south, east);
                numSquaresToEdge[squareIndex][6] = System.Math.Min(north, east);
                numSquaresToEdge[squareIndex][7] = System.Math.Min(south, west);

                // Wszystkie pole na które moe skoczyć skoczek z danego pola
                var   legalKnightJumps = new List <byte> ();
                ulong knightBitboard   = 0;
                foreach (int knightJumpDelta in allKnightJumps)
                {
                    int knightJumpSquare = squareIndex + knightJumpDelta;
                    if (knightJumpSquare >= 0 && knightJumpSquare < 64)
                    {
                        int knightSquareY = knightJumpSquare / 8;
                        int knightSquareX = knightJumpSquare - knightSquareY * 8;
                        // Zapewnienie, że skoczek pokona co najmniej 2 pola (żeby uniknąć wyjścia poza mape i glitchowania)
                        int maxCoordMoveDst = System.Math.Max(System.Math.Abs(x - knightSquareX), System.Math.Abs(y - knightSquareY));
                        if (maxCoordMoveDst == 2)
                        {
                            legalKnightJumps.Add((byte)knightJumpSquare);
                            knightBitboard |= 1ul << knightJumpSquare;
                        }
                    }
                }
                knightMoves[squareIndex]           = legalKnightJumps.ToArray();
                knightAttackBitboards[squareIndex] = knightBitboard;

                // To co wyżej tylko dla króla (nie wliczając roszady)
                var legalKingMoves = new List <byte> ();
                foreach (int kingMoveDelta in directionOffsets)
                {
                    int kingMoveSquare = squareIndex + kingMoveDelta;
                    if (kingMoveSquare >= 0 && kingMoveSquare < 64)
                    {
                        int kingSquareY = kingMoveSquare / 8;
                        int kingSquareX = kingMoveSquare - kingSquareY * 8;
                        // Zapewnienie, że król pokona co najmniej 1 pole (żeby uniknąć wyjścia poza mape i glitchowania)
                        int maxCoordMoveDst = System.Math.Max(System.Math.Abs(x - kingSquareX), System.Math.Abs(y - kingSquareY));
                        if (maxCoordMoveDst == 1)
                        {
                            legalKingMoves.Add((byte)kingMoveSquare);
                            kingAttackBitboards[squareIndex] |= 1ul << kingMoveSquare;
                        }
                    }
                }
                kingMoves[squareIndex] = legalKingMoves.ToArray();

                List <int> pawnCapturesWhite = new List <int> ();
                List <int> pawnCapturesBlack = new List <int> ();
                pawnAttackBitboards[squareIndex] = new ulong[2];
                if (x > 0)
                {
                    if (y < 7)
                    {
                        pawnCapturesWhite.Add(squareIndex + 7);
                        pawnAttackBitboards[squareIndex][Board.WhiteIndex] |= 1ul << (squareIndex + 7);
                    }
                    if (y > 0)
                    {
                        pawnCapturesBlack.Add(squareIndex - 9);
                        pawnAttackBitboards[squareIndex][Board.BlackIndex] |= 1ul << (squareIndex - 9);
                    }
                }
                if (x < 7)
                {
                    if (y < 7)
                    {
                        pawnCapturesWhite.Add(squareIndex + 9);
                        pawnAttackBitboards[squareIndex][Board.WhiteIndex] |= 1ul << (squareIndex + 9);
                    }
                    if (y > 0)
                    {
                        pawnCapturesBlack.Add(squareIndex - 7);
                        pawnAttackBitboards[squareIndex][Board.BlackIndex] |= 1ul << (squareIndex - 7);
                    }
                }
                pawnAttacksWhite[squareIndex] = pawnCapturesWhite.ToArray();
                pawnAttacksBlack[squareIndex] = pawnCapturesBlack.ToArray();

                // Ruch wieży
                for (int directionIndex = 0; directionIndex < 4; directionIndex++)
                {
                    int currentDirOffset = directionOffsets[directionIndex];
                    for (int n = 0; n < numSquaresToEdge[squareIndex][directionIndex]; n++)
                    {
                        int targetSquare = squareIndex + currentDirOffset * (n + 1);
                        rookMoves[squareIndex] |= 1ul << targetSquare;
                    }
                }
                // Ruch gońca
                for (int directionIndex = 4; directionIndex < 8; directionIndex++)
                {
                    int currentDirOffset = directionOffsets[directionIndex];
                    for (int n = 0; n < numSquaresToEdge[squareIndex][directionIndex]; n++)
                    {
                        int targetSquare = squareIndex + currentDirOffset * (n + 1);
                        bishopMoves[squareIndex] |= 1ul << targetSquare;
                    }
                }
                queenMoves[squareIndex] = rookMoves[squareIndex] | bishopMoves[squareIndex];
            }

            directionLookup = new int[127];
            for (int i = 0; i < 127; i++)
            {
                int offset    = i - 63;
                int absOffset = System.Math.Abs(offset);
                int absDir    = 1;
                if (absOffset % 9 == 0)
                {
                    absDir = 9;
                }
                else if (absOffset % 8 == 0)
                {
                    absDir = 8;
                }
                else if (absOffset % 7 == 0)
                {
                    absDir = 7;
                }

                directionLookup[i] = absDir * System.Math.Sign(offset);
            }

            // Wyszukiwanie dystansu
            orthogonalDistance      = new int[64, 64];
            kingDistance            = new int[64, 64];
            centreManhattanDistance = new int[64];
            for (int squareA = 0; squareA < 64; squareA++)
            {
                Coord coordA            = BoardRepresentation.CoordFromIndex(squareA);
                int   fileDstFromCentre = Max(3 - coordA.fileIndex, coordA.fileIndex - 4);
                int   rankDstFromCentre = Max(3 - coordA.rankIndex, coordA.rankIndex - 4);
                centreManhattanDistance[squareA] = fileDstFromCentre + rankDstFromCentre;

                for (int squareB = 0; squareB < 64; squareB++)
                {
                    Coord coordB       = BoardRepresentation.CoordFromIndex(squareB);
                    int   rankDistance = Abs(coordA.rankIndex - coordB.rankIndex);
                    int   fileDistance = Abs(coordA.fileIndex - coordB.fileIndex);
                    orthogonalDistance[squareA, squareB] = fileDistance + rankDistance;
                    kingDistance[squareA, squareB]       = Max(fileDistance, rankDistance);
                }
            }
        }
Example #5
0
        static Move MoveFromAlgebraic(Board board, string algebraicMove)
        {
            MoveGenerator moveGenerator = new MoveGenerator();

            // Remove unrequired info from move string
            algebraicMove = algebraicMove.Replace("+", "").Replace("#", "").Replace("x", "").Replace("-", "");
            var allMoves = moveGenerator.GenerateMoves(board);

            Move move = new Move();

            foreach (Move moveToTest in allMoves)
            {
                move = moveToTest;

                int   moveFromIndex = move.StartSquare;
                int   moveToIndex   = move.TargetSquare;
                int   movePieceType = Piece.PieceType(board.Square[moveFromIndex]);
                Coord fromCoord     = BoardRepresentation.CoordFromIndex(moveFromIndex);
                Coord toCoord       = BoardRepresentation.CoordFromIndex(moveToIndex);
                if (algebraicMove == "OO")                   // castle kingside
                {
                    if (movePieceType == Piece.King && moveToIndex - moveFromIndex == 2)
                    {
                        return(move);
                    }
                }
                else if (algebraicMove == "OOO")                     // castle queenside
                {
                    if (movePieceType == Piece.King && moveToIndex - moveFromIndex == -2)
                    {
                        return(move);
                    }
                }
                // Is pawn move if starts with any file indicator (e.g. 'e'4. Note that uppercase B is used for bishops)
                else if (fileNames.Contains(algebraicMove[0].ToString()))
                {
                    if (movePieceType != Piece.Pawn)
                    {
                        continue;
                    }
                    if (fileNames.IndexOf(algebraicMove[0]) == fromCoord.fileIndex) // correct starting file
                    {
                        if (algebraicMove.Contains("="))                            // is promotion
                        {
                            if (toCoord.rankIndex == 0 || toCoord.rankIndex == 7)
                            {
                                if (algebraicMove.Length == 5)                                 // pawn is capturing to promote
                                {
                                    char targetFile = algebraicMove[1];
                                    if (BoardRepresentation.fileNames.IndexOf(targetFile) != toCoord.fileIndex)
                                    {
                                        // Skip if not moving to correct file
                                        continue;
                                    }
                                }
                                char promotionChar = algebraicMove[algebraicMove.Length - 1];

                                if (move.PromotionPieceType != GetPieceTypeFromSymbol(promotionChar))
                                {
                                    continue;                                     // skip this move, incorrect promotion type
                                }

                                return(move);
                            }
                        }
                        else
                        {
                            char targetFile = algebraicMove[algebraicMove.Length - 2];
                            char targetRank = algebraicMove[algebraicMove.Length - 1];

                            if (BoardRepresentation.fileNames.IndexOf(targetFile) == toCoord.fileIndex)                              // correct ending file
                            {
                                if (targetRank.ToString() == (toCoord.rankIndex + 1).ToString())                                     // correct ending rank
                                {
                                    break;
                                }
                            }
                        }
                    }
                }
                else                     // regular piece move

                {
                    char movePieceChar = algebraicMove[0];
                    if (GetPieceTypeFromSymbol(movePieceChar) != movePieceType)
                    {
                        continue;                         // skip this move, incorrect move piece type
                    }

                    char targetFile = algebraicMove[algebraicMove.Length - 2];
                    char targetRank = algebraicMove[algebraicMove.Length - 1];
                    if (BoardRepresentation.fileNames.IndexOf(targetFile) == toCoord.fileIndex)                      // correct ending file
                    {
                        if (targetRank.ToString() == (toCoord.rankIndex + 1).ToString())                             // correct ending rank

                        {
                            if (algebraicMove.Length == 4)                               // addition char present for disambiguation (e.g. Nbd7 or R7e2)
                            {
                                char disambiguationChar = algebraicMove[1];

                                if (BoardRepresentation.fileNames.Contains(disambiguationChar.ToString()))                                     // is file disambiguation
                                {
                                    if (BoardRepresentation.fileNames.IndexOf(disambiguationChar) != fromCoord.fileIndex)                      // incorrect starting file
                                    {
                                        continue;
                                    }
                                }
                                else                                                                           // is rank disambiguation
                                {
                                    if (disambiguationChar.ToString() != (fromCoord.rankIndex + 1).ToString()) // incorrect starting rank
                                    {
                                        continue;
                                    }
                                }
                            }
                            break;
                        }
                    }
                }
            }
            return(move);
        }