COMP5001 Artificial Intelligence · Week 2 notes

Search

Finding a path from a start to a goal, and choosing a move when someone plays against you

These notes are adapted from the lecture notes of CS50’s Introduction to Artificial Intelligence with Python by Brian Yu and David J. Malan (Harvard University), licensed under CC BY-NC-SA 4.0, and are shared under the same licence. Sections 7 to 11 add material from the chapter on adversarial search and games in Russell and Norvig’s Artificial Intelligence: A Modern Approach (4th ed.).

This week

By the end of the week you should be able to:

Remember Week 1? You wrote pseudocode for a maze. By the end of Section 4 you will know which search algorithm you wrote.

  1. Search problems
  2. Nodes, the frontier and the explored set
  3. Depth-first search
  4. Breadth-first search
  5. DFS and BFS side by side
  6. In the code: maze.py
  7. Adversarial search
  8. Minimax
  9. Alpha-beta pruning
  10. Too big to search to the end
  11. Search in the real world
  12. Check yourself, key terms and reading

1Search problems

A sat-nav finding a route, a person solving a maze and a program solving a sliding-tile puzzle are all doing the same job. Each starts in one situation, wants to reach a goal, and has to find the steps that get it there. That is a search problem.

StartGoalA step is…
Sat-navwhere you arewhere you want to godriving along one road
Mazesquare Asquare Bmoving one square up, down, left or right
15 puzzlea jumbled boardtiles 1 to 15 in ordersliding one tile into the gap

To make a computer solve these problems, we describe each one with the same few words. Learn these: they come back every week until Week 6.

The words for a search problem

Agent
Something that perceives its environment and acts on it. In a sat-nav, the agent is the car.
State
One configuration of the agent and its environment. In a sliding puzzle, one arrangement of the tiles; in a maze, the square you are on.
Initial state
The state where the search starts. Your current location.
Actions
ACTIONS(s) returns the set of actions you can take in state s. In a maze: the open squares next to you.
Transition model
RESULT(s, a) returns the state you reach by doing action a in state s. Slide a tile, and you get a new board.
State space
Every state you can reach from the initial state by any sequence of actions. We draw it as a graph: states are the dots (or boxes), actions are the arrows.
Goal test
A way to check whether a state is a goal. Are we at the destination yet?
Path cost
A number for each path, such as total distance or time. A sat-nav tries to keep it low.

Example: actions depend on the state

1 2 3 4 6 7 5 8 1 2 3 4 6 7 5 8 slide 6 left s RESULT(s, slide 6 left)
Figure 1. A small sliding puzzle. With the gap in the middle, four tiles (2, 4, 5 and 6) could slide in. Sliding 6 left gives a new state: that new board is RESULT(s, slide 6 left).

How many actions are there? It depends where the gap is. With the gap in the middle there are 4 moves, on an edge there are 3, and in a corner only 2. That is why ACTIONS takes the state as its input: different states allow different actions.

Example: a road map, and what “best” means

25 20 25 30 30 35 Winchester Andover Stockbridge Romsey Salisbury Driving times in minutes (made-up but realistic).
Figure 2. A state space drawn as a graph. Each box is a state (being in that town) and each arrow is an action (driving to the next town). Winchester is the initial state; Salisbury is the goal. The numbers are driving times in minutes (made up, but realistic). The green route is the optimal solution.

A solution is any sequence of actions from the initial state to a goal. An optimal solution is a solution with the lowest path cost. Here there are three solutions:

RoutePath cost
Winchester → Andover → Salisbury25 + 30 = 55 minutes
Winchester → Stockbridge → Salisbury20 + 30 = 50 minutes (optimal)
Winchester → Romsey → Salisbury25 + 35 = 60 minutes

Notice that we chose minutes as the path cost. If the path cost were miles or fuel, a different route might be “best”. The seminar comes back to this: choosing the path cost is a design decision, and it affects people.

How big is a state space?

Real state spaces are far too big to draw. The small 3 × 3 puzzle in Figure 1 has 181,440 reachable boards (9!/2). The 4 × 4 15 puzzle has about 10.5 trillion (16!/2). This is why we need a systematic way to search, rather than drawing the whole graph.

2Nodes, the frontier and the explored set

As a search runs, it keeps a small record for every state it reaches. That record is called a node.

Node

A data structure that keeps track of: a state; its parent (the node it came from); the action that was used to get from the parent to here; and the path cost from the initial state to here.

state: Winchester parent: none action: none path cost: 0 state: Stockbridge parent: the Winchester node action: drive to Stockbridge path cost: 20 state: Salisbury parent: the Stockbridge node action: drive to Salisbury path cost: 50 start goal
Figure 3. Three nodes on the road map. Each one points back to its parent. A node does not search; it only holds information.

The parent link is what lets us read off the solution at the end. When the search reaches the goal, it follows the parents back (Salisbury → Stockbridge → Winchester) and then reverses the list of actions: “drive to Stockbridge, then drive to Salisbury”.

The frontier

