No. The search speed is limited. And positional understanding is weakened (the lower the ELO, the more positional evaluation terms are set to zero).I have questions here, when doing multi-pv (when move/blunder error is greater than 0) do you use the full strength personality?
Perhaps some code will help. First, how to decide the error this move.
Code: Select all
// Determine score error this move.
int intScoreErrorThisMove = 0;
if (BlunderError > 0)
{
if (_objRandom.Next(0, 101) <= BlunderPercent)
{
// Blunder
intScoreErrorThisMove = BlunderError;
}
}
intScoreErrorThisMove = Math.Max(intScoreErrorThisMove, MoveError);
SearchState.HasError = intScoreErrorThisMove > 0;
Pass the move error to the Aspiration Window / PVS search.
Code: Select all
// Search moves with aspiration window.
int intScore = SearchMovesWithAspirationWindow(intHorizon, intScoreErrorThisMove);
By default my engine searches with an Aspiration Window of 50, then 200, then 500, then infinite.
Code: Select all
private int SearchMovesWithAspirationWindow(int Horizon, int ScoreErrorThisMove)
{
if ((Horizon == 1) || (SearchState.BestMoves.Count == 0))
{
// Search moves with infinite aspiration window.
return Search.Recurse(SearchState, 0, Horizon, -PositionalEvaluation.MaxScore, PositionalEvaluation.MaxScore);
}
// Found a best move from prior iteration.
int intAspirationScore = SearchState.BestMoves[0].Score;
int intAlpha = intAspirationScore - ScoreErrorThisMove;
int intBeta = intAspirationScore;
ScorePrecision enuScorePrecision = ScorePrecision.Exact;
foreach (int intAspirationWindow in Search.AspirationWindows)
{
// Reset scored moves.
foreach (ScoredMove objScoredMove in SearchState.ScoredMoves)
{
objScoredMove.Score = -PositionalEvaluation.MaxScore;
objScoredMove.ScorePrecision = ScorePrecision.LowerBound;
}
// Adjust alpha / beta window.
SearchState.SearchStats.AspirationNodes++;
switch (enuScorePrecision)
{
case ScorePrecision.UpperBound:
// Fail low
intAlpha -= intAspirationWindow;
break;
case ScorePrecision.LowerBound:
// Fail high
intBeta += intAspirationWindow;
break;
default:
// Initial aspiration window
intAlpha -= intAspirationWindow;
intBeta += intAspirationWindow;
break;
}
// Search moves with aspiration window.
int intBestScore = Search.Recurse(SearchState, 0, Horizon, intAlpha, intBeta);
if (Math.Abs(intBestScore) == PositionalEvaluation.InterruptedSearchScore)
{
// Stop searching.
return intBestScore;
}
if (intBestScore >= intBeta)
{
// Fail high
enuScorePrecision = ScorePrecision.LowerBound;
Search.UpdateInfoScoreFailed(SearchState, Horizon, true, intBestScore, enuScorePrecision);
continue;
}
// Find lowest score.
int intLowestScore;
if (Search.MultiPv == 1)
{
// One principal variation scored.
intLowestScore = intBestScore;
}
else
{
// Multiple principal variations scored.
_colBestMoves.Clear();
SearchState.ScoredMoves.AddBestMoves(_colBestMoves, Search.MultiPv);
intLowestScore = _colBestMoves[_colBestMoves.Count - 1].Score;
}
if (intLowestScore <= intAlpha)
{
// Fail low
enuScorePrecision = ScorePrecision.UpperBound;
Search.UpdateInfoScoreFailed(SearchState, Horizon, true, intBestScore, enuScorePrecision);
continue;
}
// Score within aspiration window.
SearchState.SearchStats.AspirationNodesCorrect++;
return intBestScore;
}
// Search moves with infinite aspiration window.
return Search.Recurse(SearchState, 0, Horizon, -PositionalEvaluation.MaxScore, PositionalEvaluation.MaxScore);
}
Notice how the Aspiration Window bounds are checked. This is not a requirement of Limit ELO but MultiPV. The best score cannot be compared against the high and low bounds. Rather, the best score is compared against the high bound, and the lowest MultiPV score is compared against the low bound.
Code: Select all
// Multiple principal variations scored.
_colBestMoves.Clear();
SearchState.ScoredMoves.AddBestMoves(_colBestMoves, Search.MultiPv);
intLowestScore = _colBestMoves[_colBestMoves.Count - 1].Score;
The MultiPV code is a bit distracting. To be clear, a Limit ELO search uses MultiPV = 1, uses Aspiration Windows (with initial alpha set low enough to account for move error), and uses PVS. One more piece of code is critical.
Code: Select all
if (intScore > intBestScore)
{
// New principal variation
intBestScore = intScore;
intPvMoveNumber = intLegalMoveNumber;
if (intLegalMoveNumber > 1)
{
// Principal variation incorrect.
bolPvCorrect = false;
}
// Update best move cache.
UpdateBestMoveCache(SearchState, Depth, Horizon, objMove, intScore, Alpha, Beta);
if ((Depth > 0) || ((MultiPv == 1) && !SearchState.HasError))
{
// Update alpha.
Alpha = intScore;
}
}