Опубликован: 12.07.2013 | Доступ: свободный | Студентов: 735 / 20 | Длительность: 37:41:00
Лекция 10:

Tic Tac Toe

< Лекция 9 || Лекция 10: 12345 || Лекция 11 >

How the Code Works: Lines 96 to 187

Creating the Computer's Artificial Intelligence
96. def getComputerMove(board, computerLetter):
97.     # Given a board and the computer's letter, determine where to move and return that move.
98.      if computerLetter == 'X':
99.         playerLetter = 'O'
100.     else:
101.      playerLetter = 'X'

The getComputerMove() function is where our AI will be coded. The parameters are a Tic Tac Toe board (in the board parameter) and which letter the computer is (either 'X' or 'O'). The first few lines simply assign the other letter to a variable named playerLetter. This lets us use the same code, no matter who is X and who is O. This function will return the integer that represents which space the computer will move.

Remember how our algorithm works:

First, see if there is a move the computer can make that will win the game. If there is, take that move. Otherwise, go to the second step.

Second, see if there is a move the player can make that will cause the computer to lose the game. If there is, we should move there to block the player. Otherwise, go to the third step.

Third, check if any of the corner spaces (spaces 1, 3, 7, or 9) are free. (We always want to take a corner piece instead of the center or a side piece.) If no corner piece is free, then go to the fourth step.

Fourth, check if the center is free. If so, move there. If it isn't, then go to the fifth step.

Fifth, move on any of the side pieces (spaces 2, 4, 6, or 8). There are no more steps, because if we have reached this step then the side spaces are the only spaces left.

The Computer Checks if it Can Win in One Move
103.     # Here is our algorithm for our Tic Tac Toe AI:
104.     # First, check if we can win in the next move
105.      for i in range(1, 10) :
106.         copy = getBoardCopy(board)
107.          if isSpaceFree(copy, i):
108.             makeMove(copy, computerLetter, i)
109.              if isWinner(copy, computerLetter): 
110.                                      return i

More than anything, if the computer can win in the next move, the computer should immediately make that winning move. We will do this by trying each of the nine spaces on the board with a for loop. The first line in the loop makes a copy of the board list. We want to make a move on the copy of the board, and then see if that move results in the computer winning. We don't want to modify the original Tic Tac Toe board, which is why we make a call to getBoardCopy(). We check if the space we will move is free, and if so, we move on that space and see if this results in winning. If it does, we return that space's integer.

If moving on none of the spaces results in winning, then the loop will finally end and we move on to line 112.

The Computer Checks if the Player Can Win in One Move
112.     # Check if the player could win on his next move, and block them.
113.     for i in range(1, 10):
114.         copy = getBoardCopy(board)
115.         if isSpaceFree(copy, i):
116.             makeMove(copy, playerLetter, i)
117.             if isWinner(copy, playerLetter):
118.                 return i 

At this point, we know we cannot win in one move. So we want to make sure the human player cannot win in one more move. The code is very similar, except on the copy of the board, we place the player's letter before calling the isWinner() function. If there is a position the player can move that will let them win, the computer should move there to block that move.

If the human player cannot win in one more move, the for loop will eventually stop and execution continues on to line 120.

Checking the Corner, Center, and Side Spaces (in that Order)
120.      # Try to take one of the corners, if they are free.
121.      move = chooseRandomMoveFromList(board, [1, 3, 7, 9])
122.      if move != None: 
123.                       return move

Our call to chooseRandomMoveFromList() with the list of [1, 3, 7, 9] will ensure that it returns the integer for one of the corner spaces. (Remember, the corner spaces are represented by the integers 1, 3, 7, and 9.) If all the corner spaces are taken, our chooseRandomMoveFromList() function will return the None value. In that case, we will move on to line 125.

125.      # Try to take the center, if it is free.
126.      if isSpaceFree(board, 5) : 
127 .                       return 5