The frontier is a to-do list: the nodes we have found but not looked at yet. The loop below is the search algorithm. Every search in Weeks 2 and 3 is this loop with one line changed.

frontier = a node with the initial state explored = an empty set repeat: if the frontier is empty: there is no solution remove a node from the frontier if the node is a goal: return the solution add the node to explored expand the node: add each neighbour to the frontier unless it is already in the frontier or explored

Expanding a node means using ACTIONS and RESULT to find its neighbours, then adding them to the frontier as new nodes.

Search algorithm

A step-by-step method that takes a search problem and returns a solution (a sequence of actions from the initial state to a goal), or reports that there is none. Depth-first and breadth-first search (this week) and greedy best-first search and A* (Week 3) are all search algorithms. All of them run this loop; they differ only in which node they take out of the frontier next.

The search tree

Each new node points back to its parent, so as the loop runs, the nodes it creates join up into a tree.

Search tree

The tree of nodes that a search builds as it runs. The root is the node for the initial state. Expanding a node gives it one child for each action, and each child points back to its parent. The root is at depth 0, its children at depth 1, and each level down the tree is one step deeper.

The search tree is not the same thing as the state space. The state space (Figure 2) has one box for each state. The search tree has one node for each path the search has found, so the same state can appear in it more than once. Figure 4 shows the top of the search tree for the road map, with every road usable in both directions.

Winchester0 min Andover25 min Stockbridge20 min Romsey25 min Winchester50 min ⋮ Salisbury55 min Winchester40 min ⋮ Salisbury50 min Winchester50 min ⋮ Salisbury60 min and so on
Figure 4. The top of the search tree for the road map in Figure 2, starting from Winchester, with every road usable in both directions. Each box is a node: a town (its state) and the driving time so far (its path cost). The dashed nodes are Winchester again; below each of them the tree starts again.

Three things to notice:

Why the explored set?

Leave out the three highlighted lines and the search can follow the endless branches in Figure 4: from Winchester to Stockbridge, back to Winchester, and so on for ever. The explored set remembers the states we have already looked at, so we never add them to the frontier again. Then each state gets at most one node, and in a state space of finite size the loop always stops.

Going further: Russell & Norvig call a search that checks for repeated states in this way a graph search, and one that does not a tree-like search.

Which node comes out next?

The loop says “remove a node from the frontier”, but which one? There are two simple choices.

A B C out
Stack: last in, first out, like a pile of plates. C was added last, so C comes out first. This gives depth-first search.
A B C out front back
Queue: first in, first out, like a queue at a café. A was added first, so A comes out first. This gives breadth-first search.

Key idea

This one choice, stack or queue, is the only difference between depth-first and breadth-first search. The rest of the loop is exactly the same. The names come from the search tree: depth-first follows one branch down the tree as far as it goes, and breadth-first finishes each level before starting the next.

3Depth-first search

Depth-first search (DFS) uses a stack, so it always takes the newest node in the frontier. In practice, it goes as deep as it can down one route, and only turns back when it reaches a dead end.

An everyday picture: you are looking for your keys. With depth-first search, you empty every pocket of your jeans before you look anywhere else in the house.

Worked example: DFS on the Week 1 maze

The pictures below use the same rules as the maze program in the practical (Section 6): neighbours are added in the order up, down, left, right. Lilac squares have been taken out of the frontier, with the step number when that happened. Dashed squares are waiting in the frontier. The ringed square is the one just taken out.

3 4 2 A B
After step 4. At step 1, A had two open neighbours: up and right. Right was added last, so DFS took it (steps 2 to 4) and hit a dead end. The square above A has been waiting since step 1.
5 3 4 2 A B
After step 5. The dead end added nothing new, so the newest square left is the one above A. DFS takes it and starts up the left-hand side.
9 10 11 8 12 24 7 13 14 15 21 22 23 6 16 20 5 3 4 17 18 19 2 A B
Finished (step 25). At each later fork DFS again took the newest square. It explored 25 squares and found a 21-step path (green). Three squares were never looked at.
waiting in the frontierexplored (step number)path

In Week 1 you probably found the 17-step path along the top. DFS did not: it stops at the first path it finds, and that path went through the middle.

4Breadth-first search

Breadth-first search (BFS) uses a queue, so it always takes the oldest node in the frontier. It takes one step in every direction, then two steps, then three, like a ripple spreading out from the start.

The keys again: with breadth-first search you check one pocket, then one drawer, then the table, and only after one quick look everywhere do you go back for the second pocket.

Worked example: BFS on the same maze

6 4 2 5 7 3 A B
After step 7. BFS took turns between the two branches next to A: up (2), right (3), up (4), right (5), and so on. The right-hand branch has just reached its dead end (7).
9 10 11 18 20 8 12 16 6 13 14 15 4 17 2 5 7 19 3 A B
After step 20. After the long corridor, the maze splits at step 15. Now BFS grows both routes together, one square each in turn: 16 (top), 17 (lower), 18 (top), 19 (lower), 20 (top).
9 10 11 18 20 23 27 8 12 16 6 13 14 15 4 17 28 2 5 7 19 22 26 3 24 21 25 A B
Finished (step 29). The top route reached B first. BFS explored 29 squares, more than DFS, but its path is 17 steps: the shortest, the same as the Week 1 answer.
waiting in the frontierexplored (step number)path

