public void BishopBorderBlocked()
        {
            BitBoard border      = 0xff818181818181ff;
            BitBoard borderInner = 0x7e424242427e00;
            var      corners     = BitBoards.MakeBitboard(Squares.a1, Squares.a8, Squares.h1, Squares.h8);

            const int expectedCorner = 1; // just a single attack square no matter what
            const int expectedSide   = 2;

            /*
             * borderInner (X = set bit) :
             *
             * 0 0 0 0 0 0 0 0
             * 0 X X X X X X 0
             * 0 X 0 0 0 0 X 0
             * 0 X 0 0 0 0 X 0
             * 0 X 0 0 0 0 X 0
             * 0 X 0 0 0 0 X 0
             * 0 X X X X X X 0
             * 0 0 0 0 0 0 0 0
             *
             */
            foreach (var square in border)
            {
                var attacks = square.BishopAttacks(borderInner);
                attacks.IsEmpty.Should().BeFalse();
                var expected = corners & square ? expectedCorner : expectedSide;
                var actual   = attacks.Count;
                Assert.Equal(expected, actual);
            }
        }
Esempio n. 2
0
        private BitBoard ComputeDynamicFencedPawns(Player them)
        {
            // reverse order of Down
            var down = them.IsBlack
                ? Directions.South
                : Directions.North;

            var result = BitBoard.Empty;

            for (File f = Files.FileA; f < Files.FileNb; ++f)
            {
                var sq = NextFenceRankSquare(f, them);
                var b  = sq.ForwardFile(them) & _theirPawns;
                while (!b.IsEmpty)
                {
                    sq = BitBoards.PopLsb(ref b) + down;
                    if (_pos.GetPiece(sq) == _ourPawn)
                    {
                        result |= sq;
                    }
                }
            }

            return(result);
        }
Esempio n. 3
0
        /// <summary>
        /// Computes the fence ranks
        /// </summary>
        private void ComputeFenceRanks()
        {
            var covered = _fence;

            while (covered)
            {
                var sq = BitBoards.PopLsb(ref covered);
                var(r, f)             = sq.RankFile;
                _fenceRank[f.AsInt()] = r;
            }
        }
Esempio n. 4
0
        public void BitBoardOrALlTest()
        {
            var baseSquares = new Square[] { Squares.a1, Squares.a2, Squares.a3, Squares.a4, Squares.a5, Squares.a6, Squares.a7, Squares.a8 };
            var bb          = BitBoard.Empty.OrAll(baseSquares);

            while (!bb.IsEmpty)
            {
                var s     = BitBoards.PopLsb(ref bb);
                var valid = Array.BinarySearch(baseSquares, s) > -1;
                Assert.True(valid);
            }
        }
Esempio n. 5
0
        public void AlignedSimplePositiveTest()
        {
            const bool expected = true;

            const ESquare sq1 = ESquare.a1;
            const ESquare sq2 = ESquare.a2;
            const ESquare sq3 = ESquare.a3;

            var actual = BitBoards.Aligned(sq1, sq2, sq3);

            Assert.Equal(expected, actual);
        }
Esempio n. 6
0
        public void MakeBitBoardTest()
        {
            // a few squares
            var b1 = BitBoards.MakeBitboard(Squares.a1, Squares.b1, Squares.a2, Squares.b2);
            var b2 = Squares.a1.BitBoardSquare() | Squares.b1.BitBoardSquare() | Squares.a2.BitBoardSquare() | Squares.b2.BitBoardSquare();

            Assert.Equal(b1, b2);

            // a single square (not needed, but still has to work in case of list of squares etc)
            var b3 = BitBoards.MakeBitboard(Squares.h3);
            var b4 = Squares.h3.BitBoardSquare();

            Assert.Equal(b3, b4);
        }
