예제 #1
0
        public static int GetMobility(BoardState boardState, int color, ref ulong fieldsAttackedByColor)
        {
            var centerMobility         = 0;
            var extendedCenterMobility = 0;
            var outsideMobility        = 0;

            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);

                centerMobility         += (int)BitOperations.Count(availableMoves & EvaluationConstants.Center);
                extendedCenterMobility += (int)BitOperations.Count(availableMoves & EvaluationConstants.ExtendedCenter);
                outsideMobility        += (int)BitOperations.Count(availableMoves & EvaluationConstants.Outside);

                fieldsAttackedByColor |= availableMoves;
            }

            return(EvaluationConstants.CenterMobilityModifier * centerMobility +
                   EvaluationConstants.ExtendedCenterMobilityModifier * extendedCenterMobility +
                   EvaluationConstants.OutsideMobilityModifier * outsideMobility);
        }
예제 #2
0
        public static int GetLoudMoves(BoardState boardState, Span <Move> moves, int offset)
        {
            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.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);
        }
예제 #3
0
        public byte GetAttackingPiecesWithColor(int color, int fieldIndex)
        {
            byte result = 0;

            var jumpAttacks           = KnightMovesGenerator.GetMoves(fieldIndex);
            var attackingKnights      = jumpAttacks & Pieces[color][Piece.Knight];
            var attackingKnightsCount = BitOperations.Count(attackingKnights);

            if (attackingKnightsCount != 0)
            {
                result |= (byte)((attackingKnightsCount == 1 ? 1 : 3) << SeePiece.Knight1);
            }

            var diagonalAttacks  = BishopMovesGenerator.GetMoves(OccupancySummary, fieldIndex) & Occupancy[color];
            var attackingBishops = diagonalAttacks & Pieces[color][Piece.Bishop];

            if (attackingBishops != 0)
            {
                result |= 1 << SeePiece.Bishop;
            }

            var occupancyWithoutFileRankPieces = OccupancySummary & ~Pieces[color][Piece.Rook] & ~Pieces[color][Piece.Queen];
            var fileRankAttacks     = RookMovesGenerator.GetMoves(occupancyWithoutFileRankPieces, fieldIndex) & Occupancy[color];
            var attackingRooks      = fileRankAttacks & Pieces[color][Piece.Rook];
            var attackingRooksCount = BitOperations.Count(attackingRooks);

            if (attackingRooksCount != 0)
            {
                result |= (byte)((attackingRooksCount == 1 ? 1 : 3) << SeePiece.Rook1);
            }

            var attackingQueens = (fileRankAttacks | diagonalAttacks) & Pieces[color][Piece.Queen];

            if (attackingQueens != 0)
            {
                result |= 1 << SeePiece.Queen;
            }

            var boxAttacks     = KingMovesGenerator.GetMoves(fieldIndex);
            var attackingKings = boxAttacks & Pieces[color][Piece.King];

            if (attackingKings != 0)
            {
                result |= 1 << SeePiece.King;
            }

            var field          = 1ul << fieldIndex;
            var potentialPawns = boxAttacks & Pieces[color][Piece.Pawn];
            var attackingPawns = color == Color.White ?
                                 field & ((potentialPawns << 7) | (potentialPawns << 9)) :
                                 field & ((potentialPawns >> 7) | (potentialPawns >> 9));

            if (attackingPawns != 0)
            {
                result |= 1 << SeePiece.Pawn;
            }

            return(result);
        }
예제 #4
0
        /// <summary>
        /// Calculates available moves.
        /// </summary>
        /// <param name="generatorParameters">The generator parameters.</param>
        private void CalculateAvailableMoves(GeneratorParameters generatorParameters)
        {
            PawnMovesGenerator.Generate(generatorParameters);
            KnightMovesGenerator.Generate(generatorParameters);
            KingMovesGenerator.Generate(generatorParameters);

            RookMovesGenerator.Generate(PieceType.Rook, generatorParameters);
            BishopMovesGenerator.Generate(PieceType.Bishop, generatorParameters);

            RookMovesGenerator.Generate(PieceType.Queen, generatorParameters);
            BishopMovesGenerator.Generate(PieceType.Queen, generatorParameters);
        }