Why “when every step costs the same”?

BFS finds the path with the fewest steps. In a maze every step costs the same, so fewest steps means shortest. On the road map in Figure 2, steps have different costs. All three routes use two roads, so BFS returns the first one it finds: Winchester adds Andover, Stockbridge and Romsey to the queue in that order, Andover comes out first and adds Salisbury, and BFS returns the route via Andover (55 minutes), not the 50-minute route via Stockbridge. Next week’s A* search takes the costs into account.

Back to Week 1: if your maze pseudocode always took the newest square, you wrote depth-first search. If it took the oldest, you wrote breadth-first search. If it took the square closest to B, you wrote greedy best-first search, which we meet in Week 3.

5DFS and BFS side by side

Here is the search loop from Section 2 (remove a node from the frontier, check it, add it to explored, expand it) run on a small tree instead of a maze, once with a stack and once with a queue. The search starts at A and the goal is D. When a node is expanded, its children join the right-hand end of the frontier, left child first. A stack takes from the right-hand end; a queue takes from the left-hand end.

A B C D E F 1 2 3 4 5 6
DFS (stack): badges show the order nodes were taken out.
StepTake outFrontier after
1AB C
2CB F
3FB
4BD E
5ED
6Dgoal ✓
A B C D E F 1 2 3 4
BFS (queue): E and F are still waiting when it finishes.
StepTake outFrontier after
1AB C
2BC D E
3CD E F
4Dgoal ✓

The two searches split at step 2: DFS takes C (added last) and BFS takes B (added first). Both return the path A → B → D, but BFS took out 4 nodes and DFS took out 6, because DFS went down the C branch first. If D had been under C instead, DFS would have been the lucky one.

The table below sums up the two searches. Its last two rows use two more mazes, maze2.txt and maze3.txt, which come with the maze program in the practical (Section 6).

Depth-first searchBreadth-first search
Frontierstack: last in, first outqueue: first in, first out
Exploresone route as deep as it can, then backs upeverything one step away, then two steps, …
Finds a path if there is one?yes (with an explored set)yes
Shortest path?not guaranteedyes, when every step costs the same
Week 1 maze25 squares explored, 21-step path29 squares explored, 17-step path
maze2.txt194 squares explored, 30-step path77 squares explored, 30-step path
maze3.txt17 squares explored, 16-step path6 squares explored, 4-step path

Key idea

Neither search is always faster: on the Week 1 maze DFS explored fewer squares, on maze2 and maze3 BFS did. Only BFS guarantees the shortest path. DFS and BFS are called uninformed searches, because they use no knowledge about where the goal is. Week 3 adds that knowledge.

6In the code: maze.py

The practical uses week02-search/maze/maze.py. Each part of the loop above is in the code.

class Node():
    def __init__(self, state, parent, action):
        self.state = state       # where we are, e.g. (row, column)
        self.parent = parent     # the node we came from
        self.action = action     # "up", "down", "left" or "right"

There is no path cost in this Node, because every step in a maze costs the same (1). The two frontiers differ in one method only:

# StackFrontier (DFS): take the last
def remove(self):
    node = self.frontier[-1]
    self.frontier = self.frontier[:-1]
    return node
# QueueFrontier (BFS): take the first
def remove(self):
    node = self.frontier[0]
    self.frontier = self.frontier[1:]
    return node

In Python, [-1] is the last item of a list and [0] is the first. The solve method is the loop from Section 2: it removes a node, checks for the goal, adds the node to self.explored, and adds each neighbour that is not already in the frontier or explored.

Try it in the practical

7Adversarial search

In a maze, nobody fights back. In a game, an opponent chooses every other move, and wants the opposite result to you. Searching in this setting is called adversarial search. The classic example is tic-tac-toe (noughts and crosses).

What kind of game?

This week we look at the simplest kind of game. Tic-tac-toe and chess are both of this kind:

You met “fully observable” and “deterministic” in Week 1, as two of the questions we ask about an environment. In a game, a position on the board is a state and a move is an action, so the words from Section 1 still work.

Zero-sum game

A game where one player’s gain is exactly the other player’s loss, so there is no win-win result. In tic-tac-toe a win for X (+1) is a loss for O (−1): the two scores always add up to zero.

Games can be sorted with two questions. Does chance play a part? And can each player see everything?

Deterministic (no chance)Chance (dice, shuffled cards)
Perfect information
(you can see everything)
chess, draughts (checkers), Go, Othellobackgammon, Monopoly
Imperfect information
(something is hidden)
Battleships; blind tic-tac-toe, where you cannot see your opponent’s marksbridge, poker, Scrabble

Table 1. Four kinds of two-player game, sorted by the two questions. The green box is the only kind this module covers.

Key idea

