Short Exercises #4

Due: Wednesday, November 2nd at 4:30pm CT

The following short exercises are intended to help you practice some of the programming concepts introduced in the first four weeks of the quarter. These exercises should not take more than 1-2 hours in total to complete.

Fetching the instructor files

To get the files for this set of short exercises, first set the GITHUB_USERNAME environment variable by running the following command at the Linux command line (replacing replace_me with your GitHub username):

GITHUB_USERNAME=replace_me

(remember you can double-check whether the variable is properly set by running echo $GITHUB_USERNAME)

Then navigate to your Short Exercises repository and pull the new material:

cd ~/capp30121
cd short-exercises-$GITHUB_USERNAME
git pull upstream main

You will find the files you need in the se4 directory.

IMPORTANT: If you are unable to obtain the instructor files by running the commands above do not try to add the files in some other way. Doing so will likely prevent you from submitting your code. Instead, please seek assistance on Ed Discussion or at office hours.

Testing

As usual, you will want to test your solution manually before you try the automated tests. (See below for examples.) Remember to set up auto reload before you start testing.

$ ipython3

In [1]: %load_ext autoreload

In [2]: %autoreload 2

In [3]: import se4

A Game

Tic-tac-toe (also known as “noughts and crosses” or “Xs and Os”) is a game for two players who take turns marking cells in a 3 by 3 grid that is initially empty. One player marks cells with an “X” and the other marks cells with an “O”. The first player who places three marks in a row horiztonally, vertially, or diagonally is the winner. You can read more about tic-tac-toe here or play a few games against Google here.

In this set of short exercises, you will complete the implementation of a two player terminal-based tic-tac-toe game. You are going to implement several methods for managing the board (that is, the grid). We have provided a Player class which encapsulates information about a tic-tac-toe player and a Game class which runs the game.

Player class

The Player class encapsulates information about a tic-tac-toe player and has the the following attributes.

  • name (string): the name of the player

  • symbol (string): the player’s one character symbol

In [4]: p1 = se4.Player("Hannah", "X")

In [5]: p1.name
Out[5]: 'Hannah'

In [6]: p1.symbol
Out[6]: 'X'

In [7]: p2 = se4.Player("Guido", "O")

In [8]: p2.name
Out[8]: 'Guido'

In [9]: p2.symbol
Out[9]: 'O'

Game class

The Game class starts the game, repeately asks players for their next move, and plays each move in turn. A demonstration of the game is shown in the “Playing the Game” section below.

We encourage you to read through the Game class after you have completed the exercises below. It is okay if you don’t understand every detail, but reading the code will help you understand how Game interacts with Player and Board.

Exercises

The Board class encapsulates information about the 3 by 3 tic-tac-toe board and contains methods for interacting with the board. In these exercises, you will implement the Board class.

Important: The Game class and our tests access attributes and call methods of your Board class. It is important that you use the required names for the attributes and methods.

Magic numbers: Near the top of se4.py, you will see the global constant SIZE = 3. You should use this constant in your implementation to avoid the use of the “magic number” 3 throughout your code. We’ve used it in Game, too.

Exercise 1

Implement the following attribute in the Board class:

  • board (list of list of string): the 3 by 3 tic-tac-toe board, with all locations initially set to empty ("E").

In [10]: b1 = se4.Board()

In [11]: len(b1.board) # number of rows
Out[11]: 3

In [12]: len(b1.board[0]) # number of columns
Out[12]: 3

In [14]: b1.board
Out[14]: [['E', 'E', 'E'], ['E', 'E', 'E'], ['E', 'E', 'E']]

Exercise 2

Implement the __str__ method. This method should return a string representation of the board.

A row should be represented by a string that contains the symbol from each element in the row separated by space. For example, a row of all "E" would be represented with: "E E E". The string method join will be very useful for this task.

The rows in turn should be terminated by newline characters "\n". The initial board would yield the string: "E E E\nE E E\nE E E\n".

Printing this string yields:

In [15]: print(b1)
E E E
E E E
E E E

Exercise 3

Implement the method valid_move, which takes a tuple of two integers (a row number and a column number) and returns True if the move is valid, and False otherwise. A move to any unoccupied cell on the board is valid.

In [20]: print(b1)
E E E
E X O
E E E


In [21]: b1.valid_move((1, 1)) # location of "X"
Out[21]: False

