Пример #1
0
        public SearchResults Go(Board board, Colour colour, int maxDepth)
        {
            stopWatch.Restart();

            PositionCount = 0;

            transpositionTable.NextIteration();

            var transpositionKey = board.Key;

            var nodeMoves = new List <uint>(256);

            moveGenerator.Generate(board, colour, nodeMoves);

            var moveEvaluations = new List <MoveEvaluation>(128);

            // http://mediocrechess.blogspot.com/2006/12/programming-extracting-principal.html
            var principalVariations = new uint[maxDepth][];

            for (var i = 0; i < maxDepth; ++i)
            {
                principalVariations[i] = new uint[i + 1];
            }

            if (!nodeMoves.Any())
            {
                return(new SearchResults(PositionCount, new List <long>(), moveEvaluations, principalVariations, transpositionTable));
            }

            var existingTransposition = transpositionTable.Find(transpositionKey);

            if (existingTransposition.Key != 0)
            {
                if (existingTransposition.BestMove != 0)
                {
                    if (nodeMoves.First() != existingTransposition.BestMove)
                    {
                        nodeMoves.Remove(existingTransposition.BestMove);
                        nodeMoves.Insert(0, existingTransposition.BestMove);
                    }
                }
            }

            var currentMaxDepth = 1;

            var lap = stopWatch.ElapsedMilliseconds;

            var iterationLaps = new List <long>();

            Info info;

            while (currentMaxDepth <= maxDepth)
            {
                var masterPrincipalVariation = principalVariations[currentMaxDepth - 1];

                var principalVariation = new uint[64];

                moveEvaluations.Clear();

                var bestScore = int.MinValue;
                var bestMove  = 0u;

                foreach (var move in nodeMoves)
                {
                    var nextDepth = (byte)(currentMaxDepth - 1);

                    var moveView = new MoveViewer(move);

                    board.MakeMove(move);

                    var evaluatedScore = -PrincipalVariationSearch(board, colour.Opposite(), nextDepth, 1, int.MinValue, int.MaxValue, principalVariation);

                    board.UnMakeMove(move);

                    if (evaluatedScore > bestScore)
                    {
                        bestMove  = move;
                        bestScore = evaluatedScore;

                        UpdatePrincipalVariation(principalVariation, masterPrincipalVariation, 0, move);

                        info = new InfoNewPv(PositionCount, stopWatch.ElapsedMilliseconds, currentMaxDepth, bestMove, bestScore, transpositionTable);

                        Info?.Invoke(this, new InfoEventArgs(info));
                    }

                    moveEvaluations.Add(new MoveEvaluation(moveView, evaluatedScore));
                }

                nodeMoves = moveEvaluations
                            .OrderByDescending(x => x.Score)
                            .Select(x => x.Move.Value)
                            .ToList();

                transpositionTable.Set(transpositionKey, (byte)currentMaxDepth, colour, bestScore, bestMove);

                var iterationLength = stopWatch.ElapsedMilliseconds - lap;

                lap = stopWatch.ElapsedMilliseconds;

                iterationLaps.Add(iterationLength);

                info = new InfoDepthComplete(PositionCount, stopWatch.ElapsedMilliseconds, currentMaxDepth, masterPrincipalVariation, transpositionTable);

                Info?.Invoke(this, new InfoEventArgs(info));

                ++currentMaxDepth;
            }

            stopWatch.Reset();

            return(new SearchResults(PositionCount, iterationLaps, moveEvaluations, principalVariations, transpositionTable));
        }
Пример #2
0
        private int PrincipalVariationSearch(Board board, Colour colour, byte depth, ushort ply, int alpha, int beta, uint[] parentPrincipalVariation)
        {
            ++PositionCount;

            if (depth == 0)
            {
                return(positionEvaluator.Evaluate(board) * (colour == Colour.White ? 1 : -1));
            }

            var transpositionKey = board.Key;

            var existingTransposition = transpositionTable.Find(transpositionKey);

            uint previousBestMove = 0;

            if (existingTransposition.Key != 0)
            {
                if (existingTransposition.Depth > 1 && existingTransposition.Depth > depth)
                {
                    return(existingTransposition.Evaluation);
                }

                if (existingTransposition.BestMove != 0)
                {
                    previousBestMove = existingTransposition.BestMove;
                }
            }

            var bestScore = int.MinValue;
            var bestMove  = 0u;

            var principalVariation = new uint[64];

            var moveCount = 1;

            foreach (var move in GetNextMove(moveGenerator, ply, board, colour, previousBestMove))
            {
                board.MakeMove(move);

                var evaluatedScore = 0;

                var oppositeColour = colour.Opposite();

                var nextDepth = (byte)(depth - 1);
                var nextPly   = (ushort)(ply + 1);

                // https://www.chessprogramming.org/Principal_Variation_Search
                if (moveCount == 1)
                {
                    // Always do a full search on the first/PV move
                    evaluatedScore = -PrincipalVariationSearch(board, oppositeColour, nextDepth, nextPly, -beta, -alpha, principalVariation);
                }
                else
                {
                    // Late Move Reduction http://mediocrechess.blogspot.com/2007/03/other-late-move-reduction-lmr.html
                    if (ply > 3 && moveCount > 3 && move.GetCapturePieceType() == PieceType.None && move.GetNumCheckers() == 0 && nextDepth > 0)
                    {
                        --nextDepth;
                    }

                    // Search with aspiration window
                    evaluatedScore = -PrincipalVariationSearch(board, oppositeColour, nextDepth, nextPly, -alpha - 1, -alpha, principalVariation);

                    if (alpha < evaluatedScore && evaluatedScore < beta)
                    {
                        // Re-search
                        evaluatedScore = -PrincipalVariationSearch(board, oppositeColour, nextDepth, nextPly, -beta, -evaluatedScore, principalVariation);
                    }
                }

                board.UnMakeMove(move);

                if (alpha < evaluatedScore)
                {
                    // alpha = Math.Max(bestScore, evaluatedScore); // alpha acts like max in MiniMax
                    alpha = evaluatedScore;

                    bestMove  = move;
                    bestScore = evaluatedScore;

                    UpdatePrincipalVariation(principalVariation, parentPrincipalVariation, ply, move);
                }

                if (alpha > beta)
                {
                    // Fail-hard beta-cutoff
                    break;
                }

                ++moveCount;

                if (ply == 1)
                {
                    var info = new Info(PositionCount, stopWatch.ElapsedMilliseconds, depth, transpositionTable);

                    Info?.Invoke(this, new InfoEventArgs(info));
                }
            }

            transpositionTable.Set(transpositionKey, depth, colour, bestScore, bestMove);

            if (alpha == int.MinValue)
            {
                alpha = PieceValues.CheckmateValue + ply;
            }

            if (alpha == int.MaxValue)
            {
                alpha = -(PieceValues.CheckmateValue + ply);
            }

            return(alpha);
        }