Skip to content

Tic-Tac-Toe

Let’s Play a Game

Small games like Tic-Tac-Toe have a very easy ruleset and you can implement them in a Python program with what you already know. Since this is a rather long task, instead of letting you figure out all the details by yourself, you will be given some guidance.

Preparations

Start by creating a separate folder tic_tac_toe for your project.

We will need the file constants.py inside to note down some constants to make our program a bit more readable. Further, a __main__.py will hold the central part of our program.

Some Things never Change

In the constants.py you will have to define the following:

  • Assign some symbols to PLAYER_X and PLAYER_O respectively
    • These are used to identify the players on the board later, so using letters makes sense here
    • Please use the exact names for these constants, some other code later depends on it.

Bookkeeping

During the game you will need to keep track of some information. In __main__.py, create the appropriate variables for the following data. Consider which data types and initial values they might have.

  • The board_state which keeps track which fields are already taken and by whom
    • Since the board is made up of nine fields in a 2-D arrangement (i.e. rows and columns) it can not be represented by a single value
      • Extra challenge: or can it?
    • You will have to carefully which data type to use to represent the board.
      • The data type must be able to hold multiple elements
      • You must be allowed to change these elements, so you can set a player to a field
    • Alternatively, instead of remembering the whole board, you could remember for each player which fields they occupy (if any).
    • On this decision hinges a lot of how your code works in the end, so feel free to discuss your ideas with other people.
  • The current_player who is about to make a move.
  • The winner, as soon as there is one

Little Helpers

You will need to repeatedly do certain things, so it might be a good idea to write some functions for those. If any of those functions becomes to unwieldy consider to split it into smaller pieces or move it to its own file. Don’t forget to add some documantation,this can also help with getting to grips with the problem at hand.

You can try out each of the functions in isolation to test if they do what they are supposed to.

reset_board

Create an empty 3×3 board.

    Returns:
        The state of an empty board

is_occupied

Checks if a cell is occuoied by a player.

    Providing incorrect values for any of the arguments may have unexpected results.

    Args:
        board_state: The board state which to check.
        row: The row of the cell to be checked. Must be in the interval (0…2).
        column: The column of the cell to be checked. Must be in the interval (0…2).
    Returns:
        `True` if a player occupies the field, `False` otherwise.

set_player

Set a cell of a board to be occupied by a player.

    This does not check if the cell is already occupied.
    Providing incorrect values for any of the arguments may have unexpected results.

    Args:
        board_state: The board state before the change.
        row: The row of the cell to be set. Must be in the interval (0…2).
        column: The column of the cell to be set. Must be in the interval (0…2).
        player: The player which occupies the cell, either `PLAYER_X`, `PLAYER_O` or `None`.
    Returns:
        The board state after the change was made.

get_player

Get the player who occupies a given cell.

    Providing incorrect values for any of the arguments may have unexpected results.

    Args:
        board_state: The board state from which to extract the player.
        row: The row of the cell to be gotten. Must be in the interval (0…2).
        column: The column of the cell to be gotten. Must be in the interval (0…2).
    Returns:
        The player which occupies the cell, either `PLAYER_X`, `PLAYER_O` or `None`.
Print the current state of the board.

    Args:
        board_state: The current state of the board

Notes

The printed output could look something like this:

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

Note that you already have some utility functions to extract the current player at a given position.

next_player

Switch from one player to the next.

    Also print some nice text to inform the players who is next.

    Args:
        current_player: The player that is currently playing

    Returns:
        The next player in order

Notes

Note that you aleady have some constants for each of the players, make good use of them!

input_row

Ask the current player to select a row which can be used to specify the cell where the player wants to place their mark.

    Make sure that the input is an integer in the intervals (0…2).

    Returns:
        The row selected by the player as integer.

input_column

Ask the current player to select a column which can be used to specify the cell where the player wants to place their mark.

    Make sure that the input is an integer in the intervals (0…2).

    Returns:
        The column selected by the player as integer.

check_for_victory

Check the board if one of the following conditions are fulfilled.

    The possible scenarios are:
    * One player has occupied a complete row, column or diagonal
        * That player would then be the winner 
    * All fields are occupied
        * No more moves are possible, resulting in a draw

    Args:
        board_state: The current state of the game board

    Returns:
        Either the winning player, `DRAW` if no more move is possible or None if the game is not over yet.

Notes

Add a constant to represent a DRAW, since None would be used to represent that the game is not yet over

Tying it all Together

With all those building pieces, you can now implement a whole game in the __main__.py. Here is a suggested program flow to give you some inspiration. (Of course you may chose your own approach if you desire to do so.)

Kroki

Rematch

After some rounds, your fellow players have come up with some additional suggestions:

  • Run the game in a loop so it automatically starts a new game once the old one is over
  • Keep a score of
    • How many games have been played
    • How often each player won
  • Print the wins/total games ratio in percent for each player

Closing Remarks

Simple games like theese are a good way to train programming. The rules are not overburdening and well-known so you can go directly to problem-solving. Breaking these problems down into step-by-step solutions and considering how to represent the game situations in a program is a very good exercise for training to think in the more strict ruleset that programming imposes.

It is strongly recommended to revisit this exercise once you have learned more. This first implementation is still very much guided and does not take advantage of all the possible features Python offers to you. Once you get to know more language features you will likely find new ways to represent the board and structure the program flow.

Extra challenge: There is a variation that is played by three players (X, O and T (often called triangle)), which is played on a 4×4 board. Can you implement that as well? Can you implement an even more generic variant for n players, that is played on an n+1×n+1 board?