예제 #1
0
파일: Game.cs 프로젝트: stevenaw/chess-app
        private ErrorCondition Move(Move move)
        {
            if (!BitTranslator.IsValidSquare(move.StartFile, move.StartRank))
            {
                return(ErrorCondition.InvalidSquare);
            }
            if (!BitTranslator.IsValidSquare(move.EndFile, move.EndRank))
            {
                return(ErrorCondition.InvalidSquare);
            }

            ulong startSquare = BitTranslator.TranslateToBit(move.StartFile, move.StartRank);

            if ((CurrentTurn & startSquare) == 0)
            {
                return(ErrorCondition.MustMoveOwnPiece); // Can't move if not your turn
            }
            ulong endSquare = BitTranslator.TranslateToBit(move.EndFile, move.EndRank);

            if ((CurrentTurn & endSquare) != 0)
            {
                return(ErrorCondition.CantTakeOwnPiece); // Can't end move on own piece
            }
            ulong allMoves = MoveGenerator.GenerateValidMovesForPiece(CurrentState, startSquare);

            if ((endSquare & allMoves) == 0)
            {
                return(ErrorCondition.InvalidMovement); // End square is not a valid move
            }
            // The move is good, so update state
            // Update current state
            UpdateState(move, startSquare, endSquare);

            return(ErrorCondition.None);
        }
예제 #2
0
        public static GameState ApplyMove(GameState state, ulong startSq, ulong endSq)
        {
            var start = BitTranslator.TranslateToSquare(startSq);
            var end   = BitTranslator.TranslateToSquare(endSq);
            var move  = new Move(start.File, start.Rank, end.File, end.Rank);

            return(ApplyMove(state, move, startSq, endSq));
        }
예제 #3
0
파일: Game.cs 프로젝트: stevenaw/chess-app
        public SquareContents GetSquareContents(char file, int rank)
        {
            var result = (SquareContents)0;

            if (BitTranslator.IsValidSquare(file, rank))
            {
                ulong bit = BitTranslator.TranslateToBit(file, rank);
                result = CurrentState.Board.GetSquareContents(bit);
            }

            return(result);
        }
예제 #4
0
파일: Game.cs 프로젝트: stevenaw/chess-app
        public List <Square> GetValidMoves(char file, int rank)
        {
            if (!BitTranslator.IsValidSquare(file, rank))
            {
                throw new InvalidOperationException();
            }

            var   square   = BitTranslator.TranslateToBit(file, rank);
            ulong allMoves = MoveGenerator.GenerateValidMovesForPiece(CurrentState, square);

            return(BitTranslator.TranslateToSquares(allMoves));
        }