If none of the corners are available, we will try to move on the center space if it is free. If the center space is not free, the execution moves on to line 129.

129.      # Move on one of the sides.
130.      return chooseRandomMoveFromList(board, [2, 4, 6, 8])

This code also makes a call to chooseRandomMoveFromList(), except we pass it a list of the side spaces ([2, 4, 6, 8]). We know that this function will not return None, because the side spaces are the only spaces we have not yet checked. This is the end of the getComputerMove() function and our AI algorithm.

Checking if the Board is Full
132.  def isBoardFull(board):
133.      # Return True if every space on the board has been taken. Otherwise return False.
134.      for i in range(1, 10):
135.           if isSpaceFree(board, i):
136.               return False
137.      return True

The last function we will write is isBoardFull(), which returns True if the 10-string list board argument it was passed has an 'X' or 'O' in every index (except for index 0, which is just a placeholder that we ignore). If there is at least one space in board that is set to a single space ' ' then it will return False.

The for loop will let us check spaces 1 through 9 on the Tic Tac Toe board. (Remember that range(1, 10) will make the for loop iterate over the integers 1, 2, 3, 4, 5, 6, 7, 8, and 9.) As soon as it finds a free space in the board (that is, when isSpaceFree (board, i) returns True), the isBoardFull() function will return False.

If execution manages to go through every iteration of the loop, we will know that none of the spaces are free. So at that point, we will execute return True.

The Start of the Game
140. print('Welcome to Tic Tac Toe!')

Line 140 is the first line that isn't inside of a function, so it is the first line of code that is executed when we run this program.

142. while True:
143.    # Reset the board
144.    theBoard = [' '] * 10