Esempio n. 7
0
        private static void AddPawnMoves(this IPosition pos, MoveList moves, BitBoard targetSquares, Direction direction, EMoveType type, Emgf flags)
        {
            if (targetSquares.Empty())
            {
                return;
            }

            var stm   = pos.State.SideToMove;
            var piece = EPieceType.Pawn.MakePiece(stm);

            var promotionRank       = stm.PromotionRank();
            var promotionSquares    = targetSquares & promotionRank;
            var nonPromotionSquares = targetSquares & ~promotionRank;

            while (nonPromotionSquares)
            {
                var to   = nonPromotionSquares.Lsb();
                var from = to - direction;
                pos.AddMove(moves, piece, from, to, PieceExtensions.EmptyPiece, flags, type);
                BitBoards.ResetLsb(ref nonPromotionSquares);
            }

            type |= EMoveType.Promotion;

            if (flags.HasFlagFast(Emgf.Queenpromotion))
            {
                var sqTo   = promotionSquares.Lsb();
                var sqFrom = sqTo - direction;
                pos.AddMove(moves, piece, sqFrom, sqTo, EPieceType.Queen.MakePiece(stm), flags, type);
                BitBoards.ResetLsb(ref promotionSquares);
            }
            else
            {
                while (promotionSquares)
                {
                    var sqTo   = promotionSquares.Lsb();
                    var sqFrom = sqTo - direction;
                    for (var promotedPiece = EPieceType.Queen; promotedPiece >= EPieceType.Knight; promotedPiece--)
                    {
                        pos.AddMove(moves, piece, sqFrom, sqTo, promotedPiece.MakePiece(stm), flags, type);
                    }

                    BitBoards.ResetLsb(ref promotionSquares);
                }
            }
        }
Esempio n. 8
0
        /// <summary>
        /// Iterates through the piece types and generates moves based on their attacks.
        /// It does not contain any checks for moves that are invalid, as the leaf methods
        /// contains implicit denial of move generation if the target bitboard is empty.
        /// </summary>
        /// <param name="moves">The move list to add potential moves to.</param>
        /// <param name="targetSquares">The target squares to move to</param>
        private static void AddMoves(this IPosition pos, MoveList moves, BitBoard targetSquares, Emgf flags)
        {
            var c        = pos.State.SideToMove;
            var occupied = pos.Pieces();

            for (var pt = EPieceType.Knight; pt <= EPieceType.King; ++pt)
            {
                var pc     = pt.MakePiece(c);
                var pieces = pos.Pieces(pc);
                while (pieces)
                {
                    var from = pieces.Lsb();
                    pos.AddMoves(moves, pc, from, from.GetAttacks(pt, occupied) & targetSquares, flags);
                    BitBoards.ResetLsb(ref pieces);
                }
            }
        }
Esempio n. 9
0
        private static void AddMoves(this IPosition pos, MoveList moves, Piece piece, Square from, BitBoard attacks, Emgf flags)
        {
            var target = pos.Pieces(~pos.State.SideToMove) & attacks;

            while (target)
            {
                var to = target.Lsb();
                pos.AddMove(moves, piece, from, to, PieceExtensions.EmptyPiece, flags, EMoveType.Capture);
                BitBoards.ResetLsb(ref target);
            }

            target = ~pos.Pieces() & attacks;
            while (target)
            {
                var to = target.Lsb();
                pos.AddMove(moves, piece, from, to, PieceExtensions.EmptyPiece, flags);
                BitBoards.ResetLsb(ref target);
            }
        }
Esempio n. 10
0
 public static int Pow2(this int value) => 1 << BitBoards.Msb(value).AsInt();
