Ejemplo n.º 1
0
        // all moves need to be legal
        private void SortWithIid(Position position, List <Move> moves, int depth, Score alpha, Score beta, int ply)
        {
            // SortWithIid is considered a root node; it's not a leaf of the parent caller, it's more of a helper
            _statistics.NormalNonLeafNodes++;

            var cachedPositionObject = _positionCache.Get(ply);

            // create .5 pawn buffer in alpha and beta
            alpha -= 50;
            if (alpha < Score.MinValue)
            {
                alpha = Score.MinValue;
            }

            beta += 50;
            if (beta > Score.MaxValue)
            {
                beta = Score.MaxValue;
            }

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

            // TODO: use a Span<ScoredMove>, once the version of .net core we're using supports Span.Sort()
            var scoredMoves = _iidScoredMoveListCache.Get(ply);

            scoredMoves.Clear();

            foreach (var move in _moveOrderer.Order(moves, position))
            {
                var nextPosition = Position.MakeMove(cachedPositionObject, move, position);

                var score = -InnerSearch(nextPosition, (depth / 2) - 1, true, -beta, -alpha, ply + 1);
                // leave a buffer for better ordering
                if (score - 50 > alpha)
                {
                    alpha = score - 50;
                }
                //else if (score == alpha)
                //    score -= 1; // artificially lower alpha cutoffs so they don't look like PVs

                scoredMoves.Add(new ScoredMove(move, score));
            }

            // sort descending
            scoredMoves.Sort((m1, m2) => (int)(m2.Score - m1.Score));

            // copy back to moves
            for (int i = 0; i < scoredMoves.Count; i++)
            {
                moves[i] = scoredMoves[i].Move;
            }
        }
Ejemplo n.º 2
0
        public Score InnerSearch(Position position, int depth, Score alpha, Score beta, int ply)
        {
            _statistics.QSearchNonRootNodes++;

            _statistics.MaxPly = Math.Max(_statistics.MaxPly, ply);
            _statistics.Evaluations++;
            var standPatEval = _evaluator.Evaluate(position);

            // correct so side to move wants to maximize value
            if (position.SideToMove == Color.Black)
            {
                standPatEval = -standPatEval;
            }

            if (standPatEval >= beta)
            {
                return(standPatEval);
            }

            bool raisedAlpha = false;

            // TODO: what to do if standpat does/doesn't raise alpha???
            if (standPatEval > alpha)
            {
                raisedAlpha = true;
                alpha       = standPatEval;
            }

            // for now, let's do this to prevent qsearch explosion
            // TODO: should we do something in order to return a conservatively low value?
            if (depth > MaxDepth)
            {
                return(standPatEval);
            }

            var moves = _moveListCache.Get(ply);

            moves.Clear();

            bool moveGenIsStrictlyLegal;
            IEnumerable <Move> moveEnumerator;

            if (position.InCheck())
            {
                // if we're in check, we need to try all moves
                _moveGenerator.Generate(moves, position);
                moveGenIsStrictlyLegal = _moveGenerator.OnlyLegalMoves;
                moveEnumerator         = _checkEvasionsMoveOrderer.Order(moves, position);
            }
            else
            {
                // if not in check, then only look at captures and promotions
                _moveGenerator.Generate(moves, position);
                moveGenIsStrictlyLegal = _moveGenerator.OnlyLegalMoves;

                // since we don't have a qsearch movegen, we need to remove the non-applicable moves
                for (int i = moves.Count - 1; i >= 0; i--)
                {
                    var move = moves[i];
                    if (!move.MoveType.HasFlag(MoveType.Capture) && !move.MoveType.HasFlag(MoveType.Promotion))
                    {
                        moves.QuickRemoveAt(i);
                    }
                }

                // TODO: we should use SEE to determine which moves to keep!

                moveEnumerator = _quiescenceMoveOrderer.Order(moves, position);
            }

            int moveNumber           = 0;
            var cachedPositionObject = _positionCache.Get(ply);

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

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

                moveNumber++;

                var eval = -InnerSearch(nextPosition, depth + 1, -beta, -alpha, ply + 1);

                if (eval >= beta)
                {
                    _statistics.QSearchCutNodes++;
                    _statistics.QSearchCutMoveMisses += moveNumber - 1; // don't include the current move in the move misses calculation
                    return(eval);
                }

                if (eval > alpha)
                {
                    raisedAlpha = true;
                    alpha       = eval;
                }
            }

            // TODO: if there were no legal moves, then maybe we should do a terminal check? it's dangerous not to, but maybe kinda slow to do this

            if (raisedAlpha)
            {
                // TODO: eventually commit to triangular pv table? Maybe?
                _statistics.QSearchPVNodes++;
            }
            else
            {
                _statistics.QSearchAllNodes++;
            }

            return(alpha);
        }