- Alpha-beta pruning
- One form of pruning is especially useful for any adversarial situation. It avoids evaluating many positions, but still returns the same result it would if it had evaluated them all. Suppose you’ve analyzed one of your possible moves and determined that your opponent’s best reply will lead to no change in relative advantage. Now you are about to examine another of your possible moves. If you find that one response your opponent might make leads to the loss of one of your pieces, you need not examine the rest of your opponent’s replies. You don’t care about finding out whether he may be able to checkmate you instead, because you already know that this move is not your best choice. So, you skip further analysis of this move and immediately go on to examine alternate moves that you actually might make.
- Of course, the analysis of the opponent’s moves can use the same strategy. The algorithm that implements this is a slight variation of minimax called alpha-beta pruning. It uses two additional parameters, alpha and beta, to record the lower and upper cutoff bounds that are to be applied. The caller doesn’t have to provide these parameters; they are initalized internally. Like minimax, this routine is recursive. Note that on the recursive calls, the parameters $alpha and $beta are swapped and negated. That corresponds to the change of viewpoint as it becomes the other player’s turn to play.
- # Usage:
- # To minimize the next move:
- # ($move,$score) = ab_minimax($position,$depth)
- # You provide a game position object, and a maxmimum depth
- # (number of moves) to be expanded before cutting off the
- # move generation and evaluating the resulting position.
- sub ab_minimax {
- my ( $position, $depth, $alpha, $beta ) = @_;
- defined ($alpha) or $alpha = -$position->best_rating;
- defined ($beta) or $beta = $position->best_rating;
- # Have we gone as far as permitted or as far as possible?
- if ( $depth-- and defined($position->prepare_moves) ) {
- # no - keep trying additional moves from $position
- my $move;
- my $best_score = -$position->best_rating;
- my $best_move_seq;
- my $alpha_cur = $alpha;
- while ( defined($move = $position->next_move) ) {
- # Evaluate the next move.
- my ( $this_move_seq, $this_score ) =
- ab_minimax( $position->make_move($move),
- $depth, -$beta, -$alpha_cur );
- # Opponent's score is opposite meaning from ours.
- $this_score = -$this_score;
- if ( $this_score > $best_score ) {
- $best_score = $this_score;
- $alpha_cur = $best_score if $best_score > $alpha_cur;
- $best_move_seq = $this_move_seq;
- unshift ( @$best_move_seq, $move );
- # Here is the alpha-beta pruning.
- # - quit when someone else is ahead!
- last if $best_score >= $beta;
- }
- }
- # Return the best one we found.
- return ( $best_move_seq, $best_score );
- } else {
- # Yes - evaluate current position, no move to be taken.
- return ( [ $position ], -$position->evaluate );
- }
- }