All course materials and instructions are available in Canvas this year.
In this project, you’ll write a program that lets you play a game of tic-tac-toe against a computer opponent.
The goal of this project is to get you comfortable with using lists and writing functions. (You’ve been using functions all year, but in this project you’ll probably write more of them at one time than you have before.)
Before we start, I want to spend just a quick second talking about how I think about functions, because I think it’s a useful picture to have in your head. It’s helped me a lot.
Functions are like LEGO blocks.
A LEGO block is a small, simple thing—but if you put a bunch of those simple blocks together in the right way, you can make the Milennium Falcon, or the Eiffel Tower, or I don’t know, a pizza or a dog or something.
Functions are just like that. The functions you write should each be small, simple, and easy to understand—but if you put a bunch of your functions together in the right way, you can make a website, or control a robot, or fly a spaceship.
The best part about functions is that they’re even better than LEGO blocks, because you can make your own! When you buy a LEGO set, you’re stuck with whatever kinds of blocks come with the set; when you use a programming language, you can use its built-in functions to make your own way cooler functions that do whatever you want, and then you can put those functions together to make a program that does something awesome. Functions are basically my favorite thing about programming.
OK, enough philosophy. Let’s write some simple functions and put them together to make a tic-tac-toe game!
The game we’re making will look like this (the “index” stuff will make more sense in a second, keep reading):
Welcome to Tic-Tac-Toe!
Do you want to be X or O? X
The computer will go first.
O | |
| |
| |
Choose an index (0-8): 1
O | X |
| |
| |
Computer's turn:
O | X | O
| |
| |
Choose an index (0-8): 4
O | X | O
| X |
| |
Computer's turn:
O | X | O
O | X |
| |
Choose an index (0-8): 7
O | X | O
O | X |
| X |
-------------------
The player wins!
A game of tic-tac-toe takes place on a 3x3 board. A space on the board is either empty, or it has an X in it, or it has an O in it. The board will start off empty (all the strings will be ' '
initally) and it will change over time as the player and computer make their moves.
We need to represent that board in our program somehow - we need to have a variable whose value is the entire tic-tac-toe board. I think that a good way to represent a tic-tac-toe board in Python would be to use a list of strings, like this:
['X', 'O', ' ', ' ', 'O', 'X', 'X', 'O', ' ']
That’s a list of 9 strings, because that’s how many spaces there are on a 3x3 tic-tac-toe board. Each string is either 'X'
or 'O'
or ' '
. The first three elements in the list are the top row of the board, and the second three elements are the middle row, and the last three are the bottom row.
Here’s a visualization of where the indices of the list end up in the printed board:
# A nine-element list has these indexes:
[0, 1, 2, 3, 4, 5, 6, 7, 8]
# Those indexes represent these spots on the game board:
# 0 | 1 | 2
# 3 | 4 | 5
# 6 | 7 | 8
# And this list:
['X', 'O', ' ', ' ', 'O', 'X', 'X', ' ', 'O']
# Represents this board:
# X | O |
# | O | X
# X | | O
Remember that we can get the value of an element in a list by using its index, like this:
board = ['X', 'O', ' ', 'X', 'O', 'X', 'X', ' ', ' ']
# The number of elements in the list:
len(board)
Output:
9
# The second element from the list:
board[1]
Output:
'O'
# The last element from the list:
board[8]
# or:
board[-1]
Output:
' '
Throughout this assignment, I’ll be telling you to write specific functions that behave a certain way. I’ll tell you what the functions should be named; I’ll tell you what inputs the functions should take; and I’ll tell you what outputs the function should return.
It’s really important that your functions have the exact names specified in the assignment. If I tell you to write a function named make_pizza()
, but you write a function named make_hamburger()
or even make_piza()
instead, my tests won’t be able to find your function and you won’t pass the tests for that function.
Start by downloading the starter code for this project. There are two versions: one where you just code the required functions and the game is already coded for you, and one where you code the game yourself along with the required functions.
The starter code defines five empty functions. Your job is to fill in those empty functions using the instructions below. When you’re done, you’ll have a working tic-tac-toe game!
Feel free to add more functions of your own if you want! Just be sure not to change the names of the functions provided in the starter code, because the automated tests will be looking for functions with those names.
OK, here’s what each of those functions should do!
Write a function called make_board
.
[' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ']
That’s it for this one! Hopefully this function feels simple, it’s just a warm-up.
Write a function called print_board
.
None
).Here’s what its output should look like when you pass it a fresh new empty board:
>>> print_board([' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '])
| |
| |
| |
And here’s what it should look like when you pass it the board that we saw earlier:
>>> print_board(['X', 'O', ' ', ' ', 'O', 'X', 'X', ' ', 'O'])
X | O |
| O | X
X | | O
You’ll want to use several print()
calls. Notice how each time you call print()
, Python ends the current line of output and starts a new line. How many print()
calls do you think you’ll need in order to print out a 3x3 tic-tac-toe board? The answer isn’t 9! :)
Remember that if e.g. I wanted to find the value of the third element in the list, I could do board[2]
(assuming my board lived in a variable named board
).
Remember that you can use the +
sign to concatenate small strings together to make a bigger string.
When you’re working on this function (and all the other ones in this assignment!), try it out as you work on it. Use the IDLE shell to call your function with a particular input (an empty board, a half-full board, a full board—you can write all of these boards by hand, they’re just lists of nine strings!), and see how your function behaves when it’s given that input. This trick really, really helps. You’ll end up with working code much faster, and you’ll have more fun doing it.
Next, let’s think about how we’ll handle the player’s move.
Write a function called get_player_move
.
0
and 8
, indicating where the player wants to move. (The game code takes care of changing the board based on this number.)IMPORTANT: This function should NOT MODIFY the input board (e.g. the function should NOT do something like board[5] = 'X'
). There is a test that checks for this.
In general, functions shouldn’t modify their inputs, and should instead just return an output that the program can choose to save. If a program has functions that modify their inputs, that program quickly becomes hard to understand and make changes to. When you’re using a function, you want to just figure out what data it takes as input and what data it returns—you don’t want to also have to ask questions like: “Will this function delete parts of the list I’m passing to it?”.
OK, now let’s implement our AI opponent!
Write a function called get_computer_move
.
'X'
or 'O'
that indicates which team the computer is on.0
and 8
, indicating where the computer wants to move. (The game code takes care of changing the board based on this number.)IMPORTANT: This function should NOT MODIFY the input board (e.g. the function should NOT do something like board[5] = 'X'
). There is a test that checks for this.
The function should pay attention to the value of computer_team
so it knows whether the computer is X's or O's.
Start by making a simple AI that picks the first empty space it finds on the board. If it chooses a space that already has an 'X'
or an 'O'
in it, that’s a bug, and the tests will find it.
Once you’ve finished all 5 required functions and have a completely working tic-tac-toe game, come back to this function and make it smarter. The goal is for this function to crush the human player (or at least force a tie)! A good trick is to check to see if the player’s about to win on their next move. If that’s the case, the computer should make a move on the spot the player needs so that the player isn’t able to use it!
We’re almost done with our program now—just one more function to go!
Write a function called check_for_winner
.
'X'
, 'O'
, 'tie'
, or 'keep playing'
.Double-check to make sure that you’re returning the right values:
'x'
instead of 'X'
, that’s a bug.'keepplaying'
instead of 'keep playing'
, that’s a bug.Be sure to test your tic tac toe game several times by running the entire program. (If you chose the bare code version of the starter code, now would be a good time to try your hand at the game loop code.) Once you are satisfied, go back and improve your AI!
Before submitting your project, review the rubric and make sure to test your program a bunch of times to make sure it works!
Your CODE should follow the style guidelines. The part about descriptive names is important! For instance:
n
is a bad name, username
is a good one.ns
is a bad name, number_of_symbols
is a good one.On the first line of that file, write a comment with your name on it, like this:
# Tamara O'Malley
On Google Classroom, submit your program in a file called tictactoe_yourname.py
. For instance, I might submit a file called tictactoe_Tamara.py
.
The projects in this class were created by JR Heard, a TEALS volunteer at Madison, 2017-2019. His version of this project lives at https://blog.jrheard.com/python/tic-tac-toe.