This while loop has True for the condition, so that means we will keep looping in this loop until we encounter a break statement. Line 144 sets up the main Tic Tac Toe board that we will use, named theBoard. It is a 10-string list, where each string is a single space ' '. Remember the little trick using the multiplication operator with a list to replicate it: [' '] * 10. That evaluates to [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '], but is shorter for us to type [' '] * 10.

Deciding the Player's Mark and Who Goes First
145.    playerLetter, computerLetter = inputPlayerLetter()

The inputPlayerLetter() function lets the player type in whether they want to be X or O. The function returns a 2-string list, either ['X', 'O'] or ['O', 'X']. We use the multiple assignment trick here that we learned in the Hangman chapter. If inputPlayerLetter() returns ['X', 'O'], then playerLetter is set to 'X' and computerLetter is set to 'O'. If inputPlayerLetter() returns ['O', 'X'], then playerLetter is set to 'O' and computerLetter is set to 'X'.

146.     turn = whoGoesFirst()
147.     print('The ' + turn + ' will go first.')
148.     gameIsPlaying = True

The whoGoesFirst() function randomly decides who goes first, and returns either the string 'player' or the string 'computer'. On line 147, we tell the player who will go first. The gameIsPlayer variable is what we will use to keep track of whether the game has been won, lost, tied or if it is the other player's turn.

Running the Player's Turn
150.     while gameIsPlaying:

This is a loop that will keep going back and forth between the player's turn and the computer's turn, as long as gameIsPlaying is set to True.

151.           if turn == 'player':
152.               # Player's turn.
153.               drawBoard(theBoard)
154.               move = getPlayerMove(theBoard)
155.               makeMove(theBoard, playerLetter, move)

The turn variable was originally set by whoGoesFirst(). It is either set to 'player' or 'computer'. If turn contains the string 'computer', then the condition is False and execution will jump down to line 169.

The first thing we do when it is the player's turn (according to the flow chart we drew at the beginning of this chapter) is show the board to the player. Calling the drawBoard() and passing the theBoard variable will print the board on the screen. We then let the player type in his move by calling our getPlayerMove() function, and set the move on the board by calling our makeMove() function.

157.               if isWinner(theBoard, playerLetter):
158.                   drawBoard(theBoard)
159.                                          print('Hooray! You have won the game!')
160.                                          gameIsPlaying = False

Now that the player has made his move, our program should check if they have won the game with this move. If the isWinner() function returns True, we should show them the winning board (the previous call to drawBoard() shows the board before they made the winning move) and print a message telling them they have won.

Then we set gameIsPlaying to False so that execution does not continue on to the computer's turn.

161.	else:
162.	if isBoardFull(theBoard):
163.	drawBoard(theBoard)
164.	print('The game is a tie!')
165.	break

If the player did not win with his last move, then maybe his last move filled up the entire board and we now have a tie. In this else-block, we check if the board is full with a call to the isBoardFull() function. If it returns True, then we should draw the board by calling drawBoard() and tell the player a tie has occurred. The break statement will break us out of the while loop we are in and jump down to line 186.

Running the Computer's Turn

166.                 else:
167.                     turn = 'computer'

If the player has not won or tied the game, then we should just set the turn variable to 'computer' so that when this while loop loops back to the start it will execute the code for the computer's turn.

169.                    else:

If the turn variable was not set to 'player' for the condition on line 151, then we know it is the computer's turn and the code in this else-block will execute. This code is very similar to the code for the player's turn, except the computer does not need the board printed on the screen so we skip calling the drawBoard() function.

170.             # Computer's turn.
171.             move = getComputerMove(theBoard, computerLetter)
172.               makeMove(theBoard, computerLetter, move)

This code is almost identical to the code for the player's turn on line 154 and 155.

174 .	if isWinner(theBoard, computerLetter):
175 .	drawBoard(theBoard)
176 .	print('The computer has beaten you! You lose.')	
177 .	gameIsPlaying = False

We want to check if the computer won with its last move. The reason we call drawBoard() here is because the player will want to see what move the computer made to win the game. We then set gameIsPlaying to False so that the game does not continue. Notice that lines 174 to 177 are almost identical to lines 157 to 160.

178.	else:
179.	if isBoardFull(theBoard):
180.	drawBoard(theBoard)
181.	print('The game is a tie!')
182.	break

These lines of code are identical to the code on lines 162 to 165. The only difference is this is a check for a tied game after the computer has moved.

183.                                          else:
184.                                                    turn = 'player'

If the game is neither won nor tied, it then becomes the player's turn. There are no more lines of code inside the while loop, so execution would jump back to the while statement on line 150.

186.     if not playAgain() : 
187.                       break

These lines of code are located immediately after the while-block started by the while statement on line 150. Remember, we would only exit out of that while loop if it's condition (the gameIsPlaying variable) was False. gameIsPlaying is set to False when the game has ended, so at this point we are going to ask the player if they want to play again.

Remember, when we evaluate the condition in this if statement, we call the playAgain() function which will let the user type in if they want to play or not. playAgain() will return True if the player typed something that began with a 'y' like 'yes' or 'y'. Otherwise playAgain() will return False.

If playAgain() returns False, then the if statement's condition is True (because of the not operator that reverses the Boolean value) and we execute the break statement. That breaks us out of the while loop that was started on line 142. But there are no more lines of code after that while-block, so the program terminates.

Summary: Creating Game-Playing Artificial Intelligences

Creating a program that can play a game comes down to carefully considering all the possible situations the AI can be in and how it should respond in each of those situations. Our Tic Tac Toe AI is fairly simple because there are not many possible moves in Tic Tac Toe compared to a game like chess or checkers.

Our AI simply blocks the players move if the player is about to win. If the player is not about to win, it checks if any possible move can allow itself to win. Then the AI simply chooses any available corner space, then the center space, then the side spaces. This is a simple algorithm for the computer to follow.

The key to implementing our AI is by making copies of the board data and simulating moves on the copy. That way, the AI code can see if a move will result in a win or loss. Then the AI can make that move on the real board. This type of simulation is very effective at predicting what is a good move or not.

< Лекция 9 || Лекция 10: 12345 || Лекция 11 >