예제 #5
0
        private static bool TryInferStartSquare(BoardState state, ulong piecesOnCurrentSide, bool isWhiteMove, bool isCapture, char pieceDesignation, ref ParsedMoveState result)
        {
            var endBit           = BitTranslator.TranslateToBit(result.EndFile, result.EndRank);
            var disambiguityMask = BuildDisambiguityMask(result);

            switch (pieceDesignation)
            {
            case Constants.PieceNotation.King:
            {
                var possibleStartBits = MoveGenerator.GetKingMovements(endBit);
                var existingOfPiece   = state.Kings & piecesOnCurrentSide;
                var actualStartPiece  = possibleStartBits & existingOfPiece;

                if (disambiguityMask != 0)
                {
                    actualStartPiece &= disambiguityMask;
                }

                if (actualStartPiece == 0)
                {
                    return(false);
                }

                var startSquare = BitTranslator.TranslateToSquare(actualStartPiece);

                result.StartFile = startSquare.File;
                result.StartRank = startSquare.Rank;
                return(true);
            }

            case Constants.PieceNotation.Knight:
            {
                var possibleStartBits = MoveGenerator.GetKnightMovements(endBit);
                var existingOfPiece   = state.Knights & piecesOnCurrentSide;
                var actualStartPiece  = possibleStartBits & existingOfPiece;

                if (disambiguityMask != 0)
                {
                    actualStartPiece &= disambiguityMask;
                }

                if (actualStartPiece == 0)
                {
                    return(false);
                }

                var startSquare = BitTranslator.TranslateToSquare(actualStartPiece);

                result.StartFile = startSquare.File;
                result.StartRank = startSquare.Rank;
                return(true);
            }

            case Constants.PieceNotation.Queen:
            {
                var possibleStartBits = MoveGenerator.GetQueenMovements(endBit, state);
                var existingOfPiece   = state.Queens & piecesOnCurrentSide;
                var actualStartPiece  = possibleStartBits & existingOfPiece;

                if (disambiguityMask != 0)
                {
                    actualStartPiece &= disambiguityMask;
                }

                if (actualStartPiece == 0)
                {
                    return(false);
                }

                var startSquare = BitTranslator.TranslateToSquare(actualStartPiece);

                result.StartFile = startSquare.File;
                result.StartRank = startSquare.Rank;
                return(true);
            }

            case Constants.PieceNotation.Rook:
            {
                var possibleStartBits = MoveGenerator.GetRookMovements(endBit, state);
                var existingOfPiece   = state.Rooks & piecesOnCurrentSide;
                var actualStartPiece  = possibleStartBits & existingOfPiece;

                if (disambiguityMask != 0)
                {
                    actualStartPiece &= disambiguityMask;
                }

                if (actualStartPiece == 0)
                {
                    return(false);
                }

                var startSquare = BitTranslator.TranslateToSquare(actualStartPiece);

                result.StartFile = startSquare.File;
                result.StartRank = startSquare.Rank;
                return(true);
            }

            case Constants.PieceNotation.Bishop:
            {
                var possibleStartBits = MoveGenerator.GetBishopMovements(endBit, state);
                var existingOfPiece   = state.Bishops & piecesOnCurrentSide;
                var actualStartPiece  = possibleStartBits & existingOfPiece;

                if (disambiguityMask != 0)
                {
                    actualStartPiece &= disambiguityMask;
                }

                if (actualStartPiece == 0)
                {
                    return(false);
                }

                var startSquare = BitTranslator.TranslateToSquare(actualStartPiece);

                result.StartFile = startSquare.File;
                result.StartRank = startSquare.Rank;
                return(true);
            }

            case Constants.PieceNotation.Pawn:
            {
                ulong actualStartPiece;
                if (isWhiteMove)
                {
                    var possibleMoves   = endBit >> 8 | endBit >> 16;
                    var possibleAttacks = (endBit >> 7 & (~MoveGenerator.Rank8)) | (endBit >> 9 & (~MoveGenerator.Rank1));
                    var possibleStart   = isCapture ? possibleAttacks : possibleMoves;
                    var pawns           = state.Pawns & state.WhitePieces;
                    actualStartPiece = pawns & possibleStart;
                }
                else
                {
                    var possibleMoves   = endBit << 8 | endBit << 16;
                    var possibleAttacks = (endBit << 7 & (~MoveGenerator.Rank1)) | (endBit << 9 & (~MoveGenerator.Rank8));
                    var possibleStart   = isCapture ? possibleAttacks : possibleMoves;
                    var pawns           = state.Pawns & state.BlackPieces;
                    actualStartPiece = pawns & possibleStart;
                }

                if (disambiguityMask != 0)
                {
                    actualStartPiece &= disambiguityMask;
                }

                if (actualStartPiece == 0)
                {
                    return(false);
                }

                var startSquare = BitTranslator.TranslateToSquare(actualStartPiece);

                result.StartFile = startSquare.File;
                result.StartRank = startSquare.Rank;
                return(true);
            }

            default:
                return(false);
            }
        }
