Esempio n. 1
0
    public string ToFen()
    {
        var stringBuilder = new StringBuilder();
        // Position
        var x = 0;
        var unoccupiedSquares = 0;

        for (var square = Square.A8; square < Square.Illegal; square++)
        {
            x++;
            if (x == 9)
            {
                stringBuilder.Append("/");
                x = 1;
            }
            var piece = GetPiece(square);
            if (piece == Piece.None)
            {
                // Unoccupied Square
                unoccupiedSquares++;
                if (x == 8)
                {
                    // Last File
                    // Display count of unoccupied squares.
                    stringBuilder.Append(unoccupiedSquares);
                    unoccupiedSquares = 0;
                }
            }
            else
            {
                // Occupied Square
                if (unoccupiedSquares > 0)
                {
                    // Display count of unoccupied squares.
                    stringBuilder.Append(unoccupiedSquares);
                    unoccupiedSquares = 0;
                }
                stringBuilder.Append(PieceHelper.GetChar(piece));
            }
        }
        // Display side to move, castling rights, en passant square, ply, and full move number.
        stringBuilder.Append(" ");
        stringBuilder.Append(ColorToMove == Color.White ? "w" : "b");
        stringBuilder.Append(" ");
        stringBuilder.Append(Game.Castling.ToString(Castling));
        stringBuilder.Append(" ");
        stringBuilder.Append(EnPassantSquare == Square.Illegal ? "-" : Board.SquareLocations[(int)EnPassantSquare]);
        stringBuilder.Append(" ");
        stringBuilder.Append(PlySinceCaptureOrPawnMove);
        stringBuilder.Append(" ");
        stringBuilder.Append(FullMoveNumber);
        return(stringBuilder.ToString());
    }
