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); } }
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); }
/// <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; } }
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); } }
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); }
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); }
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); } } }
/// <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); } } }
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); } }
public static int Pow2(this int value) => 1 << BitBoards.Msb(value).AsInt();
/// <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); }
/// <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); }