예제 #6
0
        public static string ToMoveString(Move move, BoardState board, AttackState attackState)
        {
            // Algo: Move from end, adding tokens in order
            // - Check if move is promotion, add tokens if so
            // - Add end square
            // - Check if move is capture, output 'x' if so
            // - Then check if multiple pieces of same type can move to end square, then output first rank or file of moved piece
            // - Then output piece notation if piece is not a pawn
            //
            // - Now that the move string is built, splice to only used tokens


            var startSquareBit = BitTranslator.TranslateToBit(move.StartFile, move.StartRank);
            var endSquareBit   = BitTranslator.TranslateToBit(move.EndFile, move.EndRank);
            var movedPiece     = board.GetSquareContents(startSquareBit) & ~SquareContents.Colours;

            Span <char> buffer  = stackalloc char[8];
            var         lastIdx = buffer.Length - 1;


            if (attackState != AttackState.None)
            {
                // Check if move ended in attack
                var notation = GetAttackStateMoveNotation(attackState);
                if (notation != 0)
                {
                    buffer[lastIdx--] = notation;
                }
            }

            if (movedPiece == SquareContents.King)
            {
                // Check for castling
                var squareDiff     = (decimal)startSquareBit / endSquareBit;
                var castleNotation = "";

                if (squareDiff == 4) // King has moved 2 squares horizontally towards queenside
                {
                    castleNotation = "0-0-0";
                }
                else if (squareDiff == 0.25M) // King has moved 2 squares horizontally towards kingside
                {
                    castleNotation = "0-0";
                }

                if (!string.IsNullOrEmpty(castleNotation))
                {
                    var copyIdx        = lastIdx - castleNotation.Length + 1;
                    var notationBuffer = buffer.Slice(copyIdx, castleNotation.Length);
                    castleNotation.AsSpan().CopyTo(notationBuffer);
                    lastIdx -= castleNotation.Length;

                    var copyLocation = buffer.Slice(lastIdx + 1);
                    return(copyLocation.ToString());
                }
            }

            if (move.PromotedPiece != SquareContents.Empty)
            {
                var pieceType = move.PromotedPiece & ~SquareContents.Colours;

                buffer[lastIdx--] = PiecesAsLetters[pieceType];
                buffer[lastIdx--] = '=';
            }

            buffer[lastIdx--] = (char)(move.EndRank + '0');
            buffer[lastIdx--] = move.EndFile;

            var isCapture = (endSquareBit & board.AllPieces) != 0;

            if (!isCapture && movedPiece == SquareContents.Pawn)
            {
                // Capture pattern for pawn is diagonal movement
                // Explicitly checking will account for capture by en passant where end square is empty
                var diff = Math.Max(startSquareBit, endSquareBit) / Math.Min(startSquareBit, endSquareBit);
                isCapture = diff == 1 << 7 || diff == 1 << 9;
            }

            if (isCapture)
            {
                buffer[lastIdx--] = 'x';
            }

            if (movedPiece == SquareContents.Pawn)
            {
                if (isCapture)
                {
                    buffer[lastIdx--] = move.StartFile;
                }
            }
            else
            {
                if (movedPiece != SquareContents.King)
                {
                    // Check for disambiguation
                    var colorMask = (startSquareBit & board.WhitePieces) != 0 ? board.WhitePieces : board.BlackPieces;
                    var pieceMask = 0UL;

                    if (movedPiece == SquareContents.Knight)
                    {
                        pieceMask = board.Knights;
                    }
                    else if (movedPiece == SquareContents.Bishop)
                    {
                        pieceMask = board.Bishops;
                    }
                    else if (movedPiece == SquareContents.Rook)
                    {
                        pieceMask = board.Rooks;
                    }
                    else if (movedPiece == SquareContents.Queen)
                    {
                        pieceMask = board.Queens;
                    }

                    pieceMask = pieceMask & colorMask & ~startSquareBit;

                    var otherPossibleStartSquares = new List <Square>();
                    for (var i = 0; i < 64; i++)
                    {
                        var mask = pieceMask & (1UL << i);
                        if (mask != 0)
                        {
                            var moves = MoveGenerator.GenerateStatelessMovesForPiece(board, mask);
                            if ((moves & endSquareBit) != 0)
                            {
                                otherPossibleStartSquares.Add(BitTranslator.TranslateToSquare(mask));
                            }
                        }
                    }

                    // There may be multiple possible start squares to disambiguate from
                    var disambiguateByRank = false;
                    var disambiguateByFile = false;
                    foreach (var otherSquare in otherPossibleStartSquares)
                    {
                        disambiguateByRank = disambiguateByRank || (otherSquare.File == move.StartFile);
                        disambiguateByFile = disambiguateByFile || (otherSquare.Rank == move.StartRank);
                    }

                    // Need to disambiguate, but both rank + file are different. Default to by file.
                    if (!disambiguateByFile && !disambiguateByRank && otherPossibleStartSquares.Count > 0)
                    {
                        disambiguateByFile = true;
                    }

                    if (disambiguateByRank)
                    {
                        buffer[lastIdx--] = (char)(move.StartRank + '0');
                    }
                    if (disambiguateByFile)
                    {
                        buffer[lastIdx--] = move.StartFile;
                    }
                }

                buffer[lastIdx--] = PiecesAsLetters[movedPiece];
            }

            var segment = buffer.Slice(lastIdx + 1);

            return(segment.ToString());
        }
