Exemplo n.º 1
0
        private static void PopulateKingCastlingMoves(
            ICollection <GameMoveData2> resultMoves,
            Square sourceSquare,
            Bitboard target,
            CastlingOptions allowedCastlingOptions,
            Bitboard nonEmptySquares,
            CastlingType castlingType)
        {
            var option = castlingType.ToOption();

            if ((allowedCastlingOptions & option) == 0)
            {
                return;
            }

            var info = KingCastlingInfos[GetCastlingTypeArrayIndexInternal(castlingType)];

            if (info.KingMove.From != sourceSquare || (info.ExpectedEmptySquares & nonEmptySquares).IsAny ||
                (info.KingMove.To.Bitboard & target).IsNone)
            {
                return;
            }

            var moveData = new GameMoveData2(info.KingMove, GameMoveFlags.IsKingCastling);

            resultMoves.Add(moveData);
        }
Exemplo n.º 2
0
        public static long GetCastlingHash(CastlingOptions castlingOptions)
        {
            var result = 0L;

            if (castlingOptions.HasFlag(CastlingOptions.WhiteKingSide))
            {
                result ^= GetCastlingHash(CastlingType.WhiteKingSide);
            }

            if (castlingOptions.HasFlag(CastlingOptions.WhiteQueenSide))
            {
                result ^= GetCastlingHash(CastlingType.WhiteQueenSide);
            }

            if (castlingOptions.HasFlag(CastlingOptions.BlackKingSide))
            {
                result ^= GetCastlingHash(CastlingType.BlackKingSide);
            }

            if (castlingOptions.HasFlag(CastlingOptions.BlackQueenSide))
            {
                result ^= GetCastlingHash(CastlingType.BlackQueenSide);
            }

            return(result);
        }
Exemplo n.º 3
0
        private StandardGamePosition(
            [NotNull] PiecePosition piecePosition,
            GameSide activeSide,
            int fullMoveIndex,
            CastlingOptions castlingOptions,
            EnPassantCaptureInfo2?enPassantCaptureInfo,
            int halfMoveCountBy50MoveRule)
            : base(piecePosition, activeSide, fullMoveIndex)
        {
            if (piecePosition is null)
            {
                throw new ArgumentNullException(nameof(piecePosition));
            }

            if (halfMoveCountBy50MoveRule < 0)
            {
                throw new ArgumentOutOfRangeException(
                          nameof(halfMoveCountBy50MoveRule),
                          halfMoveCountBy50MoveRule,
                          @"The value cannot be negative.");
            }

            CastlingOptions           = castlingOptions;
            EnPassantCaptureInfo      = enPassantCaptureInfo;
            HalfMoveCountBy50MoveRule = halfMoveCountBy50MoveRule;

            //// TODO [HarinezumiSama] IMPORTANT: This Zobrist key algorithm is different from the one used in the Polyglot opening book (in the en-passant part)
            ZobristKey = PiecePosition.ZobristKey
                         ^ ZobristHashHelper.GetCastlingHash(CastlingOptions)
                         ^ ZobristHashHelper.GetEnPassantHash(
                EnPassantCaptureInfo,
                PiecePosition[ActiveSide.ToPiece(PieceType.Pawn)])
                         ^ ZobristHashHelper.GetTurnHash(ActiveSide);
        }
Exemplo n.º 4
0
        protected void GeneratePotentialKingMoves(
            [NotNull] ICollection <GameMoveData2> resultMoves,
            GameSide side,
            GeneratedMoveTypes moveTypes,
            Bitboard target,
            CastlingOptions allowedCastlingOptions)
        {
            if (resultMoves is null)
            {
                throw new ArgumentNullException(nameof(resultMoves));
            }

            var kingPiece = side.ToPiece(PieceType.King);
            var kings     = PiecePosition[kingPiece];

            while (kings.IsAny)
            {
                var kingSquareIndex = Bitboard.PopFirstSquareIndex(ref kings);

                var sourceSquare  = new Square(kingSquareIndex);
                var moves         = KingAttacksOrMoves[kingSquareIndex];
                var movesOnTarget = moves & target;

                if (moveTypes.IsAnySet(GeneratedMoveTypes.Capture))
                {
                    var enemies  = PiecePosition[side.Invert()];
                    var captures = movesOnTarget & enemies;
                    PopulateSimpleMoves(resultMoves, sourceSquare, captures, GameMoveFlags.IsRegularCapture);
                }

                //// ReSharper disable once InvertIf
                if (moveTypes.IsAnySet(GeneratedMoveTypes.Quiet))
                {
                    var emptySquares = PiecePosition[Piece.None];
                    var nonCaptures  = movesOnTarget & emptySquares;
                    PopulateSimpleMoves(resultMoves, sourceSquare, nonCaptures, GameMoveFlags.None);

                    var nonEmptySquares = ~emptySquares;

                    PopulateKingCastlingMoves(
                        resultMoves,
                        sourceSquare,
                        target,
                        allowedCastlingOptions,
                        nonEmptySquares,
                        CastlingSide.KingSide.ToCastlingType(side));

                    PopulateKingCastlingMoves(
                        resultMoves,
                        sourceSquare,
                        target,
                        allowedCastlingOptions,
                        nonEmptySquares,
                        CastlingSide.QueenSide.ToCastlingType(side));
                }
            }
        }
