Test Project Due: 4:15 PM on 2019-05-13

A test project is an assignment that you complete on your own, without the help of others. It is like a take-home exam. You may use the textbook, your notes, your previous assignments, the notes and examples on the course web page and the Python library documentation (linked from the course web page). Use of any other source, such as Google, is not permitted. You may not work with, discuss, or in any way collaborate with anyone else. You may only ask the tutors or ASI for help with hardware problems or difficulties submitting your program.

You are encouraged to reuse useful code from your assignments or our class examples. Partial credit will be awarded, so if you can’t solve the whole problem, get as far as you can. If you are stuck, contact me. While I cannot help you solve the test project itself, I may be able to suggest general problem-solving strategies, help with conceptual difficulties about Python, and/or direct you to relevant examples from class.

Tic-Tac-Toe

For this test project you will implement a text-based version of the game Tic-Tac-Toe in Python. See the Wikipedia page on the game for more information.

When the game starts, the players are shown the tic-tac-toe board with the squares labeled 1 through 9:

     |     |     
  1  |  2  |  3  
_____|_____|_____
     |     |     
  4  |  5  |  6  
_____|_____|_____
     |     |     
  7  |  8  |  9  
     |     |     

During game play, the program asks each player in turn to enter a single digit 1..9 indicating which square they choose for their ‘X’ or ‘O’.

If the user enters invalid input (anything except digits 1..9) the program notifies the user of the invalid input and prompts them again:

Player 1's move: a
Invalid move. Enter 1..9:

If the user enters a square that is already filled, the program notifies the user and prompts them again:

That square is already taken.
Enter a blank square among 1..9:

The game ends with a win for a player if they have marked three squares in a row – either horizontally, vertically, or diagonally. The game ends in a tie if all squares have been marked but neither player has achieved three squares in a row.

For example:

     |     |     
  X  |  X  |  O  
_____|_____|_____
     |     |     
  O  |  O  |  X  
_____|_____|_____
     |     |     
  X  |  O  |  X  
     |     |     

It's a tie!

Demos

Here is a transcript of a game using a minimal working program.

>>> %Run tp_tictactoe.py
Welcome to tic-tac-toe!

Board squares are numbered 1..9 as seen here:

     |     |     
  1  |  2  |  3  
_____|_____|_____
     |     |     
  4  |  5  |  6  
_____|_____|_____
     |     |     
  7  |  8  |  9  
     |     |     

At your turn, enter a number 1..9

     |     |     
     |     |     
_____|_____|_____
     |     |     
     |     |     
_____|_____|_____
     |     |     
     |     |     
     |     |     

Player 1's move [1..9]: 5

     |     |     
     |     |     
_____|_____|_____
     |     |     
     |  X  |     
_____|_____|_____
     |     |     
     |     |     
     |     |     

Player 2's move [1..9]: 3

     |     |     
     |     |  O  
_____|_____|_____
     |     |     
     |  X  |     
_____|_____|_____
     |     |     
     |     |     
     |     |     

Player 1's move [1..9]: 8

     |     |     
     |     |  O  
_____|_____|_____
     |     |     
     |  X  |     
_____|_____|_____
     |     |     
     |  X  |     
     |     |     

Player 2's move [1..9]: 2

     |     |     
     |  O  |  O  
_____|_____|_____
     |     |     
     |  X  |     
_____|_____|_____
     |     |     
     |  X  |     
     |     |     

Player 1's move [1..9]: 2

     |     |     
     |  O  |  O  
_____|_____|_____
     |     |     
     |  X  |     
_____|_____|_____
     |     |     
     |  X  |     
     |     |     

That square is already taken. 
Enter a blank square among 1..9: nine

     |     |     
     |  O  |  O  
_____|_____|_____
     |     |     
     |  X  |     
_____|_____|_____
     |     |     
     |  X  |     
     |     |     

Invalid move. Enter 1..9: 9

     |     |     
     |  O  |  O  
_____|_____|_____
     |     |     
     |  X  |     
_____|_____|_____
     |     |     
     |  X  |  X  
     |     |     

Player 2's move [1..9]: 1

     |     |     
  O  |  O  |  O  
_____|_____|_____
     |     |     
     |  X  |     
_____|_____|_____
     |     |     
     |  X  |  X  
     |     |     

Player 2 wins!

Thanks for playing!
>>> 

Here is a transcript of a game using a program with additional creativity features.

>>> %Run tp_tictactoe.py
Welcome to tic-tac-toe!

