Пример #1
0
        public (Move move, Statistics statistics) Search(Position position, ITimeStrategy timeStrategy, Statistics.PrintInfoDelegate printInfoDelegate)
        {
            // setup
            _timeStrategy = timeStrategy;
            _enteredCount = 0;
            _statistics   = new Statistics();
            _statistics.Timer.Start();
            _statistics.SideCalculating = position.SideToMove;
            _pvTable = new TriangularPVTable(); // TODO: should we be passing this in instead?
            _qSearch.StartSearch(_timeStrategy, _pvTable, _statistics);

            Move bestMove = Move.Null;
            var  moveList = new List <Move>();

            _moveGenerator.Generate(moveList, position);

            var cachedPositionObject = new Position();

            if (!_moveGenerator.OnlyLegalMoves)
            {
                // remove all illegal moves
                for (int i = moveList.Count - 1; i >= 0; i--)
                {
                    var move            = moveList[i];
                    var testingPosition = Position.MakeMove(cachedPositionObject, move, position);
                    if (testingPosition.MovedIntoCheck())
                    {
                        moveList.QuickRemoveAt(i);
                    }
                }
            }

            var scoredMoveList = new List <ScoredMove>();

            foreach (var move in moveList)
            {
                scoredMoveList.Add(new ScoredMove(move, 0));
            }

            // TODO: instead of looping here, why don't we only loop in InnerSearch and get the best value from the PV table?
            // That would simplify things a lot.
            // However, if we have aspiration windows and we get a beta cutoff, how do we retrieve the best move? Or is that even required?
            // The PV table would probably need to handle that case.
            for (int depth = 1;; depth++)
            {
                _statistics.NormalNonLeafNodes++;

                Score alpha = Score.MinValue;
                Score beta  = Score.MaxValue;

                for (int i = 0; i < scoredMoveList.Count; i++)
                {
                    var move = scoredMoveList[i].Move;

                    var nextPosition = Position.MakeMove(cachedPositionObject, move, position);
                    _pvTable.Add(move, 0);

                    _statistics.CurrentDepth = depth;
                    var nextEval = -InnerSearch(nextPosition, depth - 1, false, -beta, -alpha, 1);
                    if (nextEval == alpha)
                    {
                        nextEval -= 1;
                    }

                    scoredMoveList[i] = new ScoredMove(move, nextEval);

                    if (timeStrategy.ShouldStop(_statistics))
                    {
                        _statistics.Timer.Stop();
                        return(bestMove, _statistics);
                    }

                    if (nextEval > alpha)
                    {
                        alpha    = nextEval;
                        bestMove = move; // this is safe, because we search the best move from last pass first in the next pass
                        _pvTable.Commit(0);
                        _statistics.BestLine = _pvTable.GetBestLine();
                        if (position.SideToMove == Color.White)
                        {
                            _statistics.CurrentScore = alpha;
                        }
                        else
                        {
                            _statistics.CurrentScore = -alpha;
                        }

                        printInfoDelegate(_statistics);
                    }
                }

                // if we don't do this, we'll never return from a terminal position (with no moves)
                if (timeStrategy.ShouldStop(_statistics))
                {
                    _statistics.Timer.Stop();
                    return(bestMove, _statistics);
                }

                // sort the moves for next pass
                scoredMoveList.Sort((m1, m2) => (int)(m2.Score - m1.Score));
            }
        }
Пример #2
0
        public (Move move, Statistics statistics) Search(Position position, ITimeStrategy timeStrategy, Statistics.PrintInfoDelegate printInfoDelegate)
        {
            // setup
            _timeStrategy = timeStrategy;
            _enteredCount = 0;
            _statistics   = new Statistics();
            _statistics.Timer.Start();
            _statistics.SideCalculating = position.SideToMove;
            _pvTable = new TriangularPVTable(); // TODO: should we be passing this in instead?
            _qSearch.StartSearch(_timeStrategy, _pvTable, _statistics);

            Move bestMove = Move.Null;
            var  moveList = new List <Move>();

            _moveGenerator.Generate(moveList, position);

            // TODO: instead of looping here, why don't we only loop in InnerSearch and get the best value from the PV table?
            // That would simplify things a lot.
            // However, if we have aspiration windows and we get a beta cutoff, how do we retrieve the best move? Or is that even required?
            // The PV table would probably need to handle that case.
            var tmpBestMove = Move.Null;

            for (int depth = 1;; depth++)
            {
                _statistics.NormalNonLeafNodes++;

                Score alpha = Score.MinValue;
                Score beta  = Score.MaxValue;

                var cachedPositionObject = new Position();
                foreach (var move in moveList)
                {
                    var nextPosition = Position.MakeMove(cachedPositionObject, move, position);

                    if (!_moveGenerator.OnlyLegalMoves && nextPosition.MovedIntoCheck())
                    {
                        continue;
                    }

                    _pvTable.Add(move, 0);

                    _statistics.CurrentDepth = depth;
                    var nextEval = -InnerSearch(nextPosition, depth - 1, -beta, -alpha, 1);

                    if (timeStrategy.ShouldStop(_statistics))
                    {
                        _statistics.Timer.Stop();
                        return(bestMove, _statistics);
                    }

                    if (nextEval > alpha)
                    {
                        alpha       = nextEval;
                        tmpBestMove = move;
                        _pvTable.Commit(0);
                    }
                }

                // only committing best move after a full search
                // TODO: this will go away once we're no longer doing a search at this level
                bestMove             = tmpBestMove;
                _statistics.BestLine = _pvTable.GetBestLine();
                if (position.SideToMove == Color.White)
                {
                    _statistics.CurrentScore = alpha;
                }
                else
                {
                    _statistics.CurrentScore = -alpha;
                }

                // if we don't do this, we'll never return from a terminal position (with no moves)
                if (timeStrategy.ShouldStop(_statistics))
                {
                    _statistics.Timer.Stop();
                    return(bestMove, _statistics);
                }

                // if we didn't return, let's print some info!
                printInfoDelegate(_statistics);
            }
        }