Today's lab will use decisions to extend our simple Tic-Tac-Toe game to print who has won the game. In the second part of the lab, we will also revisit finding errors.

Turtle Tic-Tac-Toe (Version 1)

For today's lab, we're going to extend a very simple Tic-Tac-Toe program from Tic Tac Toe Lab 1.

Our first program allowed the user to specify moves. Below is the program organized into functions:

#Introductory Program, Spring 2017
#Lehman College, City University of New York
#First Version of Tic-Tac-Toe
#   This version does NO checking of anything (it doesn't 
#   check who wins, doesn't check for legal entries, etc).
#   We will add that later in the semester

from turtle import *

def setUp():
    #Set up the screen and turtle
    win = Screen()
    tic = Turtle()
    tic.speed(10)
    #Change the coordinates to make it easier to tranlate moves to screen coordinates:
    win.setworldcoordinates(-0.5,-0.5,3.5, 3.5)

    #Draw the vertical bars of the game board:
    for i in range(1,3):
        tic.up()
        tic.goto(0,i)
        tic.down()
        tic.forward(3)

    #Draw the horizontal bars of the game board:
    tic.left(90)    #Point the turtle in the right direction before drawing
    for i in range(1,3):
        tic.up()
        tic.goto(i,0)
        tic.down()
        tic.forward(3)

    tic.up()        #Don't need to draw any more lines, so, keep pen up
    return(win,tic)

def playGame(tic):
    #Ask the user for the first 8 moves, alternating between the players X and O:
    for i in range(4):
        x = int(input("Enter x coordinates for X's move: "))
        y = int(input("Enter y coordinates for X's move: "))
        tic.goto(x+.25,y+.25)
        tic.write("X",font=('Arial', 90, 'normal'))
        x = int(input("Enter x coordinates for O's move: "))
        y = int(input("Enter y coordinates for O's move: "))                 
        tic.goto(x+.25,y+.25)
        tic.write("O",font=('Arial', 90, 'normal'))
        
    # The ninth move:
    x = int(input("Enter x coordinates for X's move: "))
    y = int(input("Enter y coordinates for X's move: "))
    tic.goto(x+.25,y+.25)
    tic.write("X",font=('Arial', 90, 'normal'))

def cleanUp(tic,win):
    #Display an ending message: 
    tic.goto(-0.25,-0.25)
    tic.write("Thank you for playing!",font=('Arial', 20, 'normal'))
    
    win.exitonclick()#Closes the graphics window when mouse is clicked


def main()
    win,tic = setUp()   #Set up the window and game board
    playGame(tic)       #Ask the user for the moves and display
    #print("\nThe winner is", checkWinner(board))  #Check for winner
    cleanUp(tic,win)    #Display end message and close window


main()
We would like to print if there is a winner for our game (the line in the main that is commented out). Our current program asks the user for the move, draws it to the graphics windown, but does not save the move. If we are going to check for winners, we will need to save the moves. We can do this using a lists:
	board = [["","",""],["","",""],["","",""]]
Each position corresponds to a position on the screen and all are currently set to empty strings. But, each time someone makes a move, we can add in the move to the list board so that we can check at the end who has one. Every time you draw a move on the graphics window, also save it in the game board. For example, if user "X" chooses (x,y), then we can store that move by:
	board[x][y] = "X"

Modify the end of setUp() include setting up the game board and returning it:

    #Set up board:
    board = [["","",""],["","",""],["","",""]]
    
    return(win,tic,board)
Modify the playGame() function to stores moves:
def playGame(tic,board):
    #Ask the user for the first 8 moves, alternating between the players X and O:
    for i in range(4):
        x = int(input("Enter x coordinates for X's move: "))
        y = int(input("Enter y coordinates for X's move: "))
        tic.goto(x+.25,y+.25)
        tic.write("X",font=('Arial', 90, 'normal'))
        board[x][y] = "X"
        
        x = int(input("Enter x coordinates for O's move: "))
        y = int(input("Enter y coordinates for O's move: "))                 
        tic.goto(x+.25,y+.25)
        tic.write("O",font=('Arial', 90, 'normal'))
        board[x][y] = "O"
        
    # The ninth move:
    x = int(input("Enter x coordinates for X's move: "))
    y = int(input("Enter y coordinates for X's move: "))
    tic.goto(x+.25,y+.25)
    tic.write("X",font=('Arial', 90, 'normal'))
    board[x][y] = "X"
Add in the new function:
def checkWinner(board):
    return("No winner")
(we will add more to this).

Lastly, change your main() function to keep track of board moves:

def main():
    win,tic,board = setUp()   #Set up the window and game board
    playGame(tic,board)       #Ask the user for the moves and display
    print("The winner is", checkWinner(board))  #Check for winner
    cleanUp(tic,win)    #Display end message and close window
Try your program. What happens at the end? Who is the winner? Is it always the same? Why?

Our current program does no checking but just returns "No Winner" no matter what is played. We will add in checks to see who has won. Let's start with the row (0,0), (1,0), and (2,0). What does it mean for someone to win on that row? The entries have to be non-empty and all the same:

