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
Gameclass and a
RandomPlayerclass. The game should be initialized via
game = Game(player1, player2), where
player2are both instances of
Gameshould have an attribute
game.board, and there should be a method
player.choose_move(self, board)in the
Playerclass 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
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) game.run()
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:
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) game.run()
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:
- 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.
- Checking whether a player has won is more complicated.