TicTacToe Exercise

Thus exercise serves as the "show your instructor that you're ready to move on to Object-Oriented Programming" experience. In the process you should learn a few new things, and firm up what you already know.  This is not an easy exercise.  It is best to work with a partner, and to get help when needed.  Don't move on to the next part until you understand the code from the current part.

TicTacToe is usually played on a 3-by-3 square board.  To make things more interesting (and to help you learn how to write flexible code) our version will allow any square board size.  To see what the finished product might look like, run this file:  TicTacToe6.pyc. After you enter the board size (I suggest entering 3, 4, or 5), and press Enter, click the mouse on the board.  Note that it is possible that the board will pop up behind other windows.  If you don't see the board after entering the board size, try moving or hiding some other windows.

One step at a time!

After you have completed each step, show your code to the instructor or assistant. Of course, we will also be available to give you help whenever you need it. And you can also get help from other students. But don't just let someone tell you the code. Make sure that you understand each thing you do before you go on.
  1. (All good programmers start with 0.) Write a simple program called TicTacToe.py that creates a window and draws a 3-by-3 TicTacToe grid. Your finished program may look something like TicTacToe0.pyc . Try it out before writing your code.

    This task is fairly simple, but you should do it in a way that will help you plan ahead for future versions.  Here are a few lines in my code that you may want to use. Defining named constants and using them in your code instead of numbers can avoid the use of "magic numbers" that can make code difficult to enhance and maintain.  In this case, the board size will not always be 3 in later versions of the program,

         BOARDSIZE  = 3                   # number of rows and columns
         BOARDRANGE = range(BOARDSIZE)    # range of rows and columns
         PPS        = 150                 # pixels per square
         WINDOWSIZE = PPS * BOARDSIZE     # width (and height) of window
         INSET      = 15                  # num pixels around X's and O's in squares
         
      

    If you want to make the board linger on the screen for a few seconds and then disappear like the sample program does, import the time module, and include the line time.sleep(3) before your window.close() statement.  Don't forget that you also need to import the zellegraphics module.
     

  2. Enhance your program so it asks the user for the board size, then draws the grid, as in this program: TicTacToe1.pyc . The number the user enters can become the new value of BOARDSIZE; then after calculating WINDOWSIZE so that it will hold the different-sized board, you probably do not have to change the rest of the program . If you are a beginning programmer, you may want to skip the part that lets the user simply press ENTER, and instead require the user to always enter a number. (Hint for everyone else: look up the input function.
     
  3. Define two new functions, drawX and drawO. Each of them should take two arguments, a row number and a column number. For example, the call drawX(1, 2) should cause an X to be drawn in the second row and third column (don't forget that row and column numbers start with 0), while the call drawO(0, 2) should cause an O to be drawn in the first row and third column.  See this example: TicTacToe2.pyc . You may find it helpful to write some helper functions that map row and column positions to window pixel locations.  For, example, I wrote this function to calculate the coordinates of the upper-right point of an X that is to be drawn in the square in a given row and column:
        def rectUpperRight(row, col):
        'coordinates of top right of inset X or O'
        return Point(PPS*(col+1) - INSET, PPS*row + INSET)
    
    Call each function with some different values for the parameters and verify that they work correctly. Can you put an X and an O in the same square? (In a later step you will solve this).
     
  4. Enhance your program (using loops) by writing a fillBoard( ) function that fills the entire board with alternating Xs and Os, as in this example: TicTacToe3.pyc. Your code should work similarly to mine whether the board size is even or odd.  Don't forget to actually call fillBoard!
     
  5. Use the window's mouseClick() method (i.e. a function associated with an object) to determine where the user clicks, and draw an X  in that square.  The next click should cause an O to be placed, etc.  You can use simple arithmetic to get from the coordinates of the clicked point to the row and colum number of the board square that contains that point.  When there have been enough clicks to fill the board, the program should terminate.  At this point, we have no mechanism for keeping track of previously-clicked squares, so there is no way to prevent having an X and an O drawn in the same square.  Here is my example:  TicTacToe4.pyc.  [Hint: You should remove the call to fillBoard .]
     
  6. Keep track of which squares on the board are already filled. I suggest using a two-dimensional list of numbers, one number for each square on the board. I used -1 to represent that the corresponding square is unfilled , 0 for an X, and 1 for a Y.
    For example, if the board is , then the corresponding nested list is
    [[-1, 1, 0, -1], [-1, -1, 0, -1], [0, 1, -1, -1], [-1, -1, -1, -1]].
    Now, when the user clicks a square, see if the corresponding list position contains -1. If so, draw the appropriate X or O, and mark the corresponding list position to indicate which symbol is in that square.  If not, ignore the click altogether (make sure that you only count the number of successful clicks, so that the program will not quit until the board is actually full).  Here is my example TicTacToe5.pyc
     
  7. After each successful user click, check to see if this move caused a win by marking an entire row, column or diagonal containing the same symbol (X or O). I found it easiest to do this by breaking the task up into several small tasks and writing a short function for each task. If there is a winner, draw a line through the winning squares, and stop the loop by using a break statement. My example is the one that you ran at the beginning of this document.
Whew! There was a lot to do, but if you have successfully navigated it, you are now a certified Catapult programmer!