예제 #5
0
        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);
        }
예제 #6
0
        public static bool IsMoveLegal(BoardState boardState, Move move)
        {
            var enemyColor     = ColorOperations.Invert(boardState.ColorToMove);
            var availableMoves = KnightMovesGenerator.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);
            }

            return(false);
        }
예제 #7
0
파일: NegaMax.cs 프로젝트: Tearth/Cosette
        public static int FindBestMove(SearchContext context, int depth, int ply, int alpha, int beta, bool allowNullMove, bool friendlyKingInCheck, int extensionsCount)
        {
            if (context.Statistics.Nodes >= context.MaxNodesCount)
            {
                context.AbortSearch = true;
                return(0);
            }

            if (context.AbortSearch)
            {
                return(0);
            }

            context.Statistics.Nodes++;

            if (context.BoardState.Pieces[context.BoardState.ColorToMove][Piece.King] == 0)
            {
                context.Statistics.Leafs++;
                return(SearchConstants.NoKingValue);
            }

            if (context.BoardState.IsKingChecked(ColorOperations.Invert(context.BoardState.ColorToMove)))
            {
                context.Statistics.Leafs++;
                return(-SearchConstants.NoKingValue);
            }

            if (context.BoardState.IsThreefoldRepetition())
            {
                context.Statistics.Leafs++;
                return(EvaluationConstants.ThreefoldRepetition);
            }

            if (context.BoardState.IsInsufficientMaterial())
            {
                var enemyColor = ColorOperations.Invert(context.BoardState.ColorToMove);
                if (!friendlyKingInCheck && !context.BoardState.IsKingChecked(enemyColor))
                {
                    context.Statistics.Leafs++;
                    return(EvaluationConstants.InsufficientMaterial);
                }
            }

            if (context.BoardState.IsFiftyMoveRuleDraw())
            {
                context.Statistics.Leafs++;
                return(EvaluationConstants.ThreefoldRepetition);
            }

            if (depth <= 0)
            {
                context.Statistics.Leafs++;
                return(QuiescenceSearch.FindBestMove(context, depth, ply, alpha, beta));
            }

            var originalAlpha = alpha;
            var pvNode        = beta - alpha > 1;

            var entry    = TranspositionTable.Get(context.BoardState.Hash);
            var hashMove = Move.Empty;
            var bestMove = Move.Empty;

            if (entry.Flags != TranspositionTableEntryFlags.Invalid && entry.IsKeyValid(context.BoardState.Hash))
            {
#if DEBUG
                context.Statistics.TTHits++;
#endif
                if (entry.Flags != TranspositionTableEntryFlags.AlphaScore)
                {
                    var isMoveLegal = context.BoardState.IsMoveLegal(entry.BestMove);
                    if (isMoveLegal)
                    {
                        hashMove = entry.BestMove;
                        bestMove = entry.BestMove;
#if DEBUG
                        context.Statistics.TTValidMoves++;
#endif
                    }
#if DEBUG
                    else
                    {
                        context.Statistics.TTInvalidMoves++;
                    }
#endif
                }

                if (entry.Depth >= depth)
                {
                    entry.Score = (short)TranspositionTable.TTToRegularScore(entry.Score, ply);
                    switch (entry.Flags)
                    {
                    case TranspositionTableEntryFlags.AlphaScore:
                    {
                        if (entry.Score < beta)
                        {
                            beta = entry.Score;
                        }

                        break;
                    }

                    case TranspositionTableEntryFlags.ExactScore:
                    {
                        if (!pvNode || IterativeDeepening.IsScoreNearCheckmate(entry.Score))
                        {
                            return(entry.Score);
                        }

                        break;
                    }

                    case TranspositionTableEntryFlags.BetaScore:
                    {
                        if (entry.Score > alpha)
                        {
                            alpha = entry.Score;
                        }

                        break;
                    }
                    }

                    if (alpha >= beta)
                    {
                        context.Statistics.BetaCutoffs++;
                        return(entry.Score);
                    }
                }
            }
#if DEBUG
            else
            {
                entry = TranspositionTableEntry.Empty;
                context.Statistics.TTNonHits++;
            }
#endif

            if (RazoringCanBeApplied(depth, context.Statistics.Depth, friendlyKingInCheck, pvNode, alpha))
            {
                var fastEvaluation = Evaluation.FastEvaluate(context.BoardState, context.Statistics.EvaluationStatistics);
                var margin         = SearchConstants.RazoringMargin + (depth - SearchConstants.RazoringMinDepth) * SearchConstants.RazoringMarginMultiplier;
                var futileAlpha    = alpha - margin;

                if (fastEvaluation < futileAlpha)
                {
                    var result = QuiescenceSearch.FindBestMove(context, depth, ply, futileAlpha, futileAlpha + 1);
                    if (result <= futileAlpha)
                    {
#if DEBUG
                        context.Statistics.Razorings++;
#endif
                        return(futileAlpha);
                    }
#if DEBUG
                    else
                    {
                        context.Statistics.RazoringsRejected++;
                    }
#endif
                }
            }

            if (StaticNullMoveCanBeApplied(depth, context.Statistics.Depth, friendlyKingInCheck, pvNode, beta))
            {
                var fastEvaluation = Evaluation.FastEvaluate(context.BoardState, context.Statistics.EvaluationStatistics);
                var margin         = SearchConstants.StaticNullMoveMargin + (depth - 1) * SearchConstants.StaticNullMoveMarginMultiplier;
                var score          = fastEvaluation - margin;

                if (score >= beta)
                {
#if DEBUG
                    context.Statistics.StaticNullMovePrunes++;
#endif

                    return(score);
                }
            }

            if (NullMoveCanBeApplied(context.BoardState, depth, allowNullMove, pvNode, friendlyKingInCheck))
            {
                context.BoardState.MakeNullMove();
                var score = -FindBestMove(context, depth - 1 - NullMoveGetReduction(depth), ply + 1, -beta, -beta + 1, false, false, extensionsCount);
                context.BoardState.UndoNullMove();

                if (score >= beta)
                {
#if DEBUG
                    context.Statistics.NullMovePrunes++;
#endif

                    return(score);
                }
            }

            if (IIDCanBeApplied(depth, entry.Flags, hashMove))
            {
                FindBestMove(context, depth - 1 - SearchConstants.IIDDepthReduction, ply, alpha, beta, allowNullMove, friendlyKingInCheck, extensionsCount);

                var iidEntry = TranspositionTable.Get(context.BoardState.Hash);
                if (iidEntry.IsKeyValid(context.BoardState.Hash))
                {
                    hashMove = iidEntry.BestMove;

#if DEBUG
                    context.Statistics.IIDHits++;
#endif
                }
            }

            var futilityPruningCanBeApplied = false;
            var futilityPruningEvaluation   = 0;
            var futilityPruningMargin       = 0;

            if (FutilityPruningCanBeApplied(depth, context.Statistics.Depth, friendlyKingInCheck, pvNode, alpha))
            {
                futilityPruningCanBeApplied = true;
                futilityPruningEvaluation   = Evaluation.FastEvaluate(context.BoardState, context.Statistics.EvaluationStatistics);
                futilityPruningMargin       = SearchConstants.FutilityPruningMargin + (depth - 1) * SearchConstants.FutilityPruningMarginMultiplier;
            }

            Span <Move>  moves      = stackalloc Move[SearchConstants.MaxMovesCount];
            Span <short> moveValues = stackalloc short[SearchConstants.MaxMovesCount];
            var          movesCount = 0;

            var loudMovesGenerated  = false;
            var quietMovesGenerated = false;

            var evasionMask = ulong.MaxValue;
            if (friendlyKingInCheck && !context.BoardState.IsKingChecked(ColorOperations.Invert(context.BoardState.ColorToMove)))
            {
                var kingField      = context.BoardState.Pieces[context.BoardState.ColorToMove][Piece.King];
                var kingFieldIndex = BitOperations.BitScan(kingField);

                evasionMask = KnightMovesGenerator.GetMoves(kingFieldIndex) |
                              QueenMovesGenerator.GetMoves(context.BoardState.OccupancySummary, kingFieldIndex);
            }

            if (hashMove == Move.Empty)
            {
                movesCount = context.BoardState.GetLoudMoves(moves, 0, evasionMask);
                MoveOrdering.AssignLoudValues(context.BoardState, moves, moveValues, movesCount, depth, Move.Empty);
                loudMovesGenerated = true;

#if DEBUG
                context.Statistics.LoudMovesGenerated++;
#endif

                if (movesCount == 0)
                {
                    movesCount = context.BoardState.GetQuietMoves(moves, 0, evasionMask);
                    MoveOrdering.AssignQuietValues(context.BoardState, moves, moveValues, 0, movesCount, ply);
                    quietMovesGenerated = true;

#if DEBUG
                    context.Statistics.QuietMovesGenerated++;
#endif
                }
            }
            else
            {
                moves[0]      = hashMove;
                moveValues[0] = MoveOrderingConstants.HashMove;
                movesCount    = 1;
            }

            var pvs            = true;
            var bestScore      = 0;
            var allMovesPruned = true;

            for (var moveIndex = 0; moveIndex < movesCount; moveIndex++)
            {
                if (LMPCanBeApplied(context, depth, friendlyKingInCheck, quietMovesGenerated, moveIndex, movesCount, pvNode))
                {
                    break;
                }

                MoveOrdering.SortNextBestMove(moves, moveValues, movesCount, moveIndex);

                if (loudMovesGenerated && moves[moveIndex] == hashMove)
                {
                    goto postLoopOperations;
                }

                if (loudMovesGenerated && !quietMovesGenerated && moveValues[moveIndex] < 100)
                {
                    var loudMovesCount = movesCount;

                    movesCount = context.BoardState.GetQuietMoves(moves, movesCount, evasionMask);
                    MoveOrdering.AssignQuietValues(context.BoardState, moves, moveValues, loudMovesCount, movesCount, ply);
                    MoveOrdering.SortNextBestMove(moves, moveValues, movesCount, moveIndex);
                    quietMovesGenerated = true;

#if DEBUG
                    context.Statistics.QuietMovesGenerated++;
#endif

                    if (moves[moveIndex] == hashMove)
                    {
                        goto postLoopOperations;
                    }
                }

                if (context.MoveRestrictions != null && ply == 0)
                {
                    if (!context.MoveRestrictions.Contains(moves[moveIndex]))
                    {
                        goto postLoopOperations;
                    }
                }

                context.BoardState.MakeMove(moves[moveIndex]);

                var enemyKingInCheck = context.BoardState.IsKingChecked(context.BoardState.ColorToMove);
                var extension        = GetExtensions(depth, extensionsCount, enemyKingInCheck);

#if DEBUG
                context.Statistics.Extensions += extension;
#endif

                if (futilityPruningCanBeApplied && FutilityPruningCanBeAppliedForMove(context, moves[moveIndex], enemyKingInCheck, pvs))
                {
                    var gain = FutilityPruningGetGain(context, moves[moveIndex]);
                    if (futilityPruningEvaluation + futilityPruningMargin + gain <= alpha)
                    {
#if DEBUG
                        context.Statistics.FutilityPrunes++;
#endif

                        context.BoardState.UndoMove(moves[moveIndex]);
                        goto postLoopOperations;
                    }
                }

                allMovesPruned = false;

                if (pvs)
                {
                    bestScore = -FindBestMove(context, depth - 1 + extension, ply + 1, -beta, -alpha, allowNullMove, enemyKingInCheck, extensionsCount + extension);
                    pvs       = false;
                }
                else
                {
                    var lmrReduction = 0;
                    if (LMRCanBeApplied(context, depth, friendlyKingInCheck, enemyKingInCheck, moveIndex, moves, moveValues))
                    {
                        lmrReduction = LMRGetReduction(pvNode, moveIndex);
                    }

                    var score = -FindBestMove(context, depth - lmrReduction - 1 + extension, ply + 1, -alpha - 1, -alpha, allowNullMove, enemyKingInCheck, extensionsCount + extension);
                    if (score > alpha)
                    {
                        if (pvNode)
                        {
                            score = -FindBestMove(context, depth - 1 + extension, ply + 1, -beta, -alpha, allowNullMove, enemyKingInCheck, extensionsCount + extension);
                        }
                        else
                        {
                            if (lmrReduction != 0)
                            {
                                score = -FindBestMove(context, depth - 1 + extension, ply + 1, -beta, -alpha, allowNullMove, enemyKingInCheck, extensionsCount + extension);
                            }
                        }
                    }

                    if (score > bestScore)
                    {
                        bestScore = score;
                    }
                }

                context.BoardState.UndoMove(moves[moveIndex]);

                if (bestScore > alpha)
                {
                    alpha    = bestScore;
                    bestMove = moves[moveIndex];

                    if (alpha >= beta)
                    {
                        if (moves[moveIndex].IsQuiet())
                        {
                            KillerHeuristic.AddKillerMove(moves[moveIndex], context.BoardState.ColorToMove, ply);
                            HistoryHeuristic.AddHistoryMove(context.BoardState.ColorToMove, context.BoardState.PieceTable[moves[moveIndex].From], moves[moveIndex].To, depth);
                        }

#if DEBUG
                        if (moveIndex == 0)
                        {
                            context.Statistics.BetaCutoffsAtFirstMove++;
                        }
                        else
                        {
                            context.Statistics.BetaCutoffsNotAtFirstMove++;
                        }
#endif

                        context.Statistics.BetaCutoffs++;
                        break;
                    }
                }

postLoopOperations:
                if (!loudMovesGenerated)
                {
                    movesCount = context.BoardState.GetLoudMoves(moves, 0, evasionMask);
                    MoveOrdering.AssignLoudValues(context.BoardState, moves, moveValues, movesCount, depth, hashMove);
                    moveIndex          = -1;
                    loudMovesGenerated = true;

#if DEBUG
                    context.Statistics.LoudMovesGenerated++;
#endif

                    if (movesCount == 0)
                    {
                        movesCount = context.BoardState.GetQuietMoves(moves, 0, evasionMask);
                        MoveOrdering.AssignQuietValues(context.BoardState, moves, moveValues, 0, movesCount, ply);
                        quietMovesGenerated = true;

#if DEBUG
                        context.Statistics.QuietMovesGenerated++;
#endif
                    }
                }

                if (!quietMovesGenerated && moveIndex == movesCount - 1)
                {
                    var loudMovesCount = movesCount;

                    movesCount = context.BoardState.GetQuietMoves(moves, movesCount, evasionMask);
                    MoveOrdering.AssignQuietValues(context.BoardState, moves, moveValues, loudMovesCount, movesCount, ply);
                    quietMovesGenerated = true;

#if DEBUG
                    context.Statistics.QuietMovesGenerated++;
#endif
                }
            }

            // Don't save invalid scores to the transposition table
            if (context.AbortSearch)
            {
                return(0);
            }

            if (allMovesPruned)
            {
                return(alpha);
            }

            // Don't add invalid move (done after checkmate) to prevent strange behaviors
            if (bestScore == -SearchConstants.NoKingValue)
            {
                return(bestScore);
            }

            // Return draw score or checkmate score as leafs
            if (bestScore == SearchConstants.NoKingValue)
            {
                if (friendlyKingInCheck)
                {
                    return(-EvaluationConstants.Checkmate + ply);
                }

                return(0);
            }

            if (entry.Flags == TranspositionTableEntryFlags.Invalid || alpha != originalAlpha)
            {
                if (entry.Age != context.TranspositionTableEntryAge || entry.Depth <= depth)
                {
                    var valueToSave = alpha;
                    var entryType   = alpha <= originalAlpha ? TranspositionTableEntryFlags.AlphaScore :
                                      alpha >= beta ? TranspositionTableEntryFlags.BetaScore :
                                      TranspositionTableEntryFlags.ExactScore;

                    valueToSave = TranspositionTable.RegularToTTScore(alpha, ply);

                    TranspositionTable.Add(context.BoardState.Hash, new TranspositionTableEntry(
                                               context.BoardState.Hash,
                                               (short)valueToSave, bestMove,
                                               (byte)depth, entryType,
                                               (byte)context.TranspositionTableEntryAge)
                                           );

#if DEBUG
                    if (entry.Flags != TranspositionTableEntryFlags.Invalid)
                    {
                        context.Statistics.TTReplacements++;
                    }

                    context.Statistics.TTAddedEntries++;
#endif
                }
            }

            return(bestScore);
        }