Exemplo n.º 5
0
        public void GenerateKingMoves(
            [NotNull] ICollection <GameMoveData> resultMoves,
            GameSide side,
            CastlingOptions allowedCastlingOptions,
            Bitboard target)
        {
            if (resultMoves is null)
            {
                throw new ArgumentNullException(nameof(resultMoves));
            }

            var kingPiece = side.ToPiece(PieceType.King);
            var king      = PiecePosition[kingPiece];

            if (king.IsNone)
            {
                return;
            }

            if (!king.IsExactlyOneSquare())
            {
                throw new ChessPlatformException(
                          $@"There are multiple {kingPiece.GetDescription()} pieces ({king.GetSquareCount()}) on the board.");
            }

            var kingSquareIndex = king.FindFirstSquareIndex();
            var sourceSquare    = new Square(kingSquareIndex);
            var directTargets   = KingAttacksOrMoves[kingSquareIndex] & target;

            var emptySquares = PiecePosition[Piece.None];
            var nonCaptures  = directTargets & emptySquares;

            PopulateSimpleMoves(resultMoves, sourceSquare, nonCaptures, GameMoveFlags.None);

            var enemies  = PiecePosition[side.Invert()];
            var captures = directTargets & enemies;

            PopulateSimpleMoves(resultMoves, sourceSquare, captures, GameMoveFlags.IsRegularCapture);

            var nonEmptySquares = ~emptySquares;

            PopulateKingCastlingMoves(
                resultMoves,
                sourceSquare,
                allowedCastlingOptions,
                nonEmptySquares,
                CastlingSide.KingSide.ToCastlingType(side));

            PopulateKingCastlingMoves(
                resultMoves,
                sourceSquare,
                allowedCastlingOptions,
                nonEmptySquares,
                CastlingSide.QueenSide.ToCastlingType(side));
        }
        protected static void AssertBaseProperties(
            [NotNull] GameBoard gameBoard,
            GameSide expectedActiveSide,
            CastlingOptions expectedCastlingOptions,
            EnPassantCaptureInfo expectedEnPassantCaptureInfo,
            int expectedHalfMoveCountBy50MoveRule,
            int expectedFullMoveIndex,
            GameState expectedGameState,
            AutoDrawType autoDrawType = AutoDrawType.None)
        {
            Assert.That(gameBoard, Is.Not.Null);

            Assert.That(gameBoard.ActiveSide, Is.EqualTo(expectedActiveSide));
            Assert.That(gameBoard.CastlingOptions, Is.EqualTo(expectedCastlingOptions));
            AssertEnPassantCaptureInfo(gameBoard.EnPassantCaptureInfo, expectedEnPassantCaptureInfo);
            Assert.That(gameBoard.FullMoveIndex, Is.EqualTo(expectedFullMoveIndex));
            Assert.That(gameBoard.HalfMoveCountBy50MoveRule, Is.EqualTo(expectedHalfMoveCountBy50MoveRule));
            Assert.That(gameBoard.FullMoveCountBy50MoveRule, Is.EqualTo(expectedHalfMoveCountBy50MoveRule / 2));
            Assert.That(gameBoard.State, Is.EqualTo(expectedGameState));
            Assert.That(gameBoard.GetAutoDrawType(), Is.EqualTo(autoDrawType));
        }
Exemplo n.º 7
0
        public static string GetFenSnippet(this CastlingOptions castlingOptions)
        {
            if (castlingOptions == CastlingOptions.None)
            {
                return(ChessConstants.NoneCastlingOptionsFenSnippet);
            }

            var resultBuilder = new StringBuilder(4);

            foreach (var option in ChessConstants.FenRelatedCastlingOptions)
            {
                //// ReSharper disable once InvertIf
                if (castlingOptions.IsAnySet(option))
                {
                    var fenChar = ChessConstants.CastlingOptionToFenCharMap[option];
                    resultBuilder.Append(fenChar);
                }
            }

            return(resultBuilder.ToString());
        }