In this module we look only at the green box of Table 1: deterministic games with perfect information, such as tic-tac-toe and chess. Everything that follows, minimax and alpha-beta pruning, is for these games.

The other three boxes need more than search. With dice, a program has to weigh up what is likely to happen; with hidden cards, it has to guess what the opponent holds. Both need probability, which is the subject of Week 5, but we do not come back to games there.

Describing a game

Figure 5. A position with X to move.
S0the initial state: an empty board
PLAYER(s)whose turn it is in state s: X or O
ACTIONS(s)the legal moves: the empty squares
RESULT(s, a)the board after playing in square a
TERMINAL(s)is the game over? (someone has won, or the board is full)
UTILITY(s)the score of a finished game: X wins +1, draw 0, O wins −1

Compare this with a maze: we still have an initial state, actions and a transition model. The goal test becomes TERMINAL, and the path cost becomes UTILITY, because what matters is how the game ends, not how long it takes. X is called MAX, because X wants the score as high as possible. O is MIN, because O wants it as low as possible.

Russell and Norvig use slightly different names for three of these. They write TO-MOVE(s) instead of PLAYER(s), and IS-TERMINAL(s) instead of TERMINAL(s). Their utility function, UTILITY(s, p), has a second input: the player p whose score we want. In a zero-sum game one score is enough, because the other player’s score is the same number with the sign changed.

In Figure 5, X has three empty squares to choose from. Top-middle looks tempting, because it gives X two in the top row. But O has the centre and the bottom-right, so O is threatening the diagonal through top-left. A program finds the right move by looking ahead at the opponent’s replies, which is what minimax does (next section).

The game tree

Start from the initial state and draw every possible move, then every possible reply to each, and so on. The result is a game tree: the search tree (Section 2) of the game, followed all the way to the end of every game. The levels take turns. X (MAX) moves at the top, O (MIN) at the next level down, then X again, until the game ends. Each finished game at the bottom has its utility: +1, 0 or −1. Figure 6 shows the top of the tree for tic-tac-toe.

…………………−1O wins0draw+1X winsYou (MAX)X to moveOpponent (MIN)O to moveYou (MAX)X to moveOpponent (MIN)O to moveFinished gamesthe game is overUtilitythe score
Figure 6. The game tree for tic-tac-toe, after Russell and Norvig. The top row shows all nine first moves. Below that only the left-hand branch is followed, and the dots stand for the parts left out. The bottom row shows three ways the game can end, with their utilities.

To read it, follow the left-hand edge: X plays top-left, O replies top-middle, X plays top-right, and after a few more moves the game ends. The three finished games at the bottom all start that way. In the first, O has completed the middle column (−1). The second is a full board with no line, a draw (0). In the third, X has completed the diagonal from top-right to bottom-left (+1).

Even this small game has a big tree. On the empty board X has 9 squares to choose from, then O has 8, then X has 7, and so on. That gives at most 9 × 8 × 7 × … × 1 = 362,880 paths from the top to the bottom. There are fewer real games (255,168), because many games end before the board is full. Section 8 looks at a small part of this tree, starting from the position in Figure 5.

Ply

A ply (rhymes with “fly”) is one move by one player. Chess players often say “a move” for a turn by each side, so a single player’s turn is called a ply to avoid confusion. Down the left-hand edge of Figure 6, X’s first move is one ply, O’s reply is the second and X’s next move the third.

A game small enough to draw in full

Tic-tac-toe’s tree does not fit on a page, so here is one that does. In this tiny game of Nim there is one pile of 3 counters. On your turn you take 1 or 2 counters. Whoever takes the last counter loses. Figure 7 is the whole tree.

take 1take 2take 1take 2take 1take 1none left+1you winnone left+1you winnone left−1you loseYou (MAX)to moveOpponent (MIN)to moveYou (MAX)to moveOpponent (MIN)to move
Figure 7. The complete game tree for Nim with 3 counters. Each box is a state: the counters left in the pile. Each line is an action: take 1 or take 2. The dashed boxes are finished games, scored +1 when your opponent took the last counter (you win) and −1 when you did (you lose).

Nim can be described with the same six words as tic-tac-toe (the table beside Figure 5). The initial state is the pile of 3 counters, and any later state is the number of counters left. The players take turns. The actions are “take 1” and “take 2”, and the result of an action is a smaller pile. The game is over (TERMINAL) when no counters are left, and the utility is +1 if your opponent took the last one and −1 if you did. Because the whole tree is on the page, you can see every way the game can go: there are only three. In Section 8 you will be able to work out the best first move from this tree, and there is a question on it at the end. A bigger version of Nim comes back in Week 7, as the game a program learns to play by playing against itself.

A game is not a maze

In a maze, the answer is a fixed list of moves: up, up, left, and so on. In a game you cannot write that list in advance, because you do not know what your opponent will do. So the answer to a game is a strategy instead.

Strategy

A rule that says which move to make in every position you might reach, whatever the opponent does. Minimax (next section) finds the best strategy, assuming the opponent plays as well as possible too.

