CPTR 124 Fundamentals of Programming


In this lab you will write functions that control the logic of a graphical Tic-Tac-Toe game.


  1. Teams

    You are welcome to work with a partner on this lab. You and your partner should begin thinking about the problems and begin writing the code before lab time. You may work by yourself if you wish.

  2. What to do

    For a background on the Tic-Tac-Toe game, see http://en.wikipedia.org/wiki/Tic-tac-toe. Your task is to provide the controlling logic for a graphical two-player Tic-Tac-Toe computer game. A game in progress might look like

    Screenshot of the Tic-Tac-Toe game

    Since it is a two-player game, your program does not play against a human opponent but simply allows the user(s) to interact with the board, making marks with the mouse.

    We will divide the program into two separate components: the program logic and the presentation. The program logic forms the game engine, enforcing the rules of the game. The presentation is the interface to the user. The presentation can be graphical (pointing device and graphical screen), text based (keyboard input and text ouput), or something else, such as hand gestures, voice recognition, brain wave analysis, etc.

    The following table shows some of the division of labor between the program logic and presentation:

    Responsibilty of Engine

    Responsibility of Presentation

    Keep track of whose turn it is

    Indicate the current player (X cursor or O cursor)

    Keep track of the marks in each square

    Indicate to the user the marks in each square (draw an X, O, or nothing)

    Determine if there is a winner

    Show the winning configuration as appropriate (highlight winning squares)

    Disallow illegal moves

    Provide feedback to user about an illegal move (display message box to user)

    Place a mark in a specified position, if possible

    Allow user to specify a move (process mouse clicks over the board)

    Reset the game

    Automatic after each game

    The presentation gets all of its information about what to display from the game engine, and the game engine gets all of its input from the presentation.

    We will implement this clean separation of control logic and presentation by writing a program that consists of two separate Python source files: tictactoe.py and ttt_logic.py.

    Presentation. The file tictactoe.py is responsible for the presentation. It provides a graphical user interface (GUI) that allows the user to make moves with a pointing device such as a mouse or touch pad. The GUI draws a natural looking Tic-tac-toe game board within a window (see the figure above). The file tictactoe.py provides all the code to make to make the graphical interface work, so you should not touch any code within the tictactoe.py file.

    Game Logic. The file ttt_logic.py is responsible for making the game work properly and enforcing the rules. Your job is to complete the ttt_logic.py file—in essence to program the rules of the game in Python. Some of the things your code must handle include

    • ignore illegal moves like trying to make a mark over an existing mark.
    • control whose turn it is
    • detect when the game is over (win or draw).

    Study the comments in the ttt_logic.py Python module. The file contains the skeletal definitions of five functions. Observe that several of the functions accept string parameters and may return string results. It is important to use the exact strings specified in the comments so that the engine and the presentation can communicate successfully. The following is a comprehensive list of the strings the game engine and presentation use for communicating and making the game work:

    String

    Meaning

    Role

    'X'

    Player X or player X's mark

    Player management

    'O'

    Player O or player O's mark

    Player management

    'NorthWest'

    Top-left square

    Board location

    'North'

    Top-middle square

    Board location

    'NorthEast'

    Top-right square

    Board location

    'West'

    Left-middle square

    Board location

    'Center'

    Center square

    Board location

    'East'

    Right-middle square

    Board location

    'SouthWest'

    Bottom-left square

    Board location

    'South'

    Bottom-middle square

    Board location

    'SouthEast'

    Bottom-right square

    Board location

    'Playing'

    No one has won and a move is available

    Game state

    'Win_NW_NE'

    Win across top row

    Game state

    'Win_W_E'

    Win across middle row

    Game state

    'Win_SW_SE'

    Win across bottom row

    Game state

    'Win_NW_SW'

    Win along left column

    Game state

    'Win_N_S'

    Win along center column

    Game state

    'Win_NE_SE'

    Win along right column

    Game state

    'Win_NW_SE'

    Win from left-top corner to right-bottom corner

    Game state

    'Win_NE_SW'

    Win from right-top corner to left-bottom corner

    Game state

    'Draw'

    All squares filled with no winner

    Game state

    Your task is to implement the missing code in the functions and add global variables as needed to keep track of the state of the game.

    • look expects a location string ('NorthWest', 'North', etc.) as a parameter and returns the mark in that particular square ('X' or 'O') or None (if the square is empty). The graphical system uses the look function to determine what, if anything, to draw in a given square.

    • move puts the mark of a player ('X' or 'O') in a given square, if possible. The exact mark depends on the player with the current turn. A mark may be placed in a square only if the square is unoccupied. The move function requires its parameter to be one of the board location strings ('NorthWest', 'North', etc.). If the move is a valid move, it addition to placing the mark, the function should ensure that turn transitions to the other player and finally return True If the attempted move is not valid, the function should not change the state of the game and simply return False.

    • initialize_board clears all marks off the board making it ready for a new game. It also makes player X the current player. The function should reset any data the program may be using to monitor or control the progress of a game.

    • current_player returns the player whose turn it is ('X' or 'O').

    • check_status determines if one player has won, the game is a draw, or if the game can continue. It returns a value the graphical system can use to render the board properly; for example, if player X has three marks in a line from the northeast corner of the board to the southwest corner as shown in the figure below, check_status would return to the graphical interface the tuple ('X', 'Win_NE_SW'). The first element of the tuple is the winner, and the second element represents winning configuration. With this tuple the graphical interface in turn produces the presentation shown in the figure below. The playing and draw tuples would be (None, 'Playing') and (None, 'Draw'), respectively, as neither status involves a winner.

      The check_status function should not in any way alter the state of the game.

      Screenshot of the Tic-Tac-Toe game

    Think about it: If you can clear the board, put marks in specified squares, see what is in a given square, and check to see if the game is over or should continue, you have all the pieces necessary to model the control logic for a two-player Tic-Tac-Toe game.

    In conjunction with implementing the five functions above, you will need to add global variables in the ttt_logic.py module that maintain the state of the game. Your functions will use and potentially modify these global variables as the game progresses. You need a variable to keep track of whose turn it is. You need nine variables that keep track of the marks assigned to each of the squares on the Tic-tac-toe board. You may want to add a variable to keep track of the number of moves during a game (so you can declare a draw if no one has won after nine moves)—although you can handle a tie in other ways.

  3. Code Organization

    Put tictactoe.py and ttt_logic.py in the same folder. Do not touch tictactoe.py. You will work exclusively in ttt_logic.py. Implement the functions defined in ttt_logic.py. The comments in ttt_logic.py provide additional detail about what the functions do.

    Important! You should not modify the file tictactoe.py; it must be used as is. Also, your ttt_logic.py file should contain NO input or output statements; that is, print and input should not appear in your code. It is fine to use printing statements during development to help debug your code, but you should remove them when your work is complete.

  4. Testing

    When you are finished, thoroughly play with your Tic-Tac-Toe game, trying all combinations of moves, to ensure that your logic is correct. Does your code handle tie games properly? Does player X always have the first turn in a new game? If the very last move in a game uses the last available square but results in a win, does your code recognize the win or does it mistakenly report a draw?

  5. Check out

    Your finished program will be evaluated for correctness and compliance. Double check to ensure the following:

    • that your ttt_logic.py file contains no printing statements,

    • that your ttt_logic.py file executes properly with the unmodified tictactoe.py file, and

    • that two players can play your graphical Tic-Tac-Toe game and things work as they would in the paper version.

    Follow the special instructions given during lab for the evaluation component. When approved, you should submit your Python source file to http://eclass.e.southern.edu. Be sure your name (and your partner's name, if necessary) are included in comments at the top of each source file.