Esempio n. 11
0
        /// <summary>
        /// Apply a FEN string board setup to the board structure.
        /// </summary>
        /// <param name="fenString">The string to set</param>
        /// <param name="validate">If true, the fen string is validated, otherwise not</param>
        /// <returns>
        /// 0 = all ok.
        /// -1 = Error in piece file layout parsing
        /// -2 = Error in piece rank layout parsing
        /// -3 = Unknown piece detected
        /// -4 = Error while parsing moving side
        /// -5 = Error while parsing castleling
        /// -6 = Error while parsing en-passant square
        /// -9 = FEN length exceeding maximum
        /// </returns>
        public FenError SetFen(FenData fen, bool validate = false)
        {
            if (validate)
            {
                Fen.Fen.Validate(fen.Fen.ToString());
            }

            // correctly clear all pieces and invoke possible notification(s)
            var bb = Occupied;

            while (bb)
            {
                var square = bb.Lsb();
                Position.RemovePiece(square, Position.BoardLayout[square.AsInt()]);
                BitBoards.ResetLsb(ref bb);
            }

            var chunk = fen.Chunk();

            if (chunk.IsEmpty)
            {
                return(new FenError());
            }

            var    f = 1; // file (column)
            var    r = 8; // rank (row)
            Player player;

            foreach (var c in chunk)
            {
                if (char.IsDigit(c))
                {
                    f += c - '0';
                    if (f > 9)
                    {
                        return(new FenError(-1, fen.Index));
                    }
                }
                else if (c == '/')
                {
                    if (f != 9)
                    {
                        return(new FenError(-2, fen.Index));
                    }

                    r--;
                    f = 1;
                }
                else
                {
                    var pieceIndex = PieceExtensions.PieceChars.IndexOf(c);

                    if (pieceIndex == -1)
                    {
                        return(new FenError(-3, fen.Index));
                    }

                    player = char.IsLower(PieceExtensions.PieceChars[pieceIndex]);

                    var square = new Square(r - 1, f - 1);

                    AddPiece(square, player, (EPieceType)pieceIndex);

                    f++;
                }
            }

            // player
            chunk = fen.Chunk();

            if (chunk.IsEmpty || chunk.Length != 1)
            {
                return(new FenError(-3, fen.Index));
            }

            player = (chunk[0] != 'w').ToInt();

            // castleling
            chunk = fen.Chunk();

            if (!SetupCastleling(chunk))
            {
                return(new FenError(-5, fen.Index));
            }

            // en-passant
            chunk = fen.Chunk();

            if (chunk.Length == 1 || chunk[0] == '-' || !chunk[0].InBetween('a', 'h'))
            {
                State.EnPassantSquare = ESquare.none;
            }
            else
            {
                State.EnPassantSquare = chunk[1].InBetween('3', '6') ? ESquare.none : new Square(chunk[1] - '1', chunk[0] - 'a').Value;
            }

            // move number
            chunk = fen.Chunk();

            var moveNum     = 0;
            var halfMoveNum = 0;

            if (!chunk.IsEmpty)
            {
                chunk.ToIntegral(out halfMoveNum);

                // half move number
                chunk = fen.Chunk();

                chunk.ToIntegral(out moveNum);

                if (moveNum > 0)
                {
                    moveNum--;
                }
            }

            PositionIndex = PositionStart = moveNum;

            Position.State = _stateList[PositionIndex];

            State.ReversibleHalfMoveCount = halfMoveNum;

            State.SideToMove = player;

            if (player.IsBlack())
            {
                State.Key ^= Zobrist.GetZobristSide();
                State.PawnStructureKey ^= Zobrist.GetZobristSide();
            }

            State.Key ^= State.CastlelingRights.GetZobristCastleling();

            if (State.EnPassantSquare != ESquare.none)
            {
                State.Key ^= State.EnPassantSquare.File().GetZobristEnPessant();
            }

            Position.InCheck = Position.IsAttacked(Position.GetPieceSquare(EPieceType.King, player), ~player);

            return(0);
        }
