Title: Add Skill Level 2 to the tic-tac-toe game in Python
This example builds on the previous one Make a tic-tac-toe game in Python to implement Skill Level 2. To do that, it only needs to implement the computer_move_level_2 method and add the can_win helper function.
computer_move_level_2
At Skill Level 2, the computer following this strategy:
- If there is a win, take it.
- Else if there is a block, take it.
- Else move randomly.
The following code shows how the computer_move_level_2 method follows that strategy.
def computer_move_level_2(self):
'''
If there is a win, take it.
Else if there is a block, take it.
Else move randomly.
'''
# Look for a win. (For more variety, you could
# list all possible wins and pick one randomly.)
for r in range(3):
for c in range(3):
if self.can_win(self.computer_char, r, c):
self.squares[r][c]['text'] = self.computer_char
return
# Look for a block. (Again, you could randomize.)
for r in range(3):
for c in range(3):
if self.can_win(self.player_char, r, c):
self.squares[r][c]['text'] = self.computer_char
return
# Move randomly.
self.computer_move_level_1()
This method loops through all of the board's squares. For each square, it calls the can_win (described in the earlier post) method to see if the computer can win by taking that square. If so, it takes the square and returns.
To make the game slightly more interesting, you could make a list of all of the possible winning moves and then pick one randomly. Normally there will be zero or one possible winning move, but sometimes there may be two. (There can't be three.)
If that loop doesn't find a win, the code loops through the squares again, this time to see if the player could win by taking a square. If the player could win, the program takes the square to prevent that. (You could randomize blocking moves if there is more than one just as you could randomize winning moves.)
If neither loop picks a move, the method calls computer_move_level_1 (described in the earlier post) to pick a random move.
The following can_win method returns True if taking a square would produce a win.
def can_win(self, char, r, c):
'''Return True if player char can win by taking this square.'''
# If the square ia already taken, return False.
if self.squares[r][c]['text'] != '':
return False
# Temporarily take the square.
self.squares[r][c]['text'] = char
# See is this makes a win.
is_win = self.row_col_winner(0, c, 1, 0) # Column win
is_win = is_win or self.row_col_winner(r, 0, 0, 1) # Row win
if r == c: # Diagonal win
is_win = is_win or self.row_col_winner(0, 0, 1, 1)
if r + c == 2: # Diagonal win
is_win = is_win or self.row_col_winner(2, 0, -1, 1)
# Untake the square.
self.squares[r][c]['text'] = ''
return is_win
The method first checks whether the square is already taken. If the square is already taken, it cannot be taken again so the method returns False.
Next, the code temporarily takes the square. It then uses the row_col_winner method (described in the previous post) to see if that square is part of a column, row, of diagonal win and saves the result in variable is_win.
The method untakes the square and returns the value of is_win.
Conclusion
Download the example to experiment with it. Don't forget to switch to Skill Level 2 or you'll wonder why the progam plays so badly.
That's all we need to do to implement Skill Level 2. This strategy looks for two patterns: situations where the computer can win and situations where the computer can block.
If you play the program a few times, you'll find that it's harder to beat than the program's first version. Unless you set up a fork where you have two possible winning moves, the program will block you. Sometimes the program's first move will randomly prevent you from creating a fork and you'll end in a cat's game.
In a more complicated game like chess, Go, or reversi, there are far more patterns to look for and they can be much harder to recognize. For example, one common chess heuristic is, "When ahead, trade mercilessly." To use that strategy, the program would need to be able to find situations where it can force the player to trade pieces of equal value.
In my final tic-tac-toe post, I'll implement minimax, which lets the program examine the entire game tree to find the best possible move. You can't beat a tic-tac-toe program that uses this strategy.
|