def checkWinner(board):
    if board[0][0] != "" and (board[0][0]== board[0][1] == board[0][2]):
        return(board[0][0])  #we have a non-empty row that's identical
    return("No winner")
Note that if the row is identical and non-empty, then the conditional test is true, and the return command is performed, leaving the function immediately.

Since the same pattern holds for the rows where x=1 and x=2, we can put the if-statement into a loop:

def checkWinner(board):
    for x in range(3):
        if board[x][0] != "" and (board[x][0]== board[x][1] == board[x][2]):
            return(board[x][0])  #we have a non-empty row that's identical
    return("No winner")
Try adding in the same check for the columns without peeking below. Here's the answer if you get stuck:
def checkWinner(board):
    for x in range(3):
        if board[x][0] != "" and (board[x][0]== board[x][1] == board[x][2]):
            return(board[x][0])  #we have a non-empty row that's identical
    for y in range(3):
        if board[0][y] != "" and (board[0][y]== board[1][y] == board[2][y]):
            return(board[0][y])  #we have a non-empty row that's identical    
    return("No winner")
Lastly, we need to check for winners on the diagonal. That is, the moves at (0,0), (1,1), and (2,2) are identical (and non-empty) or (0,2), (1,1), and (2,0) are identical (and non-empty). Try writing this before looking at the answer below:
def checkWinner(board):
    for x in range(3):
        if board[x][0] != "" and (board[x][0] == board[x][1] == board[x][2]):
            return(board[x][0])  #we have a non-empty row that's identical
    for y in range(3):
        if board[0][y] != "" and (board[0][y] == board[1][y] == board[2][y]):
            return(board[0][y])  #we have a non-empty column that's identical
    if board[0][0] != "" and (board[0][0] == board[1][1] == board[2][2]):
        return(board[0][0])
    if board[2][0] != "" and (board[2][0] == board[1][1] == board[2][0]):
        return(board[2][0])   
    return("No winner")
Try playing the game several times. What happens if there is no winner? What happens if there is more than one? What happens if both players chose the same move? Whose move is counted when computing the winner? Why?

The complete program:

#Introductory Program, Spring 2017
#Lehman College, City University of New York
#Second Version of Tic-Tac-Toe
#   This version checks the number of winning 
#	positions at the end of the game.

from turtle import *

def setUp():
    #Set up the screen and turtle
    win = Screen()
    tic = Turtle()
    tic.speed(10)
    #Change the coordinates to make it easier to tranlate moves to screen coordinates:
    win.setworldcoordinates(-0.5,-0.5,3.5, 3.5)

    #Draw the vertical bars of the game board:
    for i in range(1,3):
        tic.up()
        tic.goto(0,i)
        tic.down()
        tic.forward(3)

    #Draw the horizontal bars of the game board:
    tic.left(90)    #Point the turtle in the right direction before drawing
    for i in range(1,3):
        tic.up()
        tic.goto(i,0)
        tic.down()
        tic.forward(3)

    tic.up()        #Don't need to draw any more lines, so, keep pen up

    #Set up board:
    board = [["","",""],["","",""],["","",""]]
    
    return(win,tic,board)

def playGame(tic,board):
    #Ask the user for the first 8 moves, alternating between the players X and O:
    for i in range(4):
        x = int(input("Enter x coordinates for X's move: "))
        y = int(input("Enter y coordinates for X's move: "))
        tic.goto(x+.25,y+.25)
        tic.write("X",font=('Arial', 90, 'normal'))
        board[x][y] = "X"
        
        x = int(input("Enter x coordinates for O's move: "))
        y = int(input("Enter y coordinates for O's move: "))                 
        tic.goto(x+.25,y+.25)
        tic.write("O",font=('Arial', 90, 'normal'))
        board[x][y] = "O"
        
    # The ninth move:
    x = int(input("Enter x coordinates for X's move: "))
    y = int(input("Enter y coordinates for X's move: "))
    tic.goto(x+.25,y+.25)
    tic.write("X",font=('Arial', 90, 'normal'))
    board[x][y] = "X"

def checkWinner(board):
    for x in range(3):
        if board[x][0] != "" and (board[x][0] == board[x][1] == board[x][2]):
            return(board[x][0])  #we have a non-empty row that's identical
    for y in range(3):
        if board[0][y] != "" and (board[0][y] == board[1][y] == board[2][y]):
            return(board[0][y])  #we have a non-empty column that's identical
    if board[0][0] != "" and (board[0][0] == board[1][1] == board[2][2]):
        return(board[0][0])
    if board[2][0] != "" and (board[2][0] == board[1][1] == board[2][0]):
        return(board[2][0])   
    return("No winner")

def cleanUp(tic,win):
    #Display an ending message: 
    tic.goto(-0.25,-0.25)
    tic.write("Thank you for playing!",font=('Arial', 20, 'normal'))
    
    win.exitonclick()#Closes the graphics window when mouse is clicked


def main():
    win,tic,board = setUp()   #Set up the window and game board
    playGame(tic,board)       #Ask the user for the moves and display
    print("\nThe winner is", checkWinner(board))  #Check for winner
    cleanUp(tic,win)    #Display end message and close window


main()

If you finish early, you may work on the programming problems.