# File: tictactoe.py # Purpose: A game of tic-tac-toe to be played between two players. # Author: TODO # # Collaboration statement: TODO # # Inputs: mouse clicks! from graphics import * ############################################################## ## Helper functions for Tic-Tac-Toe ## ############################################################## def createAndDrawBoard(): """ Creates a window and draws the board in it. returns: a GraphWin object """ # Create the window win = GraphWin("Tic Tac Toe", 600, 600) win.setCoords(-0.25, 3.75, 3.25, -0.25) # give a little buffer win.setBackground("white") # Draw the lines for i in range(1, 3): horizontalLine = Line(Point(0, i), Point(3, i)) horizontalLine.draw(win) verticalLine = Line(Point(i, 0), Point(i, 3)) verticalLine.draw(win) # Return the window return win def setPlayerText(label, player): """ Updates the player text label to indicate whose turn it is. label: a Text object player: 1 or 2 """ if player == 1: label.setText("Player 1, please place an X.") else: label.setText("Player 2, please place an O.") def getGridLocation(point): """ Returns the x,y grid coordinates of a point. point: a Point object returns: a pair of grid positions: x,y """ gridX = int(point.getX()) gridY = int(point.getY()) return gridX, gridY ############################################################## ## Part 1: Keeping track of game state ## ############################################################## def getCellString(gridValue): """ Returns a string corresponding to the provided value from the board state. gridValue: 0 (none) or 1 (player 1) or 2 (player 2) returns: " " (0) or "x" (player 1) or "o" (player 2) """ # TODO: Part 1 return "" # replace with your code def printRow(boardState, row): """ Prints out the current board state of the given row. boardState: a nested list containing the board state row: the row for which to print the board state """ # TODO: Part 1 pass # replace with your code def printBoardState(boardState): """ Prints out the current board state for debugging. boardState: a nested list containing the board state """ print() # Print top row printRow(boardState, 0) # Divider line print("-----") # Print middle row printRow(boardState, 1) # Divider line print("-----") # Print bottom row printRow(boardState, 2) ############################################################## ## Part 2: Placing pieces ## ############################################################## def drawX(win, gridX, gridY): """ Draws an X at the specified grid position. win: the GraphWin for the board gridX: x-coordinate of grid cell (column) gridY: y-coordinate of grid cell (row) """ # TODO: Part 2 pass # replace with your code def drawO(win, gridX, gridY): """ Draws an O at the specified grid position. win: the GraphWin for the board gridX: x-coordinate of grid cell (column) gridY: y-coordinate of grid cell (row) """ # TODO: Part 2 pass # replace with your code def drawPlayerMarker(win, gridX, gridY, player): """ Draws a player marker (X for player 1, O for player 2) at the specified grid position. win: the GraphWin for the board gridX: x-coordinate of grid cell (column) gridY: y-coordinate of grid cell (row) player: 1 or 2 """ if player == 1: drawX(win, gridX, gridY) else: # must be 2 drawO(win, gridX, gridY) ############################################################## ## Part 3: Checking for valid positions ## ############################################################## def isValidGridCell(boardState, gridX, gridY): """ Returns a Boolean indicating whether the given grid position is a valid selection given the current board state. Also checks if the grid position is within the bounds of the board. boardState: a nested list containing the board state gridX: x-coordinate of grid cell (column) gridY: y-coordinate of grid cell (row) returns: True if a piece can be placed at (gridX, gridY), False otherwise """ # TODO: Part 3 return True # replace with your code def updateBoardState(boardState, gridX, gridY, player): """ Updates the board state to indicate a player placed a marker at the specified grid position on the board. boardState: a nested list containing the board state gridX: x-coordinate of grid cell (column) gridY: y-coordinate of grid cell (row) player: 1 or 2 """ # TODO: Part 3 return True # replace with your code ############################################################## ## Part 4: Ending the game ## ############################################################## def isDraw(boardState): """ Returns a Boolean indicating whether the game has ended in a draw. Assumes neither player has won. boardState: a nested list containing the board state returns: a Boolean (True if the game is a draw, False otherwise) """ # TODO: Part 4a return False # replace with your code def didPlayerWinWithRow(boardState, player, row): """ Returns a Boolean indicating whether the player won the game due to the given row. boardState: a nested list containing the board state player: 1 or 2 row: 0, 1, or 2 returns: a Boolean (True if the player has an entire row, False otherwise) """ # TODO: Part 4b return False # replace with your code def didPlayerWinWithColumn(boardState, player, col): """ Returns a Boolean indicating whether the player won the game due to the given column. boardState: a nested list containing the board state player: 1 or 2 col: 0, 1, or 2 returns: a Boolean (True if the player has an entire column, False otherwise) """ # TODO: Part 4b return False # replace with your code def didPlayerWinWithDiagonal(boardState, player): """ Returns a Boolean indicating whether the player won the game due to either diagonal. boardState: a nested list containing the board state player: 1 or 2 returns: a Boolean (True if the player has an entire diagonal, False otherwise) """ # TODO: Part 4b return False # replace with your code def didPlayerWin(boardState, player): """ Returns a Boolean indicating whether the player has won the game. boardState: a nested list containing the board state player: 1 or 2 returns: a Boolean (True if the player won, False otherwise) """ # First, check the rows for row in range(3): if didPlayerWinWithRow(boardState, player, row): return True # Second, check the columns for col in range(3): if didPlayerWinWithColumn(boardState, player, col): return True # Finally, check the diagonals if didPlayerWinWithDiagonal(boardState, player): return True # No win condition was met return False ############################################################## ## Testing code ## ############################################################## def testGetCellString(): """ Tests getCellString by checking that the correct value is returned. """ res1 = getCellString(0) == " " res2 = getCellString(1) == "x" res3 = getCellString(2) == "o" print("Testing getCellString():", "passed\n" if res1 and res2 and res3 else "failed\n") def testPrintBoardState(): """ Tests drawPlayerMarker by drawing five pieces based on where the user clicks. Does not check for the location being valid, just tests the drawing code. """ # Test getCellString() first testGetCellString() # Set up the board state boardState = [[2,0,0], # top row [1,2,0], # middle row [0,1,0]] # bottom row # Print out the actual list print("Testing printBoardState() now.") print("The boardState list is:", boardState) # Print out the board state (useful for debugging) printBoardState(boardState) # Print the expected result print("\nThe expected board state is:\n") print("o| | ") print("-----") print("x|o| ") print("-----") print(" |x| ") print() def testDrawPlayerMarker(): """ Tests drawPlayerMarker by drawing five pieces based on where the user clicks. *Does not* check for the location being valid, just tests the drawing code. """ # Create and set up the board win = createAndDrawBoard() # Add a label to indicate to the players whose turn it is textLabel = Text(Point(1.5, 3.5), "Place five markers. Click anywhere to start.") textLabel.draw(win) win.getMouse() # wait until they click to start it all # Start with player 1 player = 1 # Draw five pieces for i in range(5): # Update the player text setPlayerText(textLabel, player) # Wait for the player to click a valid grid cell gridX, gridY = getGridLocation(win.getMouse()) # Draw a marker for the current player drawPlayerMarker(win, gridX, gridY, player) # Switch players player = 3 - player # switches between 1 and 2 # Update the text label -- we're done textLabel.setText("All done -- click again to quit.") win.getMouse() win.close() def testPlacingValidMarkers(): """ Tests drawPlayerMarker by drawing five pieces based on where the user clicks. *Does* check for the location being valid, and updates the board state as it goes. """ # Create and set up the board win = createAndDrawBoard() # Add a label to indicate to the players whose turn it is textLabel = Text(Point(1.5, 3.5), "Place five markers. Click anywhere to start.") textLabel.draw(win) win.getMouse() # wait until they click to start it all # Start with player 1 player = 1 # Maintain the board state as a list of lists; each list # corresponds to a row on the game board boardState = [[0, 0, 0], # top row [0, 0, 0], # middle row [0, 0, 0]] # bottom row # Draw five pieces for i in range(5): # Update the player text setPlayerText(textLabel, player) # Wait for the player to click a valid grid cell isValidCell = False while not isValidCell: gridX, gridY = getGridLocation(win.getMouse()) isValidCell = isValidGridCell(boardState, gridX, gridY) # Update the board state updateBoardState(boardState, gridX, gridY, player) # For debugging: print the board state printBoardState(boardState) # Draw a marker for the current player drawPlayerMarker(win, gridX, gridY, player) # Switch players player = 3 - player # switches between 1 and 2 # Update the text label -- we're done textLabel.setText("All done -- click again to quit.") win.getMouse() win.close() ############################################################## ## The actual game ## ############################################################## def playGame(): """ Play a game of Tic Tac Toe. """ # Create and set up the board win = createAndDrawBoard() # Add a label to indicate to the players whose turn it is textLabel = Text(Point(1.5, 3.5), "") textLabel.draw(win) # Start with player 1 player = 1 # Maintain the board state as a list of lists; each list # corresponds to a row on the game board boardState = [[0, 0, 0], # top row [0, 0, 0], # middle row [0, 0, 0]] # bottom row # Loop until the game is over isGameOver = False while not isGameOver: # Update the player text setPlayerText(textLabel, player) # Wait for the player to click a valid grid cell isValidCell = False while not isValidCell: gridX, gridY = getGridLocation(win.getMouse()) isValidCell = isValidGridCell(boardState, gridX, gridY) # Update the board state updateBoardState(boardState, gridX, gridY, player) # For debugging: print the board state printBoardState(boardState) # Draw a marker for the current player drawPlayerMarker(win, gridX, gridY, player) # Check if the game is over; if not, switch players if didPlayerWin(boardState, player): textLabel.setText("Player {0} wins!".format(player)) isGameOver = True elif isDraw(boardState): textLabel.setText("The game is a draw.") isGameOver = True else: player = 3 - player # switches between 1 and 2 # We're done -- wait for the user to click win.getMouse() win.close() if __name__ == "__main__": # Part 1 testPrintBoardState() # Part 2 # testDrawPlayerMarker() # Part 3 # testPlacingValidMarkers() # Part 4 # playGame() ############################################################## ## Reflection ## ############################################################## # TODO