Tic-Tac-Toe and Connect Four

One of the best ways to get practice with object-oriented programming is implementing games. Tic-tac-toe and connect four are great for learning the ropes. Later on, we will also use these implementations for developing AI players.

Exercise: Tic-Tac-Toe with Random Players

Develop a tic-tac-toe game in which two random players play against each other. You can implement your game however you want, provided that you adhere to the constraints below.

  • There should be a Game class and a RandomPlayer class. The game should be initialized via game = Game(player1, player2), where player1 and player2 are both instances of RandomPlayer and player1 moves first.
  • The Game should have an attribute game.board, and there should be a method player.choose_move(self, board) in the Player class takes a copy of the tic-tac-toe board as input and returns a random (but legal) move as output.
  • Players should NOT actually update the board themselves -- otherwise, they chould cheat by changing the board in any way they please. Rather, the game should ask the player what move it chooses, and then the game should update its own board (provided that it's a legal move). If a player attempts to make an illegal move, then the game should skip that player's turn.
  • You should be able to run an entire game via game.run(log). If log == true, then the game should print out the sequence of board states and player moves (as well as whether or not the move was legal). Be sure to implement logging as soon as you start coding up the game, because printing out logs will save you a lot of time debugging.

Exercise: Tic-Tac-Toe with Manual Player

Then, create a ManualPlayer class that allows you to play manually via the command line. You can use Python’s built-in input() function for this.

player1 = RandomPlayer()
player2 = ManualPlayer()

game = Game(player1, player2)

Be sure to test your game by manually playing a handful of games against the random player. (Don’t try to win every game – you’ll need to tie and lose some games for testing purposes.)

Exercise: Strategy Functions

Currently, you have two types of tic-tac-toe players: RandomPlayer and ManualPlayer. The only difference between these players is in how they choose moves. The rest of the code is duplicated, which is not ideal. There should really be just one Player class, where the choose_move method is automatically adjusted as desired.

To make your code cleaner, implement a single Player class that is initialized via player = Player(strategy_function) where strategy_function(board) is a function that takes a copy of the tic-tac-toe board as input and returns a random move as output. Then player.choose_move(self, board) will simply call the strategy_function on the board and return the result.

player1 = Player(random_strategy_function)
player2 = Player(manual_strategy_function)

game = Game(player1, player2)

Once you’ve implemented this, test your game again by manually playing a handful of games against the random player. Additionally, make sure that you are always able to beat the “cheater” strategy shown below. (If the cheater strategy wins, then it probably means that you’re allowing the player to access the actual game board instead of giving it a copy of the board.)

def cheater_strategy_function(board):

    # put my own pieces everywhere on the board
    for i in range(3):
        for j in range(3):
            board[i][j] = my own piece

    # doesn't really matter what we return;
    # we'll arbitrarily move into top-left corner
    return (0,0)

Exercise: Custom Strategy

Create a custom strategy that beats the random player most of the time.

Exercise: Connect Four

Repeat the above exercises for the game of connect four. There are really only two differences:

  1. A player chooses a column to place their piece into, rather than an actual board space. So, a move will be a single integer rather than a tuple.
  2. Checking whether a player has won is more complicated.

Leave a Comment