Including a search strategy also makes room for new challenges. Negascouting is the result of narrowing the search by improving the pruning heuristic. It is based on a concept called a search window, which is the interval between the alpha and beta values. So, reducing the search window increases the chance of a branch being pruned.
It is important to know the difference between a dynamic member function and a static member function, as well as recursion. A dynamic member function is bound to the instance of the class, while the static member function is bound to the class itself. The static method allows us to call it without instantiating an object. This is great for general-purpose algorithms, such as the one we are developing in this recipe.
In the case of recursion, it's not always clear that (unlike iteration) this is an iterative process that requires a base case (also called the stop condition) and a recursive case (the one to keep iterating).
We will add a new function to the BoardAI
class as follows:
ABNegascout
function:public static float ABNegascout ( Board board, int player, int maxDepth, int currentDepth, ref Move bestMove, float alpha, float beta) { // next steps here }
if (board.IsGameOver() || currentDepth == maxDepth) return board.Evaluate(player);
bestMove = null; float bestScore = Mathf.NegativeInfinity; float adaptiveBeta = beta;
foreach (Move m in board.GetMoves()) { // next steps here } return bestScore;
Board b = board.MakeMove(m);
Move currentMove = null; float recursedScore; int depth = currentDepth + 1; float max = Mathf.Max(alpha, bestScore);
recursedScore = ABNegamax(b, player, maxDepth, depth, ref currentMove, -adaptiveBeta, max);
float currentScore = -recursedScore; if (currentScore > bestScore) { // next steps here }
if (adaptiveBeta == beta || currentDepth >= maxDepth - 2) { bestScore = currentScore; bestMove = currentMove; }
else { float negativeBest; negativeBest = ABNegascout(b, player, maxDepth, depth, ref bestMove, -beta, -currentScore); bestScore = -negativeBest; }
if (bestScore >= beta) return bestScore; adaptiveBeta = Mathf.Max(alpha, bestScore) + 1f;
This algorithm works by examining the first move of each node. The following moves are examined using a scout pass with a narrower window based on the first move. If the pass fails, it is repeated using a full-width window. As a result, a large number of branches are pruned and failures are avoided.