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