예제 #7
0
        public static GameState ApplyMove(GameState state, Move move, ulong startSquare, ulong endSquare)
        {
            var history      = state.PossibleRepeatedHistory;
            var resetHistory = (
                ((state.Board.AllPieces & endSquare) != 0) ||      // regular capture
                ((state.Board.Pawns & startSquare) != 0)           // pawn movement
                );

            if (resetHistory)
            {
                history = history.Clear();
            }


            var newBoard = state.Board.MovePiece(startSquare, endSquare);

            if ((startSquare & state.Board.Pawns) != 0)
            {
                if (endSquare == state.EnPassantSquare)
                {
                    var opponentPawn = BitTranslator.TranslateToBit(move.EndFile, move.StartRank);
                    newBoard = BoardStateMutator.ClearPiece(newBoard, opponentPawn);
                }
                else if (move.PromotedPiece != SquareContents.Empty)
                {
                    newBoard = newBoard.ClearPiece(endSquare).SetPiece(endSquare, move.PromotedPiece);
                }
            }
            else if ((startSquare & state.Board.Kings) != 0)
            {
                // Castling
                if (Math.Abs(move.EndFile - move.StartFile) == 2)
                {
                    var rookDestinationRelativeKing = Math.Sign(move.StartFile - move.EndFile);
                    var rookDestinationFile         = (char)(move.EndFile + rookDestinationRelativeKing);
                    var rookDestinationBit          = BitTranslator.TranslateToBit(rookDestinationFile, move.EndRank);

                    var rookOriginFile = rookDestinationRelativeKing > 0 ? 'a' : 'h';
                    var rookOriginBit  = BitTranslator.TranslateToBit(rookOriginFile, move.EndRank);

                    newBoard = BoardStateMutator.MovePiece(newBoard, rookOriginBit, rookDestinationBit);
                }
            }


            var newPiecesStillOnStartSquare = state.PiecesOnStartSquares & ~(startSquare | endSquare);
            var castlingPiecesUnmoved       = newPiecesStillOnStartSquare & MoveGenerator.StartingKingsAndRooks;

            var enPassantSquare = 0UL;

            if ((startSquare & state.Board.Pawns) != 0)
            {
                if (startSquare >> 16 == endSquare)
                {
                    enPassantSquare = startSquare >> 8;
                }
                else if (startSquare << 16 == endSquare)
                {
                    enPassantSquare = startSquare << 8;
                }
            }

            history = history.Push((newBoard, castlingPiecesUnmoved));

            return(new GameState(newBoard, move, history, state.AttackState, newPiecesStillOnStartSquare, enPassantSquare, state.SquaresAttackedBy));
        }