Exemplo n.º 8
0
        /// <summary>
        /// Pass on both the starting rank and file and the ending, we don't want to do board analysis to determine the start.
        /// But we always need to do some board analysis, so pass on the board anyway.
        /// </summary>
        /// <param name="fullNotation">The position of the characters determine their meaning:
        /// 1. Starting file, noted by a letter a to f.
        /// 2. Starting rank, noted by a number 1 to 8.
        /// 3. An x if a piece was struck, nothing if there was no strike.
        /// 4. Ending file, noted by a letter a to f.
        /// 5. Ending rank, noted by a number 1 to 8.
        /// 6. In case of castling use another move constructor.
        /// 7. In case the king is checked add a +, if it's mated a #</param>
        public Move(string fullNotation, Board board)
        {
            if (fullNotation.Contains("0-0-0"))
            {
                this.HasCastled = board.WhiteToMove ? CastlingOptions.WhiteQueenSide : CastlingOptions.BlackQueenSide;
            }
            else if (fullNotation.Contains("0-0"))
            {
                this.HasCastled = board.WhiteToMove ? CastlingOptions.WhiteKingSide : CastlingOptions.BlackKingSide;
            }
            else
            {
                var notationIndex = 0;

                this.Letter = char.IsUpper(fullNotation[notationIndex]) ? fullNotation[notationIndex++] : ' ';

                this.StartFile = (byte)(fullNotation[notationIndex++] - Constants.FileLetterOffset);
                this.StartRank = (byte)(fullNotation[notationIndex++] - Constants.RankNumberOffset - 1);

                var startField = board.Fields[this.StartFile, this.StartRank];

                if (startField.Piece == null)
                {
                    throw new Exception("Invalid argument, no piece found at given location, file: " + this.StartFile + ", rank: " + this.StartRank + 1);
                }

                this.White = startField.Piece.White;

                if (fullNotation[2] == 'x')
                {
                    this.Struck = true;
                    notationIndex++;
                }

                this.EndFile = (byte)(fullNotation[notationIndex++] - Constants.FileLetterOffset);
                this.EndRank = (byte)(fullNotation[notationIndex++] - Constants.RankNumberOffset);
            }
        }