Games also have time limits. A chess player has a clock, and a program usually cannot search all the way to the end of the game before its time runs out. So it has to make a good guess about how good a position is. Section 10 shows how.

8Minimax

Minimax assumes the opponent plays perfectly. MAX imagines each of its moves, then imagines MIN’s best reply to it, and so on to the end of the game. Then it works back up: MIN always picks the lowest score, and MAX the highest.

Minimax value

The score MAX ends up with from a state if both players play perfectly from there to the end. It is the best score MAX can be sure of, whatever MIN does. It is worked out from the bottom of the tree up:

MAX then plays the move that leads to the highest minimax value.

Example: the board in Figure 5

0 0 0draw +1X wins −1 −1O wins +1X wins −1 −1O wins 0draw You (MAX)Opponent (MIN)Final score picks the highestpicks the lowestof each game
Figure 8. Minimax on the board in Figure 5. Middle row: X’s three possible moves (the new X’s square is tinted green). Bottom row: O’s two replies to each (the new O’s square is tinted purple). Where X’s last square is forced, it is drawn faint. A red line marks a win.

Worked example

  1. Score the finished games: X wins +1, draw 0, O wins −1.
  2. O picks the lowest under each of X’s moves. After top-middle or bottom-left, O takes top-left and wins: −1. After the block at top-left, O’s best reply leads to a draw: 0 (its other reply would let X complete the top row).
  3. X picks the highest of −1, −1 and 0: the block at top-left, worth 0. The attack at top-middle looked good, but it ignored O’s threat.

The same idea with numbers

Real games have many more moves, so we draw the tree with numbers instead of boards. Figure 9 has the same shape: MAX chooses a move, MIN replies, and the bottom row holds the scores of finished games. The scores can be any numbers, not just +1, 0 and −1: higher is better for MAX.

4 8 5 9 3 7 2 4 6 432 4 You (MAX) Opponent (MIN) Final score picks the highest picks the lowest of each game left middle right
Figure 9. MAX (the dark triangle) has three moves. After each one, MIN (the light triangles) has three replies. The numbers at the bottom are the scores of the finished games.

Worked example

  1. Work from the bottom up. Each MIN node takes the lowest of its children: left min(4, 8, 5) = 4; middle min(9, 3, 7) = 3; right min(2, 4, 6) = 2.
  2. MAX takes the highest of those: max(4, 3, 2) = 4. MAX plays the left move.

The middle move has a 9 under it, but MIN will never let MAX get it: after the middle move, MIN replies with the 3. Minimax chooses the move with the best worst case.

In pseudocode, two functions call each other:

function MAX-VALUE(state): if TERMINAL(state): return UTILITY(state) v = −∞ for action in ACTIONS(state): v = MAX(v, MIN-VALUE( RESULT(state, action))) return v
function MIN-VALUE(state): if TERMINAL(state): return UTILITY(state) v = +∞ for action in ACTIONS(state): v = MIN(v, MAX-VALUE( RESULT(state, action))) return v

Given a state s, MAX picks the action a with the highest MIN-VALUE(RESULT(s, a)), and MIN picks the action with the lowest MAX-VALUE(RESULT(s, a)). Why start at −∞ in MAX-VALUE? So that the first real score is always bigger and replaces it. MIN-VALUE starts at +∞ for the same reason. A function that calls itself, here through the other function, is called recursive: you need to be able to read it, not write it. Russell and Norvig add a third function for that first step, which returns MAX’s best action, not just its value.

Tracing it once: for the left MIN node in Figure 9, MIN-VALUE starts with v = +∞, then v = min(+∞, 4) = 4, then min(4, 8) = 4, then min(4, 5) = 4, and returns 4.

How good is minimax?

Russell and Norvig judge every search algorithm with the same four questions. You have met two of them already: the table in Section 5 asked whether DFS and BFS find a path if there is one (is the search complete?) and whether that path is the shortest (is it optimal?). The other two ask how much time and how much memory the search needs. Two numbers come up in those answers. Suppose every position has about the same number of legal moves, and call that number b, the branching factor. Suppose a game lasts about m ply. Here are the answers for minimax.

QuestionAnswer for minimax
Complete? Does it always find an answer?Yes, as long as every game ends (the tree is finite). Chess has rules to make sure games end: for example, a player can claim a draw when the same position has come up three times.
Optimal? Does it find the best move?Yes, against an opponent who also plays perfectly. See below for a weaker opponent.
Time: how many positions does it look at?Every position in the tree: about bm (worked out below).
Space: how much does it keep in memory?Only about b × m positions, because it searches depth-first.

What if the opponent is not perfect? Minimax still gets at least its minimax value, because it has planned for the worst. But it may miss a chance to do better. Suppose that in Figure 9, MIN is careless and always plays its left-hand reply. Then the left move would give MAX 4, the middle 9 and the right 2, so the middle move would be best. Minimax still plays left, because it assumes MIN will spot the 3. Playing safe is the right choice when you know nothing about your opponent.

How much work is it? MAX has b moves, MIN has b replies to each, MAX has b replies to each of those, and so on, m times: b × b × … × b = bm games in all. Computer scientists write the time as O(bm), read “order b to the power m”: it grows like bm.