Esempio n. 2
0
    public static ulong ParseStandardAlgebraic(Board board, string standardAlgebraic)
    {
        var move = Null;
        // Remove check and checkmate symbols.
        var standardAlgebraicNoCheck = standardAlgebraic.TrimEnd("+#".ToCharArray());

        // ReSharper disable once SwitchStatementMissingSomeCases
        switch (standardAlgebraicNoCheck)
        {
        case "O-O-O":
        case "0-0-0":
            // Castle Queenside
            SetFrom(ref move, Board.CastleFromSquares[(int)board.CurrentPosition.ColorToMove]);
            SetTo(ref move, Board.CastleToSquares[(int)board.CurrentPosition.ColorToMove][(int)BoardSide.Queen]);
            if (!board.ValidateMove(ref move))
            {
                throw new Exception($"Move {standardAlgebraic} is illegal in position {board.CurrentPosition.ToFen()}.");
            }
            return(move);

        case "O-O":
        case "0-0":
            // Castle Kingside
            SetFrom(ref move, Board.CastleFromSquares[(int)board.CurrentPosition.ColorToMove]);
            SetTo(ref move, Board.CastleToSquares[(int)board.CurrentPosition.ColorToMove][(int)BoardSide.King]);
            if (!board.ValidateMove(ref move))
            {
                throw new Exception($"Move {standardAlgebraic} is illegal in position {board.CurrentPosition.ToFen()}.");
            }
            return(move);
        }
        var    length        = standardAlgebraicNoCheck.Length;
        var    fromFile      = -1;
        var    fromRank      = -1;
        var    promotedPiece = Game.Piece.None;
        Piece  piece;
        Square toSquare;

        if (char.IsLower(standardAlgebraicNoCheck, 0))
        {
            // Pawn Move
            piece    = PieceHelper.GetPieceOfColor(ColorlessPiece.Pawn, board.CurrentPosition.ColorToMove);
            fromFile = Board.Files[(int)Board.GetSquare($"{standardAlgebraicNoCheck[0]}1")];
            switch (length)
            {
            case 2:
                // Pawn Move
                toSquare = Board.GetSquare(standardAlgebraicNoCheck);
                break;

            case 4 when standardAlgebraicNoCheck[1] == 'x':
Esempio n. 3
0
    public override string ToString()
    {
        // Iterate over the piece array to construct an 8 x 8 text display of the chessboard.
        var stringBuilder = new StringBuilder();

        stringBuilder.AppendLine("  +---+---+---+---+---+---+---+---+");
        var square = Square.A8;

        for (var y = 8; y > 0; y--)
        {
            // Add rank.
            stringBuilder.Append(y);
            stringBuilder.Append(" ");
            for (var x = 1; x <= 8; x++)
            {
                stringBuilder.Append("| ");
                var piece = GetPiece(square);
                if (piece == Piece.None)
                {
                    stringBuilder.Append(" ");
                }
                else
                {
                    stringBuilder.Append(PieceHelper.GetChar(piece));
                }
                stringBuilder.Append(" ");
                square++;
            }
            stringBuilder.AppendLine("|");
            stringBuilder.AppendLine("  +---+---+---+---+---+---+---+---+");
        }
        stringBuilder.Append("  ");
        for (var x = 1; x <= 8; x++)
        {
            // Add file.
            var chrFile = (char)(x + 96);
            stringBuilder.Append($"  {chrFile} ");
        }
        stringBuilder.AppendLine();
        stringBuilder.AppendLine();
        // Display FEN, key, position count, and check.
        stringBuilder.AppendLine($"FEN:             {ToFen()}");
        stringBuilder.AppendLine($"Key:             {Key:X16}");
        stringBuilder.AppendLine($"Position Count:  {_board.GetPositionCount()}");
        stringBuilder.AppendLine($"King in Check:   {(KingInCheck ? "Yes" : "No")}");
        stringBuilder.AppendLine($"Static Score:    {StaticScore}");
        stringBuilder.AppendLine($"Played Move:     {Move.ToString(PlayedMove)}");
        return(stringBuilder.ToString());
    }
Esempio n. 4
0
    public static ulong ParseLongAlgebraic(string longAlgebraic, Color colorToMove)
    {
        var fromSquare = Board.GetSquare(longAlgebraic.Substring(0, 2));
        var toSquare   = Board.GetSquare(longAlgebraic.Substring(2, 2));
        // Set case of promoted piece character based on side to move.
        var promotedPiece = longAlgebraic.Length == 5
            ? PieceHelper.ParseChar(colorToMove == Color.White ? char.ToUpper(longAlgebraic[4]) : char.ToLower(longAlgebraic[4]))
            : Game.Piece.None;
        var move = Null;

        SetFrom(ref move, fromSquare);
        SetTo(ref move, toSquare);
        SetPromotedPiece(ref move, promotedPiece);
        return(move);
    }
Esempio n. 5
0
 private void GeneratePieceMoves(MoveGeneration moveGeneration, ulong fromSquareMask, ulong toSquareMask)
 {
     for (var colorlessPiece = ColorlessPiece.Knight; colorlessPiece <= ColorlessPiece.Queen; colorlessPiece++)
     {
         var attacker = PieceHelper.GetPieceOfColor(colorlessPiece, ColorToMove);
         var pieces   = PieceBitboards[(int)attacker] & fromSquareMask;
         if (pieces == 0)
         {
             continue;
         }
         var    getPieceMovesMask        = Board.PieceMoveMaskDelegates[(int)colorlessPiece];
         var    unOrEnemyOccupiedSquares = ~ColorOccupancy[(int)ColorToMove];
         var    enemyOccupiedSquares     = ColorOccupancy[(int)ColorLastMoved];
         Square fromSquare;
         while ((fromSquare = Bitwise.PopFirstSetSquare(ref pieces)) != Square.Illegal)
         {
             var pieceDestinations = moveGeneration switch
             {
                 MoveGeneration.AllMoves => getPieceMovesMask(fromSquare, Occupancy) & unOrEnemyOccupiedSquares & toSquareMask,
                 MoveGeneration.OnlyCaptures => getPieceMovesMask(fromSquare, Occupancy) & enemyOccupiedSquares & toSquareMask,
                 MoveGeneration.OnlyNonCaptures => getPieceMovesMask(fromSquare, Occupancy) & ~Occupancy & toSquareMask,
                 _ => throw new Exception($"{moveGeneration} move generation not supported.")
             };
             Square toSquare;
             while ((toSquare = Bitwise.PopFirstSetSquare(ref pieceDestinations)) != Square.Illegal)
             {
                 var victim = GetPiece(toSquare);
                 var move   = Move.Null;
                 Move.SetPiece(ref move, attacker);
                 Move.SetFrom(ref move, fromSquare);
                 Move.SetTo(ref move, toSquare);
                 if (victim != Piece.None)
                 {
                     Move.SetCaptureAttacker(ref move, attacker);
                 }
                 Move.SetCaptureVictim(ref move, victim);
                 Moves[MoveIndex] = move;
                 MoveIndex++;
             }
         }
     }
 }
Esempio n. 6
0
    public void FindMagicMultipliers(ColorlessPiece colorlessPiece, Delegates.WriteMessageLine writeMessageLine = null)
    {
        Direction[] directions;
        ulong[]     unoccupiedMoveMasks;
        ulong[]     relevantOccupancyMasks;
        ulong[]     magicMultipliers;
        int[]       shifts;
        ulong[][]   moveMasks;
        // ReSharper disable SwitchStatementHandlesSomeKnownEnumValuesWithDefault
        switch (colorlessPiece)
        {
        case ColorlessPiece.Bishop:
            directions             = new[] { Direction.NorthEast, Direction.SouthEast, Direction.SouthWest, Direction.NorthWest };
            unoccupiedMoveMasks    = Board.BishopMoveMasks;
            relevantOccupancyMasks = _bishopRelevantOccupancyMasks;
            magicMultipliers       = _bishopMagicMultipliers;
            shifts    = _bishopShifts;
            moveMasks = _bishopMoveMasks;
            break;

        case ColorlessPiece.Rook:
            directions             = new[] { Direction.North, Direction.East, Direction.South, Direction.West };
            unoccupiedMoveMasks    = Board.RookMoveMasks;
            relevantOccupancyMasks = _rookRelevantOccupancyMasks;
            magicMultipliers       = _rookMagicMultipliers;
            shifts    = _rookShifts;
            moveMasks = _rookMoveMasks;
            break;

        default:
            throw new ArgumentException($"{colorlessPiece} piece not supported.");
        }
        // ReSharper restore SwitchStatementHandlesSomeKnownEnumValuesWithDefault
        // Generate moves mask on each square.
        var occupancyToMovesMask = new Dictionary <ulong, ulong>();
        var uniqueMovesMasks     = new HashSet <ulong>();

        for (var square = Square.A8; square < Square.Illegal; square++)
        {
            occupancyToMovesMask.Clear();
            uniqueMovesMasks.Clear();
            var moveDestinations         = unoccupiedMoveMasks[(int)square];
            var relevantMoveDestinations = moveDestinations & relevantOccupancyMasks[(int)square];
            var uniqueOccupancies        = (int)Math.Pow(2, Bitwise.CountSetBits(relevantMoveDestinations));
            occupancyToMovesMask.EnsureCapacity(uniqueOccupancies);
            // Generate moves mask for every permutation of occupancy of the relevant destination squares.
            var occupancyPermutations = Bitwise.GetAllPermutations(relevantMoveDestinations);
            for (var occupancyIndex = 0; occupancyIndex < occupancyPermutations.Count; occupancyIndex++)
            {
                var occupancy = occupancyPermutations[occupancyIndex];
                var movesMask = Board.CreateMoveDestinationsMask(square, occupancy, directions);
                occupancyToMovesMask.Add(occupancy, movesMask);
                if (!uniqueMovesMasks.Contains(movesMask))
                {
                    uniqueMovesMasks.Add(movesMask);
                }
            }
            Debug.Assert(occupancyToMovesMask.Count == uniqueOccupancies);
            // Determine bit shift that produces number >= unique occupancies.
            // A stricter condition is number >= unique moves but this requires more computing time to find magic multipliers.
            var shift = 64 - (int)Math.Ceiling(Math.Log(uniqueOccupancies, 2d));
            shifts[(int)square] = shift;
            var magicMultiplier      = magicMultipliers[(int)square];
            var knownMagicMultiplier = magicMultiplier == 0 ? null : (ulong?)magicMultiplier;
            (magicMultipliers[(int)square], moveMasks[(int)square]) = FindMagicMultiplier(occupancyToMovesMask, shift, knownMagicMultiplier);
            writeMessageLine?.Invoke($"{Board.SquareLocations[(int)square],6}  {PieceHelper.GetName(colorlessPiece),6}  {shift,5}  {occupancyToMovesMask.Count,18}  {uniqueMovesMasks.Count,12}  {magicMultipliers[(int)square],16:X16}");
        }
    }
 public void HandlePieceDeselection(GameObject pieceGameObject)
 {
     IsSelected = false;
     PieceHelper.SetDefaultPieceMaterial(pieceGameObject);
     _onBoardMovementLogic.RemoveBacklightFromSquares();
 }
Esempio n. 8
0
    private void GenerateKingMoves(MoveGeneration moveGeneration, ulong fromSquareMask, ulong toSquareMask)
    {
        var king = GetKing(ColorToMove) & fromSquareMask;

        if (king == 0)
        {
            return;
        }
        var   unOrEnemyOccupiedSquares = ~ColorOccupancy[(int)ColorToMove];
        var   enemyOccupiedSquares     = ColorOccupancy[(int)ColorLastMoved];
        var   attacker = PieceHelper.GetPieceOfColor(ColorlessPiece.King, ColorToMove);
        ulong move;
        var   fromSquare = Bitwise.FirstSetSquare(king);

        if (fromSquare == Square.Illegal)
        {
            return;
        }
        var kingDestinations = moveGeneration switch
        {
            MoveGeneration.AllMoves => Board.KingMoveMasks[(int)fromSquare] & unOrEnemyOccupiedSquares & toSquareMask,
            MoveGeneration.OnlyCaptures => Board.KingMoveMasks[(int)fromSquare] & enemyOccupiedSquares & toSquareMask,
            MoveGeneration.OnlyNonCaptures => Board.KingMoveMasks[(int)fromSquare] & ~Occupancy & toSquareMask,
            _ => throw new Exception($"{moveGeneration} move generation not supported.")
        };
        Square toSquare;

        while ((toSquare = Bitwise.PopFirstSetSquare(ref kingDestinations)) != Square.Illegal)
        {
            var victim = GetPiece(toSquare);
            move = Move.Null;
            Move.SetPiece(ref move, attacker);
            Move.SetFrom(ref move, fromSquare);
            Move.SetTo(ref move, toSquare);
            if (victim != Piece.None)
            {
                Move.SetCaptureAttacker(ref move, attacker);
            }
            Move.SetCaptureVictim(ref move, victim);
            Move.SetIsKingMove(ref move, true);
            Moves[MoveIndex] = move;
            MoveIndex++;
        }
        if (moveGeneration != MoveGeneration.OnlyCaptures)
        {
            for (var boardSide = BoardSide.Queen; boardSide <= BoardSide.King; boardSide++)
            {
                var castleEmptySquaresMask = Board.CastleEmptySquaresMask[(int)ColorToMove][(int)boardSide];
                if (!KingInCheck && Game.Castling.Permitted(Castling, ColorToMove, boardSide) && ((Occupancy & castleEmptySquaresMask) == 0))
                {
                    // Castle
                    toSquare = Board.CastleToSquares[(int)ColorToMove][(int)boardSide];
                    if ((Board.SquareMasks[(int)toSquare] & toSquareMask) > 0)
                    {
                        move = Move.Null;
                        Move.SetPiece(ref move, attacker);
                        Move.SetFrom(ref move, fromSquare);
                        Move.SetTo(ref move, toSquare);
                        Move.SetIsCastling(ref move, true);
                        Move.SetIsKingMove(ref move, true);
                        Moves[MoveIndex] = move;
                        MoveIndex++;
                    }
                }
            }
        }
    }
Esempio n. 9
0
    private void GeneratePawnMoves(MoveGeneration moveGeneration, ulong fromSquareMask, ulong toSquareMask)
    {
        var pawns = GetPawns(ColorToMove) & fromSquareMask;

        if (pawns == 0)
        {
            return;
        }
        var    pawnMoveMasks        = Board.PawnMoveMasks[(int)ColorToMove];
        var    pawnDoubleMoveMasks  = Board.PawnDoubleMoveMasks[(int)ColorToMove];
        var    pawnAttackMasks      = Board.PawnAttackMasks[(int)ColorToMove];
        var    enemyOccupiedSquares = ColorOccupancy[(int)ColorLastMoved];
        var    unoccupiedSquares    = ~Occupancy;
        var    ranks           = Board.Ranks[(int)ColorToMove];
        var    attacker        = PieceHelper.GetPieceOfColor(ColorlessPiece.Pawn, ColorToMove);
        var    queen           = PieceHelper.GetPieceOfColor(ColorlessPiece.Queen, ColorToMove);
        var    knight          = PieceHelper.GetPieceOfColor(ColorlessPiece.Knight, ColorToMove);
        var    enPassantVictim = PieceHelper.GetPieceOfColor(ColorlessPiece.Pawn, ColorLastMoved);
        Square fromSquare;
        ulong  move;

        if ((EnPassantSquare != Square.Illegal) && ((Board.SquareMasks[(int)EnPassantSquare] & toSquareMask) > 0) && (moveGeneration != MoveGeneration.OnlyNonCaptures))
        {
            var enPassantAttackers = Board.EnPassantAttackerMasks[(int)EnPassantSquare] & pawns;
            while ((fromSquare = Bitwise.PopFirstSetSquare(ref enPassantAttackers)) != Square.Illegal)
            {
                // Capture pawn en passant.
                move = Move.Null;
                Move.SetPiece(ref move, attacker);
                Move.SetFrom(ref move, fromSquare);
                Move.SetTo(ref move, EnPassantSquare);
                Move.SetCaptureAttacker(ref move, attacker);
                Move.SetCaptureVictim(ref move, enPassantVictim);
                Move.SetIsEnPassantCapture(ref move, true);
                Move.SetIsPawnMove(ref move, true);
                Moves[MoveIndex] = move;
                MoveIndex++;
            }
        }
        while ((fromSquare = Bitwise.PopFirstSetSquare(ref pawns)) != Square.Illegal)
        {
            ulong  pawnDestinations;
            Square toSquare;
            int    toSquareRank;
            if (moveGeneration != MoveGeneration.OnlyCaptures)
            {
                // Pawns may move forward one square (or two if on initial square) if forward squares are unoccupied.
                pawnDestinations = pawnMoveMasks[(int)fromSquare] & unoccupiedSquares & toSquareMask;
                while ((toSquare = Bitwise.PopFirstSetSquare(ref pawnDestinations)) != Square.Illegal)
                {
                    var doubleMove = Board.SquareDistances[(int)fromSquare][(int)toSquare] == 2;
                    if (doubleMove && ((Occupancy & pawnDoubleMoveMasks[(int)fromSquare]) > 0))
                    {
                        continue;                                                                         // Double move is blocked.
                    }
                    toSquareRank = ranks[(int)toSquare];
                    if (toSquareRank < 7)
                    {
                        move = Move.Null;
                        Move.SetPiece(ref move, attacker);
                        Move.SetFrom(ref move, fromSquare);
                        Move.SetTo(ref move, toSquare);
                        Move.SetIsDoublePawnMove(ref move, doubleMove);
                        Move.SetIsPawnMove(ref move, true);
                        Moves[MoveIndex] = move;
                        MoveIndex++;
                    }
                    else
                    {
                        for (var promotedPiece = queen; promotedPiece >= knight; promotedPiece--)
                        {
                            // Promote pawn.
                            move = Move.Null;
                            Move.SetPiece(ref move, attacker);
                            Move.SetFrom(ref move, fromSquare);
                            Move.SetTo(ref move, toSquare);
                            Move.SetPromotedPiece(ref move, promotedPiece);
                            Move.SetIsPawnMove(ref move, true);
                            Moves[MoveIndex] = move;
                            MoveIndex++;
                        }
                    }
                }
            }
            if (moveGeneration != MoveGeneration.OnlyNonCaptures)
            {
                // Pawns may attack diagonally forward one square if occupied by enemy.
                pawnDestinations = pawnAttackMasks[(int)fromSquare] & enemyOccupiedSquares & toSquareMask;
                while ((toSquare = Bitwise.PopFirstSetSquare(ref pawnDestinations)) != Square.Illegal)
                {
                    toSquareRank = ranks[(int)toSquare];
                    var victim = GetPiece(toSquare);
                    if (toSquareRank < 7)
                    {
                        move = Move.Null;
                        Move.SetPiece(ref move, attacker);
                        Move.SetFrom(ref move, fromSquare);
                        Move.SetTo(ref move, toSquare);
                        Move.SetCaptureAttacker(ref move, attacker);
                        Move.SetCaptureVictim(ref move, victim);
                        Move.SetIsPawnMove(ref move, true);
                        Moves[MoveIndex] = move;
                        MoveIndex++;
                    }
                    else
                    {
                        for (var promotedPiece = queen; promotedPiece >= knight; promotedPiece--)
                        {
                            // Promote pawn.
                            move = Move.Null;
                            Move.SetPiece(ref move, attacker);
                            Move.SetFrom(ref move, fromSquare);
                            Move.SetTo(ref move, toSquare);
                            Move.SetCaptureAttacker(ref move, attacker);
                            Move.SetPromotedPiece(ref move, promotedPiece);
                            Move.SetCaptureVictim(ref move, victim);
                            Move.SetIsPawnMove(ref move, true);
                            Moves[MoveIndex] = move;
                            MoveIndex++;
                        }
                    }
                }
            }
        }
    }