Short Exercises #4

Due: Sunday, Nov 8 at 3pm CST

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.

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. 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 the following exercises, you will complete the implementation of a two player terminal-based tic-tac-toe game. We have provided a Player class which encapsulates information about a tic-tac-toe player and a Game class which runs the game. You will implement a Board class, which Game utilizes.

Our game runs from the command line. The first two turns will look like this:

$ python3 se4.py

 Ready to play tic-tac-toe?

 Player 1: Enter your name and pick your symbol: C-3PO X
 Player 2: Enter your name and pick your symbol: R2-D2 O

 Enter your move C-3PO: 1 2

 | | | |
 --+-+--
 | | |X|
 --+-+--
 | | | |

 Enter your move R2-D2: 2 0

 | | | |
 --+-+--
 | | |X|
 --+-+--
 |O| | |

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 [1]: from se4 import Player

In [2]: p1 = Player("C-3PO", "X")

In [3]: p1.name
Out[3]: 'C-3PO'

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

In [5]: p2 = Player("R2-D2", "O")

In [5]: p2.name
Out[5]: 'R2-D2'

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

The Game class starts the game, repeately asks players for their next move, and plays each move in turn. Please read through the Game class. 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.

We will make the following simplifying assumptions for our game:

  • A player’s name contains no spaces.

  • A player’s symbol is a one character string (“X” or “O”).

  • During a turn, a player will enter two integers in (row then column) to indicate which cell on the board they want to mark.

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. Therefore, it is very important that you follow the attribute names and method parameter order exactly as shown below.

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.

  1. Implement a Board class with the following attribute.

    • board (list of list of string): the 3 by 3 tic-tac-toe board

    We will represent the board as a nested list of strings where each element is a one character string (" ", "X", or "O"). Upon construction, the board should be empty. We will use a blank space (" ") to indicate an empty cell.

    In [7]: from se4 import Board
    
    In [8]: b1 = Board()
    
    In [9]: len(b1.board) # Number of rows
    Out[9]: 3
    
    In [10]: len(b1.board[0]) # Number of columns
    Out[10]: 3
    
    In [11]: b1.board[0][0]
    Out[11]: ' '
    
  2. Implement the __repr__ method. __repr__ should return a useful string representation of the board. The design is up to you, it does not need to match ours.

    In [12]: b1
    Out[12]:
    
    | | | |
    --+-+--
    | | | |
    --+-+--
    | | | |
    
  3. Implement the method valid_move, which takes two integer parameters row and col and returns True if the move is valid, and False otherwise. Recally that during a turn, a player will enter two integers to indicate which cell they want to mark. A move to any unoccupied cell on the board is valid.

    In [18]: b1
    Out[18]:
    
    | | | |
    --+-+--
    | |X|O|
    --+-+--
    | | | |
    
    In [19]: b1.valid_move(1, 1)
    Out[19]: False
    
    In [20]: b1.valid_move(1, 2)
    Out[20]: False
    
    In [21]: b1.valid_move(1, 4)
    Out[21]: False
    
    In [22]: b1.valid_move(-1, 0)
    Out[22]: False
    
    In [23]: b1.valid_move(0, 0)
    Out[23]: True
    
    In [24]: b1.valid_move(0, 1)
    Out[24]: True
    
  4. Implement the method move, which takes the following three parameters.

    • row (int): row

    • col (int): column

    • player (Player): the player who is moving

    move updates the board at the indicated location with the player’s symbol. You can assume that this method is only called when the move is valid.

    In [25]: b1
    Out[25]:
    
    | | | |
    --+-+--
    | |X|O|
    --+-+--
    | | | |
    
    In [26]: b1.move(2, 2, p1)
    
    In [27]: b1
    Out[27]:
    
    | | | |
    --+-+--
    | |X|O|
    --+-+--
    | | |X|
    
    In [28]: b1.move(0, 0, p2)
    
    In [29]: b1
    Out[29]:
    
    |O| | |
    --+-+--
    | |X|O|
    --+-+--
    | | |X|
    
  5. Implement the method winner, which takes a Player object as a parameter and returns True if the player is a winner, and False otherwise. A player wins tic-tac-toe if they place three marks in a row horiztonally, vertially, or diagonally.

    In [32]: p1.symbol
    Out[32]: 'X'
    
    In [33]: p2.symbol
    Out[33]: 'O'
    
    In [34]: b2
    Out[34]:
    
    | |O|X|
    --+-+--
    |O|X| |
    --+-+--
    |X| |O|
    
    In [35]: b2.winner(p1)
    Out[35]: True
    
    In [36]: b2.winner(p2)
    Out[36]: False
    
    In [41]: b3
    Out[41]:
    
    | |X|O|
    --+-+--
    |X|X|X|
    --+-+--
    |O|O|O|
    
    In [42]: b3.winner(p1)
    Out[42]: True
    
    In [43]: b3.winner(p2)
    Out[43]: True
    
    In [46]: b4
    Out[46]:
    
    |X|O|X|
    --+-+--
    |O|X|O|
    --+-+--
    |O|X|O|
    
    In [47]: b4.winner(p1)
    Out[47]: False
    
    In [48]: b4.winner(p2)
    Out[48]: False
    

Testing and Submitting your Solutions

Like the previous short exercises, you will need to pull some instructor files to your repository, and then add your code to one of those files. You can find detailed instructions on how to do this in our Coursework Basics page. For instructions on how to test your code, please see our Testing Your Code.

Once you’ve completed the exercises, you must submit your work through Gradescope (linked from our Canvas site). In the “Short Exercises #4” assignment, simply upload the file se4.py (do not upload any other file!). Please note:

  • You are allowed to make as many submissions as you want before the deadline.

  • There are no extensions for the short exercises. The two free extensions you get for the programming assignments cannot be applied towards the short exercises. Please note that, if you need an extension due to extraordinary circumstances, you should alert us via a private message on Piazza.

  • Your score on the short exercises is determined solely based on the automated tests, but we may adjust your score if you attempt to pass tests by rote (e.g., by writing code that hard-codes the expected output for each possible test input).

  • Gradescope will report the test score it obtains when running your code. If there is a discrepancy between the score you get when running our grader script, and the score reported by Gradescope, please let us know so we can take a look at it.