In [22]: b1.valid_move((1, 2)) # location of "O"
Out[22]: False

In [23]: b1.valid_move((1, 3)) # not on the board
Out[23]: False

In [24]: b1.valid_move((-1, 0)) # not on the board
Out[24]: False

In [25]: b1.valid_move((0, 0))
Out[25]: True

In [26]: b1.valid_move((0, 1))
Out[26]: True

Exercise 4

Implement the method play_move, which takes the following parameters:

  • location (tuple): the location of the player’s move as the tuple (row, column)

  • player (Player): the player who is moving

play_move checks to make sure that the player’s move is valid and, if it is, updates the board at the indicated location with the player’s symbol and returns True. If the player’s move is not valid, this method should return False. An invalid move should not modify the board.

In [27]: print(b1)
E E E
E X O
E E E


In [29]: b1.play_move((2, 2), p1)
Out[29]: True

In [30]: print(b1)
E E E
E X O
E E X


In [31]: b1.play_move((0, 0), p2)
Out[31]: True

In [32]: print(b1)
O E E
E X O
E E X


In [33]: b1.play_move((0, 0), p1) # invalid move
Out[33]: False

In [34]: print(b1)
O E E
E X O
E E X

Exercise 5

Implement the method winner, which takes a Player object as a parameter and returns True if that player is a winner, and False otherwise.

We will play a simplified version of tick-tac-toe, where a player wins if they place three of their symbols in a row horizontally or vertically. In the full game, a player also wins if they get three-in-a-row along either diagonal. You are not required to implement this, but you’re welcome to.

In [46]: p1.symbol
Out[46]: 'X'

In [47]: p2.symbol
Out[47]: 'O'

In [48]: print(b2)
X O O
E X O
X E O


In [50]: b2.winner(p1)
Out[50]: False

In [51]: b2.winner(p2)
Out[51]: True

In [62]: print(b3)
E X O
X X X
O O O


In [63]: b3.winner(p1)
Out[63]: True

In [64]: b3.winner(p2)
Out[64]: True

In [72]: print(b4)
X O E
O X O
E X E


In [73]: b4.winner(p1)
Out[73]: False

In [74]: b4.winner(p2)
Out[74]: False

Automated Tests

As usual, we have included automated tests for each exercises. Please see the Testing Your Code page on the course website for more information about automated testing.

To test Exercises 1-5 invidually, use the commands:

py.test -xvk constructor

py.test -xvk str

py.test -xvk valid

py.test -xvk play

py.test -xvk winner

Playing the Game

We have included code to allow you to play against another person. To start the game, run:

python3 se4.py

from the command line. The first few turns might look like this:

$ python3 se4.py

Ready to play tic-tac-toe?

Player 1: Enter you name and pick your symbol: Hannah X
Player 2: Enter you name and pick your symbol: Guido O

Enter your move Hannah: 1 2
E E E
E E X
E E E

Enter your move Guido: 2 0
E E E
E E X
O E E

Note

Wondering how we’re able to run a game by running python3 se4.py? Confused about the line if __name__ == "__main__": in our code? Now might be a good moment to read the textbook chapter on the Basics of Code Organization, which explains the difference between importing a Python file and running a Python file.

Submitting your work

Once you’ve completed the exercises, you must submit your work through Gradescope (linked from our Canvas site). Gradescope will fetch your files directly from your GitHub repository, so it is important that you remember to commit and push your work!

To submit your work, go to the “Gradescope” section on our Canvas site. Then, click on “Short Exercises #4”. Then, under “Repository”, make sure to select your uchicago-CAPP30121-aut-2022/short-exercises-$GITHUB_USERNAME.git repository. Under “Branch”, just select “main”.

Finally, click on “Upload”. An autograder will run, and will report back a score. Please note that this autograder runs the exact same tests (and the exact same grading script) described in Testing Your Code. If there is a discrepancy between the tests when you run them on your computer, and when you submit your code to Gradescope, please let us know.

Your ESNU score on this set of exercises will be determined solely on the basis of these automated tests:

Grade

Percent tests passed

Exemplary

at least 95%

Satisfactory

at least 80%

Needs Improvement

at least 60%

Ungradable

less than 60%

If there is a discrepancy between the tests when you run them on your computer, and when you submit your code to Gradescope, please let us know. Please remember that you can submit as many times as you want before the deadline. We will only look at your last submission, and the number of submissions you make has no bearing on your score.