Minimax and Alpha-Beta Template


next up previous
Next: Othello implementation Up: Parallel Implementation and Optimization Previous: Introduction

Minimax and Alpha-Beta Template

Explanation of Minimax

The minimax algorithm is a method of selecting the best choice of action in a situation, or game, where two opposing forces, or players, are working toward mutually exclusive goals, acting on the same set of perfect information about the outcome of the situation. It is specifically applied in searching game trees to determine the best move for the current player of a game. It uses the simple principle that at each move, the moving player will choose the best move available to them.

The game tree consists of all moves available to the current player as children of the root, and then all moves available to the next player as children of these nodes, and so forth, as far into the future of the game as desired. Each branch of the tree represents a possible move that player could make at that point in the game. Evaluating the game at a leaf of this tree yields the projected status of the game after that sequence of moves is made by the players. A deeper search of the game tree provides more information about possible advantages or traps and therefore yields a better move.

In a situation, like Othello, with two players, Black and White, Black values moves with high evaluation scores and White values moves with low evaluation scores. A minimax search determines all possible continuations of the game to the desired level, evaluating each possible set of moves and assigning a score. The search then steps back up through the game tree and alternates between choosing the highest child score at nodes representing Black, and the lowest child score at nodes representing White. The score returned is called the ``backed up'' score since it is chosen by backing up through the tree. Thus, stepping through the game tree is equivalent to examining alternating moves between Black and White. Such a forward looking search is beneficial because Black might make a fantastic move at one level, only to allow White to make a move that can be oppositionally fantastic. By looking ahead in this way, Black can make moves that are advantageous, but that don't allow White to make moves negating such benefit.

Figure 1 shows a tree simulates the progression of a game through the next four moves, with Black as the current player. As shown, while Black could possibly obtain higher scores by choosing moves that lead to the left or right trees, those moves expose Black to the risk that White will make moves that lead to the lower scores. Therefore, while the goal score is not the overall optimal score possible after 4 moves, it is the optimal score when White's motives are taken into account.

 
Figure: Minimax Search Tree: Backed-up score shown in nodes

Minimax Algorithm

The minimax algorithm is a recursive algorithm that takes as arguments the current layout of the game board, the depth being searched, and the maximum depth to be searched. It returns the chosen move, and the score that such a move might lead to. The algorithm has a two part base case and then calls minimax on the boards resulting from each possible move at the current level.

The base case of the algorithm evaluates the score for the given board. This occurs in two situations. First, the board will be evaluated if the current depth of the search is the maximum desired depth. Second, the board will be evaluated if there are no possible moves for either player, signaling a possible end to the game.

Should there be moves for the current player and the maximum depth hasn't yet been searched, minimax generates possible moves at that level, and applies them in turn to the current board, calling minimax on the resulting board. Each returned score is compared to the current best. If the returned score is more advantageous for the current player, that score replaces the best score and the move that generated it replaces the best move.

Minimax Psuedocode

minimax(in game board, in int depth, in int max_depth,
        out score chosen_score, out score chosen_move)
begin 
    if (depth = max_depth) then
        chosen_score = evaluation(board);
    else 
        moves_list = generate_moves(board);
        if (moves_list = NULL) then
            chosen_score = evaluation(board);
        else 
            for (i = 1 to moves_list.length) do
                best_score = infinity;
                new_board = board;
                apply_move(new_board, moves_list[i]);
                minimax(new_board, depth+1, max_depth, the_score, the_move);
                if (better(the_score, best_score)) then
                    best_score = the_score;
                    best_move = the_move;
                endif
            enddo
            chosen_score = best_score;
            chosen_move = best_move;
        endif
    endif
end.

Alpha-Beta cutoffs

Since the amount of work a minimax search generates increases exponentially as a move is examined to a greater depth, to reduce the time required for the search it must be restricted so that no time is wasted searching moves that are obviously bad for the player. One key way to do this is to implement alpha-beta cutoffs in the minimax search. Alpha-beta cutoffs work on the previously discussed principle that a player, Black, will not make a seemingly good move if it allows White to make an even better move. Thus, if while examining moves, a move is found with a score worse than the best score now guaranteed, the algorithm will discontinue the search of that branch of the tree. The exact implementation of alpha-beta keeps track of the best move for each side as it moves through the tree. If a move is evaluated that is advantageous for the current player but not for the opponent, then the rest of the moves in the tree are discarded because progressing to that state would not be allowed by one of the players.

Using alpha-beta cutoffs can cause immense speedups in the run of minimax by reducing the size of the tree that must be searched. To illustrate this, Figure 2 shows the above minimax tree adjusted with alpha-beta cutoffs is shown below. The cutoffs reduce the search by 10 nodes, cutting the necessary time by about 30%.

 
Figure: Minimax Search Tree with Alpha-Beta cutoffs: cutoff nodes are shaded

Alpha-Beta Pseudocode

Note: Here Black prefers high scores and White prefers low scores.

minimax (in game board, in int depth, in int max_depth,
         in boolean alpha_beta, in score black_best, in score white_best, 
         out score chosen_score, out score chosen_move)
begin
        :	
        :
        minimax(new_board, depth+1, max_depth,
                alpha_beta, black_best, white_best, 
                the_score, the_move);
        if (alpha_beta) then
            if (to_move(black) and (the_score > black_best))
                if (the_score > white_best)
                    break;  /*  alpha_beta cutoff  */
                else
                    black_best = the_score;
                endif
            endif
            if (to_move(white) and (the_score < white_best))
                if (the_score < white_best)
                    break;  /*  alpha_beta cutoff  */
                else
                    white_best = the_score;
                endif
            endif
        endif
        :
        :
end.

Minimax and Alpha-Beta Types and Functions

Any implementation of minimax and alpha-beta must be supplied with the following types and routines.

board type
This type contains all information specific to the current state of the game, including layout of the board and current player.

score type
This data type indicates piece advantage, strategic advantage, and possible wins. In most games, strategic advantage includes the number of moves available to each player with the goal of minimizing the opponent's mobility.

neg_infinity and pos_infinity
The most extreme scores possible in the game, each most disadvantageous for one player in the game.

generate_moves
This function takes the current board and generates a list of possible moves for the current player.

apply_move
This function takes a board and a move, returning the board with all the updates required by the given move.

null_move
If the chosen game allows or requires a player to forfeit moves in the case where no moves are available, this function takes the current board and returns it, after switching the current player.

static_evaluation
This function takes the board as input and returns a score for the game.

compare_scores
This function takes 2 scores to compare and a player, returning the score that is more advantageous for the given player. If scores are stored as simple integers, this function can be the standard < and > operators.


next up previous
Next: Othello implementation Up: Parallel Implementation and Optimization Previous: Introduction


abierman@cs.caltech.edu