# File: tictactoeExtension.py # Purpose: A game of tic-tac-toe to be played between two players, # extended to an nxn board instead of just 3x3. # Author: TODO # # Collaboration statement: TODO # # Inputs: mouse clicks! from graphics import * import math import random ############################################################## ## Helper functions for Tic-Tac-Toe ## ############################################################## def createAndDrawBoard(n): """ Creates a window and draws the board in it. n: the size of the grid (an int) returns: a GraphWin object """ # Create the window win = GraphWin("Tic Tac Toe", 600, 600) win.setCoords(-0.25, n + 0.75, n + 0.25, -0.25) # give a little buffer win.setBackground("white") # Draw the lines for i in range(1, n): horizontalLine = Line(Point(0, i), Point(n, i)) horizontalLine.draw(win) verticalLine = Line(Point(i, 0), Point(i, n)) 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 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 pass # replace with your code def printBoardState(boardState): """ Prints out the current board state for debugging. boardState: a nested list containing the board state """ n = len(boardState) print() # Print each row, starting with the top for row in range(n): printRow(boardState, row) # If it wasn't the bottom row, also print a divider line if row < n-1: print("-" * (2*n - 1)) ############################################################## ## 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 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 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 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 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 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 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 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 return False # replace with your code def didPlayerWinWithSquare(boardState, player): """ Returns a Boolean indicating whether the player won the game due to an entire square of side-length m=ceil(n/2). boardState: a nested list containing the board state player: 1 or 2 returns: a Boolean (True if the player has an mxm square, where m=ceil(n/2), and False otherwise) """ # TODO (hint: it may be easier to add a function to check a given square) 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) """ n = len(boardState) # First, check the rows for row in range(n): if didPlayerWinWithRow(boardState, player, row): return True # Second, check the columns for col in range(n): if didPlayerWinWithColumn(boardState, player, col): return True # Then, check the diagonals if didPlayerWinWithDiagonal(boardState, player): return True if didPlayerWinWithSquare(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(n): """ 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 = [] for i in range(n): row = [random.randint(0,2) for j in range(n)] boardState.append(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) def testDrawPlayerMarker(n): """ 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(n) # Add a label to indicate to the players whose turn it is textLabel = Text(Point(n/2, n + 0.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(n): """ 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(n) # Add a label to indicate to the players whose turn it is textLabel = Text(Point(n/2, n + 0.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]*n for i in range(n)] # 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(n): """ Play a game of Tic Tac Toe. """ # Create and set up the board win = createAndDrawBoard(n) # Add a label to indicate to the players whose turn it is textLabel = Text(Point(n/2, n + 0.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]*n for i in range(n)] # 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__": # CHOOSE n :D n = int(input("How big is the board (ex: 3)? ")) # Part 1 testPrintBoardState(n) # Part 2 # testDrawPlayerMarker(n) # Part 3 # testPlacingValidMarkers(n) # Part 4 # playGame(n) ############################################################## ## Reflection ## ############################################################## # TODO