Memory is much smaller: about b × m positions, written O(bm). That is because minimax searches depth-first, like DFS in Section 3. MAX-VALUE calls MIN-VALUE, which calls MAX-VALUE, all the way down one line of play before it tries the next. So it only has to remember the positions on that one line, plus the other moves waiting at each step.

Example: minimax for chess

In chess, a position has about 35 legal moves (b ≈ 35), and a game of a sensible length is about 100 ply (m ≈ 100, so 50 moves each). Minimax would have to look at about 35100 games, which is about 10154: a 1 followed by 154 zeros. There are only about 1080 atoms in the observable universe. Working out perfect chess this way is completely out of reach.

But does minimax really need to look at every game? No: the next section shows how to skip some of them.

9Alpha-beta pruning

Minimax looks at every possible game. Alpha-beta pruning gets the same answer while skipping parts of the tree that cannot change it.

4 8 5 4 9 3 7 2 4 6 ≤3 ≤2 4 You (MAX) Opponent (MIN) Final score picks the highest picks the lowest of each game left middle right
Figure 10. The same tree as Figure 9. The crossed-out leaves are never looked at.

Worked example

  1. The left move is worked out first, as before: it is worth 4 to MAX.
  2. Middle move: the first leaf is 9, the second is 3. MIN can already hold MAX to 3 here, so this move is worth at most 3 to MAX. MAX already has a move worth 4, so it will never choose this one. Whatever the 7 is (10 or −10), it cannot change that. Skip it.
  3. Right move: the first leaf is 2, so this move is worth at most 2. Skip the 4 and the 6.

MAX still plays the left move, worth 4, but only 6 of the 9 leaves were looked at.

Pruning never changes the answer; it only saves work. In real games the saving is much bigger than here, because whole branches with thousands of games below them are skipped, not single leaves.

Alpha and beta

To know when it can skip a branch, the algorithm carries two numbers down the tree. They give it its name.

Alpha (α) and beta (β)

Alpha is the best score MAX can already be sure of, from the moves it has looked at so far on the way down. MAX will never settle for less. Beta is the best score MIN can already be sure of: MIN will never allow more. At the start nothing has been looked at, so alpha is −∞ and beta is +∞.

The rule: if a branch can give MAX no more than alpha, MAX will avoid it, so stop looking at it. In the same way, if a branch can give MIN no less than beta, MIN will avoid it.

Reading Figure 10 with alpha: after the left move, alpha = 4, because MAX is sure of 4. Under the middle move, MIN finds a 3, so that move is worth at most 3. That is less than alpha, so MAX will never choose it, and the 7 is skipped. Under the right move, the first leaf is 2, also less than alpha, so the 4 and the 6 are skipped.

A second example: the textbook’s tree

Russell and Norvig use the tree in Figure 11. It has the same shape as Figure 9, with different scores. Work out the minimax answer yourself first, then read the trace below.

3 12 8 2 4 6 14 5 2 3 ≤2 2 3 You (MAX) Opponent (MIN) Final score picks the highest picks the lowest of each game left middle right
Figure 11. The tree Russell and Norvig use for minimax and alpha-beta (they call the three moves a1, a2 and a3). MAX plays the left move, worth 3. The crossed-out leaves are never looked at.

Worked example: alpha-beta, one leaf at a time

Minimax first: the MIN nodes are worth min(3, 12, 8) = 3, min(2, 4, 6) = 2 and min(14, 5, 2) = 2, so MAX plays left, worth 3. Now alpha-beta, which looks at the leaves from left to right:

LeafWhat we now knowSo…
3the left move is worth at most 3keep looking
12still at most 3keep looking
8the left move is worth exactly 3alpha = 3: MAX is sure of 3
2the middle move is worth at most 22 is less than alpha: skip the 4 and the 6
14the right move is worth at most 14could still beat 3: keep looking
5the right move is worth at most 5could still beat 3: keep looking
2the right move is worth exactly 2less than 3
MAX takes the highest: 3play the left move

Alpha-beta looked at 7 of the 9 leaves and got the same answer as minimax.

Key idea

The order matters. If the right-hand leaves had come in the order 2, 5, 14, alpha-beta would have stopped after the 2 and skipped two more leaves. Alpha-beta prunes most when it looks at the best moves first.

The alpha-beta algorithm

Alpha-beta is minimax with two extra inputs, alpha and beta, and two extra lines in each function (highlighted).

function MAX-VALUE(state, α, β): if TERMINAL(state): return UTILITY(state) v = −∞ for action in ACTIONS(state): v = MAX(v, MIN-VALUE( RESULT(state, action), α, β)) if v ≥ β: return v α = MAX(α, v) return v
function MIN-VALUE(state, α, β): if TERMINAL(state): return UTILITY(state) v = +∞ for action in ACTIONS(state): v = MIN(v, MAX-VALUE( RESULT(state, action), α, β)) if v ≤ α: return v β = MIN(β, v) return v