Exemplo n.º 9
0
        internal MakeMoveData MakeMove(
            [NotNull] GameMove move,
            GameSide movingSide,
            [CanBeNull] EnPassantCaptureInfo enPassantCaptureInfo,
            ref CastlingOptions castlingOptions)
        {
            if (move is null)
            {
                throw new ArgumentNullException(nameof(move));
            }

            var piece = PiecePosition[move.From];

            if (piece == Piece.None || piece.GetSide() != movingSide)
            {
                throw new ArgumentException($@"Invalid move '{move}' in the position.", nameof(move));
            }

            GameMove castlingRookMove             = null;
            Square?  enPassantCapturedPieceSquare = null;

            var movingSideAllCastlingOptions = ChessHelper.GameSideToCastlingOptionsMap[movingSide];

            // Performing checks before actual move!
            var castlingInfo       = CheckCastlingMove(move);
            var isEnPassantCapture = IsEnPassantCapture(move.From, move.To, enPassantCaptureInfo);
            var isPawnPromotion    = IsPawnPromotion(move.From, move.To);

            var moveData      = MovePieceInternal(move);
            var capturedPiece = moveData.CapturedPiece;

            if (isEnPassantCapture)
            {
                if (enPassantCaptureInfo is null)
                {
                    throw ChessPlatformException.CreateInconsistentStateError();
                }

                enPassantCapturedPieceSquare = enPassantCaptureInfo.TargetPieceSquare;
                capturedPiece = PiecePosition.SetPiece(enPassantCaptureInfo.TargetPieceSquare, Piece.None);
                if (capturedPiece.GetPieceType() != PieceType.Pawn)
                {
                    throw ChessPlatformException.CreateInconsistentStateError();
                }
            }
            else if (isPawnPromotion)
            {
                if (move.PromotionResult == PieceType.None)
                {
                    throw new ChessPlatformException($@"Promoted piece type is not specified ({move}).");
                }

                var previousPiece = PiecePosition.SetPiece(move.To, move.PromotionResult.ToPiece(movingSide));
                if (previousPiece.GetPieceType() != PieceType.Pawn)
                {
                    throw ChessPlatformException.CreateInconsistentStateError();
                }
            }
            else if (castlingInfo != null)
            {
                if (!castlingOptions.IsAllSet(castlingInfo.Option))
                {
                    throw new ChessPlatformException(
                              $@"The castling {{{move}}} ({castlingInfo.CastlingType.GetName()}) is not allowed.");
                }

                castlingRookMove = castlingInfo.RookMove;
                var rookMoveData = MovePieceInternal(castlingRookMove);
                if (rookMoveData.CapturedPiece != Piece.None)
                {
                    throw ChessPlatformException.CreateInconsistentStateError();
                }

                castlingOptions &= ~movingSideAllCastlingOptions;
            }

            var movingSideCurrentCastlingOptions = castlingOptions & movingSideAllCastlingOptions;

            if (movingSideCurrentCastlingOptions != CastlingOptions.None)
            {
                switch (piece.GetPieceType())
                {
                case PieceType.King:
                    castlingOptions &= ~movingSideAllCastlingOptions;
                    break;

                case PieceType.Rook:
                {
                    var castlingInfoByRook =
                        ChessConstants.AllCastlingInfos.SingleOrDefault(obj => obj.RookMove.From == move.From);

                    if (castlingInfoByRook != null)
                    {
                        castlingOptions &= ~castlingInfoByRook.Option;
                    }
                }

                break;
                }
            }

            var oppositeSide = movingSide.Invert();
            var oppositeSideAllCastlingOptions     = ChessHelper.GameSideToCastlingOptionsMap[oppositeSide];
            var oppositeSideCurrentCastlingOptions = castlingOptions & oppositeSideAllCastlingOptions;

            if (oppositeSideCurrentCastlingOptions != CastlingOptions.None &&
                capturedPiece.GetPieceType() == PieceType.Rook)
            {
                var oppositeCastlingInfo =
                    ChessConstants.AllCastlingInfos.SingleOrDefault(obj => obj.RookMove.From == move.To);

                if (oppositeCastlingInfo != null)
                {
                    castlingOptions &= ~oppositeCastlingInfo.Option;
                }
            }

            var undoMoveData = new MakeMoveData(
                move,
                moveData.MovedPiece,
                capturedPiece,
                castlingRookMove,
                enPassantCapturedPieceSquare);

            _undoMoveDatas.Push(undoMoveData);

            PiecePosition.EnsureConsistency();

            return(undoMoveData);
        }
Exemplo n.º 10
0
        public Square[] GetPotentialMoveSquares(
            CastlingOptions castlingOptions,
            [CanBeNull] EnPassantCaptureInfo enPassantCaptureInfo,
            Square sourceSquare)
        {
            var piece     = PiecePosition[sourceSquare];
            var pieceType = piece.GetPieceType();

            if (pieceType == PieceType.None)
            {
                throw new ArgumentException(
                          $@"No piece at the source square '{sourceSquare}'.",
                          nameof(sourceSquare));
            }

            var pieceSide = piece.GetSide().EnsureNotNull();

            switch (pieceType)
            {
            case PieceType.Knight:
                //// TODO [HarinezumiSama] Use bitboard instead of squares
                var result = ChessHelper.GetKnightMoveSquares(sourceSquare)
                             .Where(square => PiecePosition[square].GetSide() != pieceSide)
                             .ToArray();

                return(result);

            case PieceType.King:
            case PieceType.Pawn:
                throw new InvalidOperationException("MUST NOT go into this branch anymore.");

            case PieceType.None:
                throw new ArgumentException(
                          $@"No piece at the source square '{sourceSquare}'.",
                          nameof(sourceSquare));

            case PieceType.Bishop:
            case PieceType.Rook:
            case PieceType.Queen:
                // Just go ahead
                break;

            default:
                throw pieceType.CreateEnumValueNotImplementedException();
            }

            var resultList = new List <Square>();

            if (pieceType.IsSlidingStraight())
            {
                GetPotentialMoveSquaresByRays(
                    sourceSquare,
                    pieceSide,
                    ChessHelper.StraightRays,
                    ChessHelper.MaxSlidingPieceDistance,
                    true,
                    resultList);
            }

            if (pieceType.IsSlidingDiagonally())
            {
                GetPotentialMoveSquaresByRays(
                    sourceSquare,
                    pieceSide,
                    ChessHelper.DiagonalRays,
                    ChessHelper.MaxSlidingPieceDistance,
                    true,
                    resultList);
            }

            return(resultList.ToArray());
        }