private static void Perft(BoardState boardState, int depth, AdvancedPerftResult result) { Span <Move> moves = stackalloc Move[SearchConstants.MaxMovesCount]; var movesCount = boardState.GetAllMoves(moves); if (depth <= 0) { result.Leafs++; return; } if (depth == 1) { UpdateResult(boardState, moves, movesCount, result); return; } for (var i = 0; i < movesCount; i++) { boardState.MakeMove(moves[i]); if (!boardState.IsKingChecked(ColorOperations.Invert(boardState.ColorToMove))) { Perft(boardState, depth - 1, result); } boardState.UndoMove(moves[i]); } }
/// <summary> /// Checks if the specified bitboard is finished or not. /// </summary> /// <param name="bitboard">The bitboard to check.</param> private void CheckIfGameHasEnded(Bitboard bitboard) { var enemyColor = ColorOperations.Invert(_currentColor); if (Bitboard.IsStalemate(enemyColor)) { ConsoleManager.WriteLine("$gStalemate, wins!"); _done = true; return; } if (Bitboard.IsThreefoldRepetition()) { ConsoleManager.WriteLine("$gThreefold repetition!"); _done = true; return; } if (Bitboard.IsMate(enemyColor)) { ConsoleManager.WriteLine($"$gMate, {_currentColor} wins!"); _done = true; return; } }
public static int GetLoudMoves(BoardState boardState, Span <Move> moves, int offset, ulong evasionMask) { var color = boardState.ColorToMove; var enemyColor = ColorOperations.Invert(color); var queens = boardState.Pieces[color][Piece.Queen]; while (queens != 0) { var piece = BitOperations.GetLsb(queens); queens = BitOperations.PopLsb(queens); var from = BitOperations.BitScan(piece); var availableMoves = QueenMovesGenerator.GetMoves(boardState.OccupancySummary, from) & boardState.Occupancy[enemyColor]; availableMoves &= evasionMask; while (availableMoves != 0) { var field = BitOperations.GetLsb(availableMoves); var fieldIndex = BitOperations.BitScan(field); availableMoves = BitOperations.PopLsb(availableMoves); moves[offset++] = new Move(from, fieldIndex, MoveFlags.Capture); } } return(offset); }
private static ulong Perft(BoardState boardState, int depth, ref bool verificationSuccess) { if (!VerifyBoard(boardState)) { verificationSuccess = false; return(0); } if (depth <= 0) { return(1); } Span <Move> moves = stackalloc Move[SearchConstants.MaxMovesCount]; var movesCount = boardState.GetAllMoves(moves); ulong nodes = 0; for (var i = 0; i < movesCount; i++) { boardState.MakeNullMove(); boardState.UndoNullMove(); boardState.MakeMove(moves[i]); if (!boardState.IsKingChecked(ColorOperations.Invert(boardState.ColorToMove))) { nodes += Perft(boardState, depth - 1, ref verificationSuccess); } boardState.UndoMove(moves[i]); } return(nodes); }
/// <summary> /// Does an AI move. /// </summary> private void MoveAI() { Task.Run(() => { var openingBookMove = _openingBook.GetMoveFromBook(_history); var enemyColor = ColorOperations.Invert(_currentColor); Move moveToApply = null; if (openingBookMove != null) { moveToApply = Bitboard.Moves.First(p => p.From == openingBookMove.From && p.To == openingBookMove.To); } else { var aiResult = _ai.Calculate(_currentColor, Bitboard, _preferredTime, _helperTasksCount); moveToApply = aiResult.PVNodes[0]; ConsoleManager.WriteLine(); ConsoleManager.WriteLine($"$w{_currentColor}:"); ConsoleManager.WriteLine($"$wBest move: $g{aiResult.PVNodes} $w(Score: $m{aiResult.Score}$w)"); ConsoleManager.WriteLine($"$wTotal nodes: $g{aiResult.Stats.TotalNodes} N $w(Depth: $m{aiResult.Depth}$w)"); ConsoleManager.WriteLine($"$wTime: $m{aiResult.Time} s"); ConsoleManager.WriteLine(); } CalculateBitboard(moveToApply, false); CheckIfGameHasEnded(Bitboard); _currentColor = enemyColor; _history.Add(moveToApply); }); }
public static int GetQuietMoves(BoardState boardState, Span <Move> moves, int offset, ulong evasionMask) { var color = boardState.ColorToMove; var enemyColor = ColorOperations.Invert(color); var knights = boardState.Pieces[color][Piece.Knight]; while (knights != 0) { var piece = BitOperations.GetLsb(knights); knights = BitOperations.PopLsb(knights); var from = BitOperations.BitScan(piece); var availableMoves = KnightMovesGenerator.GetMoves(from) & ~boardState.OccupancySummary; availableMoves &= evasionMask; while (availableMoves != 0) { var field = BitOperations.GetLsb(availableMoves); var fieldIndex = BitOperations.BitScan(field); availableMoves = BitOperations.PopLsb(availableMoves); moves[offset++] = new Move(from, fieldIndex, MoveFlags.Quiet); } } return(offset); }
private static int GetPrincipalVariation(BoardState board, Move[] moves, int movesCount) { var entry = TranspositionTable.Get(board.Hash); if (entry.Flags == TranspositionTableEntryFlags.ExactScore && entry.IsKeyValid(board.Hash) && movesCount < SearchConstants.MaxDepth) { if (!board.IsMoveLegal(entry.BestMove)) { return(movesCount); } moves[movesCount] = entry.BestMove; board.MakeMove(entry.BestMove); var enemyColor = ColorOperations.Invert(board.ColorToMove); var king = board.Pieces[enemyColor][Piece.King]; var kingField = BitOperations.BitScan(king); if (board.IsFieldAttacked(enemyColor, (byte)kingField)) { board.UndoMove(entry.BestMove); return(movesCount); } movesCount = GetPrincipalVariation(board, moves, movesCount + 1); board.UndoMove(entry.BestMove); } return(movesCount); }
public bool IsFieldPassing(int color, int field) { var enemyColor = ColorOperations.Invert(color); var passingArea = PassingPatternGenerator.GetPattern(color, field); return((passingArea & Pieces[enemyColor][Piece.Pawn]) == 0); }
public static int GetAvailableCaptureMoves(BoardState boardState, Span <Move> moves, int offset) { var color = boardState.ColorToMove; var enemyColor = ColorOperations.Invert(color); var rooks = boardState.Pieces[color][Piece.Rook]; while (rooks != 0) { var piece = BitOperations.GetLsb(rooks); rooks = BitOperations.PopLsb(rooks); var from = BitOperations.BitScan(piece); var availableMoves = RookMovesGenerator.GetMoves(boardState.OccupancySummary, from) & boardState.Occupancy[enemyColor]; while (availableMoves != 0) { var field = BitOperations.GetLsb(availableMoves); var fieldIndex = BitOperations.BitScan(field); availableMoves = BitOperations.PopLsb(availableMoves); moves[offset++] = new Move(from, fieldIndex, MoveFlags.Capture); } } return(offset); }
public static int GetQuietMoves(BoardState boardState, Span <Move> moves, int offset) { var color = boardState.ColorToMove; var enemyColor = ColorOperations.Invert(color); var piece = boardState.Pieces[color][Piece.King]; if (piece == 0) { return(offset); } var from = BitOperations.BitScan(piece); var availableMoves = KingMovesGenerator.GetMoves(from) & ~boardState.OccupancySummary; while (availableMoves != 0) { var field = BitOperations.GetLsb(availableMoves); var fieldIndex = BitOperations.BitScan(field); availableMoves = BitOperations.PopLsb(availableMoves); moves[offset++] = new Move(from, fieldIndex, MoveFlags.Quiet); } return(offset); }
private static bool LMRCanBeApplied(SearchContext context, int depth, bool friendlyKingInCheck, bool enemyKingInCheck, int moveIndex, Span <Move> moves, Span <short> moveValues) { if (depth >= SearchConstants.LMRMinDepth && moveIndex >= SearchConstants.LMRMovesWithoutReduction && (moves[moveIndex].IsQuiet() || (moves[moveIndex].IsCapture() && moveValues[moveIndex] < 0)) && !friendlyKingInCheck && !enemyKingInCheck) { var enemyColor = ColorOperations.Invert(context.BoardState.ColorToMove); var piece = context.BoardState.PieceTable[moves[moveIndex].To]; if (HistoryHeuristic.GetRawMoveValue(enemyColor, piece, moves[moveIndex].To) >= HistoryHeuristic.GetMaxValue() / SearchConstants.LMRMaxHistoryValueDivider) { return(false); } if (piece == Piece.Pawn && context.BoardState.IsFieldPassing(enemyColor, moves[moveIndex].To)) { return(false); } #if DEBUG context.Statistics.LMRReductions++; #endif return(true); } return(false); }
/// <summary> /// Calculates en passant move. /// </summary> /// <param name="bitboard">The bitboard.</param> public override void CalculateMove(Bitboard bitboard) { var from = BitPositionConverter.ToULong(From); var to = BitPositionConverter.ToULong(To); var enemyColor = ColorOperations.Invert(Color); RemoveEnPassantPiece(bitboard, enemyColor, to); CalculatePieceMove(bitboard, from, to); }
public int Do(Color color, Bitboard bitboard, int alpha, int beta, AIStats stats) { var enemyColor = ColorOperations.Invert(color); var colorSign = ColorOperations.ToSign(color); stats.QuiescenceTotalNodes++; var whiteGeneratorMode = GetGeneratorMode(color, Color.White); var blackGeneratorMode = GetGeneratorMode(color, Color.Black); bitboard.Calculate(whiteGeneratorMode, blackGeneratorMode, true); if (bitboard.IsCheck(enemyColor)) { stats.QuiescenceEndNodes++; return(AIConstants.MateValue); } var evaluation = colorSign * bitboard.GetEvaluation(); if (evaluation >= beta) { stats.QuiescenceEndNodes++; return(beta); } if (evaluation > alpha) { alpha = evaluation; } var sortedMoves = SortMoves(color, bitboard, bitboard.Moves); foreach (var move in sortedMoves) { var bitboardAfterMove = bitboard.Move(move); var nodeValue = -Do(enemyColor, bitboardAfterMove, -beta, -alpha, stats); if (nodeValue >= beta) { return(beta); } if (nodeValue > alpha) { alpha = nodeValue; } } if (!sortedMoves.Any()) { stats.QuiescenceEndNodes++; } return(alpha); }
/// <summary> /// Initializes a new instance of the <see cref="Bitboard"/> class. /// </summary> /// <param name="bitboard">The previous bitboard.</param> /// <param name="move">The move to apply.</param> public Bitboard(Bitboard bitboard, Move move) : this(bitboard) { IncrementalZobrist.ClearEnPassant(ColorOperations.Invert(move.Color), this); EnPassant[(int)ColorOperations.Invert(move.Color)] = 0; ReversibleMoves++; move.Do(this); CalculateGamePhase(); UpdateHistory(); }
/// <summary> /// Checks if king with the specified color is checked. /// </summary> /// <param name="color">The king color.</param> /// <returns>True if king with specified color is checked, otherwise false.</returns> public bool IsCheck(Color color) { if (!_calculated) { throw new BitboardNotCalculatedException(); } var enemyColor = ColorOperations.Invert(color); var king = Pieces[FastArray.GetPieceIndex(color, PieceType.King)]; return((AttacksSummary[(int)enemyColor] & king) != 0); }
public void UndoNullMove() { NullMoves--; ColorToMove = ColorOperations.Invert(ColorToMove); Hash = _hashes.Pop(); EnPassant = _enPassants.Pop(); if (ColorToMove == Color.White) { MovesCount--; } }
public static int GetLoudMoves(BoardState boardState, Span <Move> moves, int offset) { var color = boardState.ColorToMove; var enemyColor = ColorOperations.Invert(color); var piece = boardState.Pieces[color][Piece.King]; if (piece == 0) { return(offset); } var from = BitOperations.BitScan(piece); var availableMoves = KingMovesGenerator.GetMoves(from) & boardState.Occupancy[enemyColor]; while (availableMoves != 0) { var field = BitOperations.GetLsb(availableMoves); var fieldIndex = BitOperations.BitScan(field); availableMoves = BitOperations.PopLsb(availableMoves); moves[offset++] = new Move(from, fieldIndex, MoveFlags.Capture); } if (color == Color.White) { if (IsWhiteKingCastlingAvailable(boardState, color)) { moves[offset++] = new Move(3, 1, MoveFlags.KingCastle); } if (IsWhiteQueenCastlingAvailable(boardState, color)) { moves[offset++] = new Move(3, 5, MoveFlags.QueenCastle); } } else { if (IsBlackKingCastlingAvailable(boardState, color)) { moves[offset++] = new Move(59, 57, MoveFlags.KingCastle); } if (IsBlackQueenCastlingAvailable(boardState, color)) { moves[offset++] = new Move(59, 61, MoveFlags.QueenCastle); } } return(offset); }
public static int Evaluate(BoardState board, int color, int openingPhase, int endingPhase) { var doubledRooks = 0; var rooksOnOpenFile = 0; var pairOfBishops = 0; var enemyColor = ColorOperations.Invert(color); var rooks = board.Pieces[color][Piece.Rook]; while (rooks != 0) { var lsb = BitOperations.GetLsb(rooks); var field = BitOperations.BitScan(lsb); rooks = BitOperations.PopLsb(rooks); var file = FilePatternGenerator.GetPattern(field) | lsb; var rooksOnFile = file & board.Pieces[color][Piece.Rook]; var friendlyPawnsOnFile = file & board.Pieces[color][Piece.Pawn]; var enemyPawnsOnFile = file & board.Pieces[enemyColor][Piece.Pawn]; if (BitOperations.Count(rooksOnFile) > 1) { // We don't assume that there will be more than two rooks - even if, then this color is probably anyway winning doubledRooks = 1; } if (friendlyPawnsOnFile == 0 && enemyPawnsOnFile == 0) { rooksOnOpenFile++; } } var bishops = board.Pieces[color][Piece.Bishop]; if (BitOperations.Count(bishops) > 1) { pairOfBishops = 1; } var doubledRooksOpeningScore = doubledRooks * EvaluationConstants.DoubledRooks; var doubledRooksAdjusted = TaperedEvaluation.AdjustToPhase(doubledRooksOpeningScore, 0, openingPhase, endingPhase); var rooksOnOpenFileOpeningScore = rooksOnOpenFile * EvaluationConstants.RookOnOpenFile; var rooksOnOpenFileAdjusted = TaperedEvaluation.AdjustToPhase(rooksOnOpenFileOpeningScore, 0, openingPhase, endingPhase); var pairOfBishopsOpeningScore = pairOfBishops * EvaluationConstants.PairOfBishops; var pairOfBishopsAdjusted = TaperedEvaluation.AdjustToPhase(pairOfBishopsOpeningScore, 0, openingPhase, endingPhase); return(doubledRooksAdjusted + rooksOnOpenFileAdjusted + pairOfBishopsAdjusted); }
public bool IsFieldAttacked(int color, int fieldIndex) { var enemyColor = ColorOperations.Invert(color); var fileRankAttacks = RookMovesGenerator.GetMoves(OccupancySummary, fieldIndex) & Occupancy[enemyColor]; var attackingRooks = fileRankAttacks & (Pieces[enemyColor][Piece.Rook] | Pieces[enemyColor][Piece.Queen]); if (attackingRooks != 0) { return(true); } var diagonalAttacks = BishopMovesGenerator.GetMoves(OccupancySummary, fieldIndex) & Occupancy[enemyColor]; var attackingBishops = diagonalAttacks & (Pieces[enemyColor][Piece.Bishop] | Pieces[enemyColor][Piece.Queen]); if (attackingBishops != 0) { return(true); } var jumpAttacks = KnightMovesGenerator.GetMoves(fieldIndex); var attackingKnights = jumpAttacks & Pieces[enemyColor][Piece.Knight]; if (attackingKnights != 0) { return(true); } var boxAttacks = KingMovesGenerator.GetMoves(fieldIndex); var attackingKings = boxAttacks & Pieces[enemyColor][Piece.King]; if (attackingKings != 0) { return(true); } var field = 1ul << fieldIndex; var potentialPawns = boxAttacks & Pieces[enemyColor][Piece.Pawn]; var attackingPawns = color == Color.White ? field & ((potentialPawns >> 7) | (potentialPawns >> 9)) : field & ((potentialPawns << 7) | (potentialPawns << 9)); if (attackingPawns != 0) { return(true); } return(false); }
/// <summary> /// Calculates a promotion move. /// </summary> /// <param name="bitboard">The bitboard.</param> public override void CalculateMove(Bitboard bitboard) { var from = BitPositionConverter.ToULong(From); var to = BitPositionConverter.ToULong(To); if (KillMove) { CalculateKill(bitboard, ColorOperations.Invert(Color), to); } CalculatePieceMove(bitboard, Piece, from, PromotionPiece, to); IncrementalMaterial.RemovePiece(bitboard, Color, Piece); IncrementalMaterial.AddPiece(bitboard, Color, PromotionPiece); }
/// <summary> /// The event handler for OnFieldSelection. /// </summary> /// <param name="sender">The event sender.</param> /// <param name="e">The event arguments.</param> private void Board_OnFieldSelection(object sender, FieldSelectedEventArgs e) { if (!_done && _currentColor == _playerColor && e.Piece != null && e.Piece.Color == _currentColor) { var enemyColor = ColorOperations.Invert(_currentColor); var movesForPiece = Bitboard.Moves .Where(p => p.From == e.Position && !Bitboard.Moves.Any(q => p.Piece == PieceType.King && q.Color == enemyColor && q.To == p.To)) .Select(p => p.To) .ToList(); VisualBoard.AddExternalSelections(movesForPiece); } }
/// <summary> /// The event handler for OnPieceMove. /// </summary> /// <param name="sender">The event sender.</param> /// <param name="e">The event arguments.</param> private void Board_OnPieceMove(object sender, PieceMovedEventArgs e) { if (!_done && _currentColor == _playerColor && e.Piece != null && e.Piece.Color == _currentColor) { var enemyColor = ColorOperations.Invert(_currentColor); var move = Bitboard.Moves.FirstOrDefault(p => p.From == e.From && p.To == e.To); if (move == null) { return; } var testBitboard = Bitboard.Move(move); testBitboard.Calculate(false); if (testBitboard.IsCheck(_currentColor)) { ConsoleManager.WriteLine("$RInvalid move"); return; } switch (move) { case PromotionMove _: { var promotionMoves = Bitboard.Moves.OfType <PromotionMove>().Where(p => p.From == move.From && p.To == move.To); PromotionWindow.Display(move.Color, promotionMoves); break; } default: { CalculateBitboard(move, false); break; } } CheckIfGameHasEnded(Bitboard); _currentColor = ColorOperations.Invert(_currentColor); _history.Add(move); MoveAI(); } }
/// <summary> /// Calculates SEE for the specified bitboard. All fields that are attacked by the passed color will be processed. /// </summary> /// <param name="initialColor">The color of the first attacker.</param> /// <param name="bitboard">The bitboard.</param> /// <returns>The list of all attacked fields with associated scores (relative to the first color attacker).</returns> public LinkedList <SEEResult> Calculate(Color initialColor, Bitboard bitboard) { var seeResults = new LinkedList <SEEResult>(); var enemyColor = ColorOperations.Invert(initialColor); var possibleAttacks = bitboard.AttacksSummary[(int)initialColor] & bitboard.Occupancy[(int)enemyColor]; while (possibleAttacks != 0) { var field = BitOperations.GetLSB(possibleAttacks); possibleAttacks = BitOperations.PopLSB(possibleAttacks); RunSEEForField(field, initialColor, bitboard, seeResults); } return(seeResults); }
private void ParseMoves(List <string> moves) { var color = Color.White; foreach (var move in moves) { var parsedMove = Move.FromTextNotation(_uciClient.BoardState, move); if (parsedMove == Move.Empty) { _uciClient.SendError("invalidmove"); return; } _uciClient.BoardState.MakeMove(parsedMove); color = ColorOperations.Invert(color); } }
public static bool IsMoveLegal(BoardState boardState, Move move) { var enemyColor = ColorOperations.Invert(boardState.ColorToMove); var toField = 1ul << move.To; if (!move.IsCapture()) { if (move.Flags == MoveFlags.Quiet || ((int)move.Flags & MoveFlagFields.Promotion) != 0) { if ((boardState.OccupancySummary & toField) == 0) { return(true); } } else if (move.Flags == MoveFlags.DoublePush) { var middleField = 1ul << ((move.From + move.To) / 2); if ((boardState.OccupancySummary & middleField) == 0 && (boardState.OccupancySummary & toField) == 0) { return(true); } } } else { if (move.Flags == MoveFlags.EnPassant) { if ((boardState.EnPassant & toField) != 0) { return(true); } } else { var difference = move.To - move.From; var colorDifference = -(boardState.ColorToMove * 2 - 1) * difference; if ((boardState.Occupancy[enemyColor] & toField) != 0 && (colorDifference == 7 || colorDifference == 9)) { return(true); } } } return(false); }
/// <summary> /// Calculates generator parameters for the specified player and generator mode. /// </summary> /// <param name="color">The player color.</param> /// <param name="mode">The generator mode.</param> /// <param name="quiescenceSearch">If true, only quiescence moves (mainly captures) will be generated.</param> /// <returns>The generator parameters.</returns> private GeneratorParameters GetGeneratorParameters(Color color, GeneratorMode mode, bool quiescenceSearch) { return(new GeneratorParameters { Bitboard = this, FriendlyColor = color, EnemyColor = ColorOperations.Invert(color), Mode = mode, OccupancySummary = Occupancy[(int)Color.White] | Occupancy[(int)Color.Black], FriendlyOccupancy = Occupancy[(int)color], EnemyOccupancy = Occupancy[(int)ColorOperations.Invert(color)], QuiescenceSearch = quiescenceSearch }); }
public static bool IsMoveLegal(BoardState boardState, Move move) { var enemyColor = ColorOperations.Invert(boardState.ColorToMove); var availableMoves = QueenMovesGenerator.GetMoves(boardState.OccupancySummary, move.From); var toField = 1ul << move.To; if (move.IsSinglePush() && (availableMoves & toField) != 0 && (boardState.OccupancySummary & toField) == 0) { return(true); } if (move.IsCapture() && (availableMoves & toField) != 0 && (boardState.Occupancy[enemyColor] & toField) != 0) { return(true); } return(false); }
public static bool IsMoveLegal(BoardState boardState, Move move) { var enemyColor = ColorOperations.Invert(boardState.ColorToMove); var availableMoves = BishopMovesGenerator.GetMoves(boardState.OccupancySummary, move.From); var toField = 1ul << move.To; if (move.Flags == MoveFlags.Quiet && (availableMoves & toField) != 0 && (boardState.OccupancySummary & toField) == 0) { return(true); } if (move.Flags == MoveFlags.Capture && (availableMoves & toField) != 0 && (boardState.Occupancy[enemyColor] & toField) != 0) { return(true); } return(false); }
private static void UpdateResult(BoardState boardState, Span <Move> moves, int movesCount, AdvancedPerftResult result) { var legalMoveFound = false; for (var i = 0; i < movesCount; i++) { boardState.MakeMove(moves[i]); if (!boardState.IsKingChecked(ColorOperations.Invert(boardState.ColorToMove))) { if (moves[i].IsCapture()) { result.Captures++; } if (moves[i].Flags == MoveFlags.KingCastle || moves[i].Flags == MoveFlags.QueenCastle) { result.Castles++; } if (moves[i].Flags == MoveFlags.EnPassant) { result.EnPassants++; result.Captures++; } if (boardState.IsKingChecked(boardState.ColorToMove)) { result.Checks++; } result.Leafs++; legalMoveFound = true; } boardState.UndoMove(moves[i]); } if (!legalMoveFound) { result.Checkmates++; } }
public static bool IsMoveLegal(BoardState boardState, Move move) { var enemyColor = ColorOperations.Invert(boardState.ColorToMove); var availableMoves = KingMovesGenerator.GetMoves(move.From); var toField = 1ul << move.To; if (move.Flags == MoveFlags.Quiet && (availableMoves & toField) != 0 && (boardState.OccupancySummary & toField) == 0) { return(true); } if (move.Flags == MoveFlags.Capture && (availableMoves & toField) != 0 && (boardState.Occupancy[enemyColor] & toField) != 0) { return(true); } if (move.Flags == MoveFlags.KingCastle) { if (boardState.ColorToMove == Color.White && IsWhiteKingCastlingAvailable(boardState, boardState.ColorToMove)) { return(true); } else if (boardState.ColorToMove == Color.Black && IsBlackKingCastlingAvailable(boardState, boardState.ColorToMove)) { return(true); } } if (move.Flags == MoveFlags.QueenCastle) { if (boardState.ColorToMove == Color.White && IsWhiteQueenCastlingAvailable(boardState, boardState.ColorToMove)) { return(true); } else if (boardState.ColorToMove == Color.Black && IsBlackQueenCastlingAvailable(boardState, boardState.ColorToMove)) { return(true); } } return(false); }