How to read the two new lines in MAX-VALUE:

MIN-VALUE is the mirror image. It stops when v is at most α, because MAX already has something at least as good elsewhere, and it lowers beta. The search starts with MAX-VALUE(initial state, −∞, +∞). As with minimax, a third function makes this first call and returns the best action.

How much does alpha-beta save?

Alpha-beta is a simple example of metareasoning: reasoning about which calculations are worth doing. Before doing a calculation, the program asks whether its result could change the decision. If it could not, the calculation is skipped.

10Too big to search to the end

There are 255,168 possible games of tic-tac-toe, so a computer can play them all out in a moment. Chess is different. Section 8 showed that minimax would need about 10154 games of a sensible length, and Section 9 that even alpha-beta with perfect ordering would need about 1077. If you count every legal game of chess, including very long ones, there are roughly 1029000: a 1 followed by 29,000 zeros. No computer will ever play out every game of chess, so a chess program has to stop early and guess.

Depth-limited minimax

Look only a fixed number of moves ahead, then stop, even though the game is not over.

Evaluation function

A function that estimates how good a position is without playing to the end. Depth-limited minimax uses it to score the positions where it stops.

Example: a simple chess evaluation function

Give each piece points: pawn 1, knight 3, bishop 3, rook 5, queen 9. The score of a position is White’s points minus Black’s points. If White has an extra rook, the score is about +5. Real chess programs add much more (which squares are attacked, how safe the king is), but the idea is the same. The program can only be as good as its evaluation function.

Depth-limited minimax gives up the guarantee of the perfect move: the evaluation function is only a guess, so the move it picks is only as good as that guess. In return, it can answer within the time limit, and with alpha-beta it can look deeper in the same time.

11Search in the real world

In the seminar you describe a sat-nav, a chess engine and a warehouse robot as search problems, and discuss who decides what the “best” route is. Remember Figure 2: the answer depends on the path cost, and someone has to choose it.

Where these ideas came from

Most of this week’s ideas about games are older than the name “artificial intelligence” (1956, Week 1).

YearWhoIdea
1846Charles BabbageA machine could play a game by working through the possible lines of play.
1912Ernst ZermeloPerfect play in a game like chess can be worked out, in principle.
1944John von Neumann and Oskar MorgensternGame theory, including minimax. Economists still use it.
1945–50Konrad Zuse, Norbert Wiener, Claude ShannonLook a fixed number of moves ahead, then estimate the score: depth-limited minimax with an evaluation function.
1951Alan TuringOne of the first chess programs. No computer could run it yet, so Turing worked through its moves by hand.
1952–57Arthur SamuelA draughts (checkers) program that improved its own evaluation function by playing games: an early example of machine learning (Week 7).
1956John McCarthyPruning, so that a program can search deeper: the idea behind alpha-beta.

Check yourself

Try these without looking back. The answers are at the end.

  1. In a 4 × 4 sliding puzzle, how many actions are there when the gap is in a corner? Why does ACTIONS take the state as its input?
  2. Describe a sat-nav as a search problem: give its initial state, actions, transition model, goal test and path cost.
  3. What could go wrong if the search loop had no explored set?
  4. What is the difference between the state space and the search tree? In Figure 4, why does Salisbury appear three times?
  5. The frontier holds P, Q and R, added in that order. Which node does DFS take out next? Which does BFS take out?
  6. On the tree in Section 5, make F the goal instead of D. In what order does each search take nodes out of the frontier?
  7. Why does BFS not find the 50-minute route on the road map in Figure 2?
  8. Put each game in Table 1: Connect Four, Ludo, Battleships and poker. Which of them could plain minimax play?
  9. Use minimax (Section 8) on the Nim tree in Figure 7. What is the minimax value of each state, and should you take 1 or 2 counters first?
  10. In Figure 9, change the 3 in the middle branch to a 6. Which move does MAX choose now, and what is it worth?
  11. With that change, can alpha-beta pruning still skip the 7? Can it still skip anything?
  12. In Figure 11, suppose the leaves under the right move came in the order 2, 5, 14. How many of the nine leaves does alpha-beta look at now?
  13. A game has 10 legal moves in every position and always lasts 20 ply. About how many games does minimax look at? About how many does alpha-beta look at with perfect move ordering?
  14. Why can a chess program not use plain minimax, and what does it do instead?

Key terms