Esempio n. 12
0
        /// <summary>
        /// Computes whether the current position contains a pawn fence which makes the game a draw.
        /// </summary>
        /// <returns>true if the game is a draw position - otherwise false</returns>
        public bool IsBlocked()
        {
            // Quick check if there is only pawns and kings on the board It might be possible to
            // have a minor piece and exchange it into a passing pawn
            if (_pos.Board.PieceCount(PieceTypes.AllPieces) > _pos.Board.PieceCount(PieceTypes.Pawn) + 2)
            {
                return(false);
            }

            var up = _us.PawnPushDistance();

            MarkOurPawns(up);
            MarkTheirPawns();

            var isFenceFormed = IsFenceFormed();

            if (!isFenceFormed)
            {
                return(false);
            }

            ComputeFenceRanks();

            var ourKsq   = _pos.GetKingSquare(_us);
            var theirKsq = _pos.GetKingSquare(_them);

            if (ourKsq.Rank.RelativeRank(_us) > _fenceRank[ourKsq.File.AsInt()].RelativeRank(_us))
            {
                return(false);
            }

            _dynamicPawns |= ComputeDynamicFencedPawns(_them);

            while (_dynamicPawns)
            {
                var sq = BitBoards.PopLsb(ref _dynamicPawns);
                var(r, f) = sq.RankFile;
                var rr = sq.RelativeRank(_us);

                if (r > _fenceRank[f.AsInt()])
                {
                    if ((_theirPawns & sq.PassedPawnFrontAttackSpan(_us)).IsEmpty && (theirKsq.File != f || theirKsq.Rank.RelativeRank(_us) < rr))
                    {
                        return(false);
                    }
                }
                else if (_fence & sq)
                {
                    if (rr >= Ranks.Rank6)
                    {
                        return(false);
                    }

                    if (_pos.GetPiece(sq + _us.PawnDoublePushDistance()) != _theirPawn)
                    {
                        if (theirKsq.File != f || theirKsq.Rank.RelativeRank(_us) < rr)
                        {
                            return(false);
                        }

                        if (f != Files.FileA &&
                            _pos.GetPiece(sq + Directions.West) != _ourPawn ||
                            BitBoards.PopCount(_ourPawns & PreviousFile(f)) > 1 ||
                            (_fixedPawn & (sq + Directions.West)).IsEmpty ||
                            (_fence & (sq + Directions.West)).IsEmpty)
                        {
                            return(false);
                        }
                        if (f != Files.FileH &&
                            _pos.GetPiece(sq + Directions.East) != _ourPawn ||
                            BitBoards.PopCount(_ourPawns & NextFile(f)) > 1 ||
                            (_fixedPawn & (sq + Directions.East)).IsEmpty ||
                            (_fence & (sq + Directions.East)).IsEmpty)
                        {
                            return(false);
                        }
                    }

                    if ((sq + _us.PawnDoublePushDistance()).PawnAttack(_us) & _theirPawns)
                    {
                        return(false);
                    }

                    if (BitBoards.PopCount(_ourPawns & f) > 1)
                    {
                        return(false);
                    }
                }
                else if (r < _fenceRank[f.AsInt()])
                {
                    sq += up;
                    r   = sq.Rank;
                    rr  = sq.RelativeRank(_us);
                    while ((_fence & Square.Make(r, f)).IsEmpty)
                    {
                        var pawnAttacks = sq.PawnAttack(_us);
                        if (_theirPawns & pawnAttacks)
                        {
                            return(false);
                        }

                        if (_pos.GetPiece(sq) == _ourPawn)
                        {
                            break;
                        }

                        sq += up;
                        r   = sq.Rank;
                    }

                    if ((_fence & Square.Make(r, f)).IsEmpty || _pos.IsOccupied(sq))
                    {
                        continue;
                    }

                    if (rr >= Ranks.Rank6)
                    {
                        return(false);
                    }

                    if ((_theirPawns & (sq + _us.PawnDoublePushDistance())).IsEmpty)
                    {
                        if (theirKsq.File != f || theirKsq.Rank.RelativeRank(_us) < rr)
                        {
                            return(false);
                        }

                        if (f != Files.FileA &&
                            _pos.GetPiece(sq + Directions.West) != _ourPawn ||
                            BitBoards.PopCount(_ourPawns & (f - 1)) > 1 ||
                            (_fixedPawn & Square.Make(r, PreviousFile(f))).IsEmpty ||
                            (_fence & Square.Make(r, PreviousFile(f))).IsEmpty)
                        {
                            return(false);
                        }
                        if (f != Files.FileH &&
                            _pos.GetPiece(sq + Directions.East) != _ourPawn ||
                            BitBoards.PopCount(_ourPawns & (f + 1)) > 1 ||
                            (_fixedPawn & Square.Make(r, NextFile(f))).IsEmpty ||
                            (_fence & Square.Make(r, NextFile(f))).IsEmpty)
                        {
                            return(false);
                        }
                    }

                    if ((sq + up).PawnAttack(_us) & _theirPawns)
                    {
                        return(false);
                    }
                }
            }

            return(true);
        }