Name of player 1 (playing X's)? Anna
Name of player 2 (playing O's)? Mom

Board squares are numbered 1..9 as seen here:

     |     |     
  1  |  2  |  3  
_____|_____|_____
     |     |     
  4  |  5  |  6  
_____|_____|_____
     |     |     
  7  |  8  |  9  
     |     |     

At your turn, enter a number 1..9, or ? for this help message

     |     |     
     |     |     
_____|_____|_____
     |     |     
     |     |     
_____|_____|_____
     |     |     
     |     |     
     |     |     

Anna's move [1..9 or ?]: 1

     |     |     
  X  |     |     
_____|_____|_____
     |     |     
     |     |     
_____|_____|_____
     |     |     
     |     |     
     |     |     

Mom's move [1..9 or ?]: ?

Board squares are numbered 1..9 as seen here:

     |     |     
  1  |  2  |  3  
_____|_____|_____
     |     |     
  4  |  5  |  6  
_____|_____|_____
     |     |     
  7  |  8  |  9  
     |     |     

At your turn, enter a number 1..9, or ? for this help message

     |     |     
  X  |     |     
_____|_____|_____
     |     |     
     |     |     
_____|_____|_____
     |     |     
     |     |     
     |     |     

Mom's move [1..9 or ?]: 7

     |     |     
  X  |     |     
_____|_____|_____
     |     |     
     |     |     
_____|_____|_____
     |     |     
  O  |     |     
     |     |     

Anna's move [1..9 or ?]: 3

     |     |     
  X  |     |  X  
_____|_____|_____
     |     |     
     |     |     
_____|_____|_____
     |     |     
  O  |     |     
     |     |     

Mom's move [1..9 or ?]: 2

     |     |     
  X  |  O  |  X  
_____|_____|_____
     |     |     
     |     |     
_____|_____|_____
     |     |     
  O  |     |     
     |     |     

Anna's move [1..9 or ?]: 9

     |     |     
  X  |  O  |  X  
_____|_____|_____
     |     |     
     |     |     
_____|_____|_____
     |     |     
  O  |     |  X  
     |     |     

Mom's move [1..9 or ?]: 5

     |     |     
  X  |  O  |  X  
_____|_____|_____
     |     |     
     |  O  |     
_____|_____|_____
     |     |     
  O  |     |  X  
     |     |     

Anna's move [1..9 or ?]: 6

     |     |     
  X  |  O  |  X  
_____|_____|_____
     |     |     
     |  O  |  X  
_____|_____|_____
     |     |     
  O  |     |  X  
     |     |     

Anna wins!

Thanks for playing, Anna and Mom!
>>> 

Specifications

  1. Your program must implement the game play shown above in the minimal solution:

    • The displayed board must look exactly like the board shown above.

    • It must check the user’s input and make sure it is a valid single digit 1..9. It must check that the given square number is not yet taken. It will continue to ask the user for input until a valid empty square number is entered.

    • Once the user enters a valid empty square number, the game state is updated appropriately to reflect the new move, and the updated board is displayed.

    • The game should finish as soon as a player wins. If the board becomes filled and neither player has won, the game ends in a tie.

  2. Your game must be launched by invoking a function named play_game that does not take any parameters.

  3. When run, your program should start playing the game automatically. If your module is imported, however, the game should not start playing.

  4. All functionality should be launched from play_game, i.e., the main program should simply call play_game and not do anything else. If your program is imported, the user should be able to play the game by just calling play_game.

Creativity

If you were to follow the above requirements and also get full credit for style and documentation, you would earn 43 of 45 points. For the remaining two points, your program should add additional features. You could give the option of a single-player mode that plays against the computer, or you could add features such as those in the demo above which includes asking the player names and providing a help mechanism.

Guide

As always, I strongly suggest an incremental approach to developing your program. Before you start programming, develop a design (like you did for lab 8) that describes what functions you will need, the parameters each function should take and how each function will work. You won’t turn this in, but it will save you a lot of time if you think through the design of the program first. As you start to code, work incrementally - pick one function and get it working before moving on. Read through this entire lab before you start the program.

Representing state

As you’re thinking about how you will write your program, think about what information you need to store and update as the game progresses and how you are going to represent this information. For example, for hangman we had a number of pieces of information:

What information do we need to keep track of for this game? And how will it be stored? As a string, a list, a set, a dictionary, etc.? You should choose data structures that are convenient and efficient.

Game play

Your program will necessarily have a loop to repeat the steps of each “turn. In each “turn”:

  1. A valid move should be obtained from the user
  2. The current version of the board should be displayed
  3. Check whether the move wins the game

I suggest starting with this rough sketch of each “turn” and adding detail during the planning phase.

A suggested approach

There are many ways to implement this program. Here is one approach:

  1. Pick your representation of the game state. Write a function that prints the board for a given game state.
  2. Write a function that repeatedly asks the user for input until they enter a valid empty square number.
  3. Add functionality to update the game state with a player’s move.
  4. Write a function to check whether the game should continue or is over with a win or a tie.

When you write a function, test it as a stand-alone function from the Python shell.

A Python Tip

To avoid unwanted blank lines in what gets printed, recall these two approaches:

Use the optional keyword parameter end to specify what should be printed after each item (by default end is the newline character). See help(print). For example:

>>> for i in range(3):
...     print(i, end="")
...
012>>>

Or, build up a string, and then print that string:

>>> s = ''
>>> for i in range(3):
...     s += str(i)
...
>>> print(s)
012

In either approach, you can embed the newline character ‘\n’ wherever needed.

When you’re done

Before submitting your code, double check to make sure you have satisfied all of the requirements and your program runs properly in a newly started Python shell. One third of the points for the test project come from style, so be sure to check your program is implemented with good coding style.

In particular, make sure that your program is properly documented:

And use good style:

Submit your program via Gradescope. Your program program file must be named tp_tictactoe.py. You can submit multiple times, with only the most recent submission (before the due date) graded. Note that the tests performed by Gradescope are limited. Passing all of the visible tests does not guarantee that your submission correctly satisfies all of the requirements of the assignment.

Gradescope will import your file for testing so make sure that, as specified, no code executes on import. That is, when imported your program should not start the game.

Feature Points
Style/comments 15
if/while/for  
Variable naming  
Comments and docstrings  
Formatting  
Parameters  
Additional functions  
Constants  
Miscellaneous  
Functionality  
play_game() defined and takes no parameters 1
Starts properly when run 2
Initial numbered board displays correctly 4
Loops until player gives valid input 4
Properly updates game state 3
Game board displays correctly 4
Properly checks for win or tie 4
Loops until game completed 4
Properly reports win or tie 2
Creativity: e.g., computer player, names, help 2
Total 15 + 28 + 2 = 45