In your version of the game, the two players will be a human and a program. The human will go first using token "X". The human player will be asked via an input statement to specify an empty location to place "X". The program will use token "O". The program player is particularly mindless, placing its token on any randomly chosen open space. Think about how you could make it a bit smarter.
You don't have to do this from scratch. It is very common in computing to collaborate with others in a team to build a system/program. In this assignment, you will be supplied with some code and be required to write other code to interact with what is supplied to produce a working system. Of course, in this class you should never use work that you find on the internet or done by anyone other than youself, except as specifically allowed, as in this case!
You will be supplied a class template and a driver function which will play the game. You will have to fill in some other code including some constants, class methods, and possibly some subsidiary functions. Below is the template and driver. This is also in the following file: Tic Tac Toe template. Note that the board is simply a 2D (3x3) list of characters (" ", "X", "O"); this makes it pretty simple to print.
Machine moves are guaranteed to be legal. But you should validate the human (user-supplied) moves. A user supplied move must have form 'r c' where r and c are in the range [0..2]. Here's a possible algorithm for getting and validating a move:
Accept the move from the user Strip off leading and trailing whitespace Split into a list of strings by whitespace Is that list of length 2? If not, print an error message and get another input Get the two (string) elements of that list, call them row and col Are both row and col all digits? If not, print an error message and get another input Convert row and col to integers Are they in the right range? If not, print an error message and get another input Is the designated space empty? If not, print an error message and get another input Update the space with the user token ReturnBelow is the template for you to use. You shouldn't change any of this code; just add your own code.
import random # Some global constants: HUMAN = 0 MACHINE = 1 # Messages for printing: ILLEGAL_MOVE = "Illegal move. Try again!\n" # other messages here YOU_WON = "Congratulations! You won!\n" def getInitialBoard(): return [ [" ", " ", " "], \ [" ", " ", " "], \ [" ", " ", " "] ] # Put any auxiliary functions you need here. class TicTacToeBoard: def __init__(self): """Initialize a Tic Tac Toe board and initial player.""" def __str__(self): """This returns a string representation of the board.""" def getPlayer( self ): """Return the current player.""" def isWin( self ): """Does the current board configuration represent a win for the current player.""" def swapPlayers( self ): """Swap current player: HUMAN for MACHINE or vice versa.""" def humanMove( self ): """Accept and validate a move from the HUMAN. Keep trying until a valid move is entered. Update the board accordingly.""" def machineMove( self ): """Generate a random legal move by MACHINE. Make and report the move.""" print("Machine's turn:") while True: # Generate a random move r = random.randint(0, 2) c = random.randint(0, 2) # Make sure the space is unoccupied. if self.__board[r][c] == " ": # Report and make the move. print(" Machine chooses: ", r, c ) self.__board[r][c] = "O" return def driver( ): """ This plays tic-tac-toe in a pretty simple-minded fashion. The human player goes first with token "X" and alternates with the machine using token "O". We print the board before the first move and after each move. """ # Print the welcome message print( WELCOME ) # Initialize the board and player. HUMAN is the initial current # player. ttt = TicTacToeBoard() # Show the initial state of the board. print( ttt ) # There are up to 9 moves in tic-tac-toe. for move in range(9): # At a given step, the current player may be HUMAN or MACHINE. if ttt.getPlayer() == HUMAN: # If HUMAN, take a move, print the board, and see if it's # a win. ttt.humanMove() print( ttt ) if ttt.isWin(): print( YOU_WON ) # Game is over. return else: # Else Machine takes a move. Print the board and see if # the machine won. ttt.machineMove() print( ttt ) if ttt.isWin(): print( YOU_LOST ) # Game is over. return # Swap players. ttt.swapPlayers() # After nine moves with no winner, it's a tie. print( YOU_TIED ) driver()Aside 1: Notice that this is a pretty dumb way to play tic-tac-toe.
Aside 2: I previously had the initial board as a global constant:
INITIAL_BOARD = [ [" ", " ", " "], \ [" ", " ", " "], \ [" ", " ", " "] ]and used that to initialize the board in my __init__ function. But that makes the initial board a global variable and, since lists are mutable, it's that variable that gets updated as play progresses. So if you were to start a new game (initializing a new instance of the class) thinking you'd get a clean board, you'd actually get the final board from the previous game. Programming can be subtle and even longtime programmers make mistakes!
> python TicTacToe.py Welcome to our Tic-Tac-Toe game! Please begin playing. | | ----- | | ----- | | Your turn: Specify a move r c: 3 Illegal move. Try again! | | ----- | | ----- | | Specify a move r c: 1 1 1 Illegal move. Try again! | | ----- | | ----- | | Specify a move r c: abc 2 Illegal move. Try again! | | ----- | | ----- | | Specify a move r c: 1 1 | | ----- |X| ----- | | Machine's turn: Machine chooses: 2 2 | | ----- |X| ----- | |O Your turn: Specify a move r c: 1 1 Space is occupied. Try again! | | ----- |X| ----- | |O Specify a move r c: 0 2 | |X ----- |X| ----- | |O Machine's turn: Machine chooses: 2 1 | |X ----- |X| ----- |O|O Your turn: Specify a move r c: 0 0 X| |X ----- |X| ----- |O|O Machine's turn: Machine chooses: 1 0 X| |X ----- O|X| ----- |O|O Your turn: Specify a move r c: 2 0 X| |X ----- O|X| ----- X|O|O Congratulations! You won! > python TicTacToe.py Welcome to our Tic-Tac-Toe game! Please begin playing. | | ----- | | ----- | | Your turn: Specify a move r c: 1 1 | | ----- |X| ----- | | Machine's turn: Machine chooses: 2 0 | | ----- |X| ----- O| | Your turn: Specify a move r c: 0 1 |X| ----- |X| ----- O| | Machine's turn: Machine chooses: 2 1 |X| ----- |X| ----- O|O| Your turn: Specify a move r c: 0 0 X|X| ----- |X| ----- O|O| Machine's turn: Machine chooses: 1 2 X|X| ----- |X|O ----- O|O| Your turn: Specify a move r c: 1 0 X|X| ----- X|X|O ----- O|O| Machine's turn: Machine chooses: 2 2 X|X| ----- X|X|O ----- O|O|O Sorry! You lost! > python TicTacToe.py Welcome to our Tic-Tac-Toe game! Please begin playing. | | ----- | | ----- | | Your turn: Specify a move r c: 1 0 | | ----- X| | ----- | | Machine's turn: Machine chooses: 0 1 |O| ----- X| | ----- | | Your turn: Specify a move r c: 2 2 |O| ----- X| | ----- | |X Machine's turn: Machine chooses: 1 2 |O| ----- X| |O ----- | |X Your turn: Specify a move r c: 0 2 |O|X ----- X| |O ----- | |X Machine's turn: Machine chooses: 1 1 |O|X ----- X|O|O ----- | |X Your turn: Specify a move r c: 0 0 X|O|X ----- X|O|O ----- | |X Machine's turn: Machine chooses: 2 0 X|O|X ----- X|O|O ----- O| |X Your turn: Specify a move r c: 2 1 X|O|X ----- X|O|O ----- O|X|X Looks like a tie. Better luck next time! >
Your file must compile and run before submission. It must also contain a header with the following format:
# Assignment: HW10 # File: TicTacToe.py # Student: # UT EID: # Course Name: CS303E # # Date: # Description of Program:
Functions are one of your most useful tools for managing the complexity of code. Strictly speaking, you can get by without them. But your code is likely to be an unreadable, unmaintainable jumble. Consider the question of whether the tic-tac-toe player who just placed a token on the board thereby won the game. She did if there are now three of her tokens in a row, either within one row of the board, one column of the board, or either of the two diagonals.
As an experienced programmer and someone who appreciates the value of functional abstraction, my natural inclination is to write the following auxiliary function:
def isWin( board, token ): return isRowWin( board, token ) \ or isColumnWin( board, token ) \ or isDiagonalWin( board, token )And then go off and write the other three functions isRowWin, isColumnWin, and isDiagonalWin. And yes, I could write one big complicated expression involving lots of ands, ors, and indexes into the board. But I'd be much less sure that it was correct. Having broken it up into a series of simple functions, it's much easier to see that I'm doing it right.
Students often think that writing functions is a waste of time; they're wrong. Just the increase in clarity is well worth the effort.