Search problemFind a sequence of actions from an initial state to a goal.
AgentSomething that perceives its environment and acts on it.
State / initial stateOne configuration of the agent and its environment / the one the search starts from.
Actions, ACTIONS(s)The choices available in state s.
Transition model, RESULT(s, a)The state you reach by doing a in s.
State spaceAll states reachable from the initial state; drawn as a graph.
Goal testChecks whether a state is a goal.
Path costA number for each path, such as time or distance.
Solution / optimal solutionActions from the initial state to a goal / a solution with the lowest path cost.
NodeA record of a state, its parent, the action that reached it and the path cost.
Search treeThe tree of nodes a search builds as it runs; one node per path, so a state can appear more than once.
FrontierThe nodes found but not yet looked at.
Explored setThe states already looked at; stops the search going round in circles.
Search algorithmA method that returns a solution to a search problem, or reports that there is none (DFS, BFS, and in Week 3 greedy and A*).
Stack / queueLast in, first out / first in, first out.
Depth-first searchTakes the newest node (stack); goes deep first; path may not be shortest.
Breadth-first searchTakes the oldest node (queue); spreads out evenly; shortest path when every step costs the same.
Uninformed searchA search that uses no knowledge about where the goal is, such as DFS and BFS.
Adversarial searchSearch when an opponent chooses some of the moves.
Zero-sum gameOne player’s gain is the other player’s loss; there is no win-win result.
Perfect informationBoth players can see the whole game (it is fully observable).
Game tree / plyEvery possible move and reply, drawn as a tree / one move by one player.
StrategyWhich move to make in every position you might reach, whatever the opponent does.
Terminal state / utilityA state where the game is over / its score (for tic-tac-toe: +1, 0 or −1).
MinimaxMAX picks the highest value and MIN the lowest, working up from the end of the game.
Minimax valueThe score MAX gets from a state if both players play perfectly from there.
Branching factor (b)The number of legal moves in a position. Minimax looks at about bm games, for games m ply long.
Alpha-beta pruningSkips branches that cannot change the minimax answer.
Alpha (α) / beta (β)The best score MAX / MIN can already be sure of.
Move orderingLooking at the most promising moves first, so that alpha-beta can prune more.
Depth-limited minimaxMinimax that stops a fixed number of moves ahead.
Evaluation functionEstimates how good a position is without playing to the end.

Reading and next week

Next week: smarter search. A heuristic estimates how far a state is from the goal, and greedy best-first search and A* search use it to look in the right direction first. Then logic: writing knowledge down precisely, and letting a computer work out what follows from it.

Answers to Check yourself

  1. Two: only the two tiles next to a corner can slide into it. In the middle of an edge there are 3 actions, and with the gap in one of the four middle squares there are 4. ACTIONS takes the state as input because the moves allowed depend on where the gap is.
  2. Initial state: the car’s current location. Actions: the roads you can drive along from the current junction. Transition model: driving along a road takes you to the junction at its end. Goal test: are you at the destination? Path cost: total travel time (or distance, or fuel).
  3. The search could go round in circles for ever, adding the same states again and again (for example Winchester → Stockbridge → Winchester → …). Even when it does finish, it would waste time looking at the same states many times.
  4. The state space has one box for each state; the search tree has one node for each path the search has found, so a state can appear in it many times. Salisbury appears three times because there are three routes to it (via Andover, Stockbridge and Romsey), each with its own parent and path cost (55, 50 and 60 minutes).
  5. DFS takes R (the newest, from the stack). BFS takes P (the oldest, from the queue).
  6. DFS: A, C, F (goal). After A the frontier is B C; DFS takes C, adds F, then takes F: lucky, only 3 nodes. BFS: A, B, C, D, E, F (goal), all 6 nodes, because F is on the deepest level and BFS finishes each level first.
  7. BFS counts steps, not minutes. All three routes use two roads, so BFS returns the first route it completes. Andover was added to the queue first, so it comes out first and adds Salisbury with Andover as its parent. BFS returns Winchester → Andover → Salisbury (55 minutes). To find the 50-minute route, the search has to use the path cost, which A* does.
  8. Connect Four: deterministic, perfect information. Ludo: chance (dice), perfect information. Battleships: deterministic, imperfect information (the ships are hidden). Poker: chance and imperfect information. Only Connect Four is in the green box of Table 1, so it is the only one plain minimax could play.
  9. Work up from the bottom. With 1 counter and you to move, you must take the last counter: −1. With 2 counters and your opponent to move, they choose min(−1, +1) = −1: they take 1 and leave you the last one. With 1 counter and your opponent to move, they must take the last: +1. At the start, max(−1, +1) = +1: take 2, leaving 1, so your opponent has to take the last counter.
  10. The middle MIN node becomes min(9, 6, 7) = 6. MAX now takes max(4, 6, 2) = 6: the middle move, worth 6.
  11. No, the 7 cannot be skipped. After the 9 and the 6, the middle move is worth at most 6, and that is better than the 4 MAX already has, so MAX must look at the 7 to find the real value (6). But on the right, the first leaf is 2: that move is worth at most 2, which is worse than 6, so the 4 and the 6 on the right are still skipped.
  12. Five: all three on the left (3, 12, 8), the 2 in the middle, and the 2 on the right. After that 2, the right move is worth at most 2, which is less than alpha = 3, so the 5 and the 14 are skipped.
  13. Minimax: bm = 1020, a hundred billion billion. Alpha-beta with perfect ordering: about bm/2 = 1010, ten billion. That is still a lot, but it is ten billion times fewer.
  14. There are far too many possible games (roughly 1029000) to play every one out to the end. Instead it uses depth-limited minimax: it looks a few moves ahead and scores the positions it reaches with an evaluation function, such as counting the pieces.