CS35 Lab 3: Game Engine

Due 11:59pm Tuesday, February 11, 2014

The goal of this lab is to introduce you to basic concepts in the C++ program language. Concepts you will be familiar with after this lab include:

A skeleton version the program will appear in your cs35/labs/03 directory when you run update35. The program handin35 will only submit files in this directory. Partners will again be assigned for this lab. You will be working with a new partner for this lab. You can find your new partner in this file. Both partners should be present and working on the code together. You will both be responsible for understanding all concepts, so dividing and conquering is not an option. The academic integrity policy applies to the entire pair; you cannot work or share code with anyone outside of your partner. Also, be sure to indicate who your partner is by including both names in the header and by also selecting the 'p' option when using handin35. Only one student in the pair should submit the lab.

See this page for tips on sharing files with your lab partner.


Introduction

In this lab, you will create classes representing different games, including Tic Tac Toe and a game called Pig (see below for rules of Pig). Both of these classes will be subclasses of an abstract Game class. This Game class will encapsulate all information and functionality that is common to all games. Your subclasses will add additional data and methods particular to each game, as well as implement inherited method from the Game class.

In addition, you will create classes to represent different players for the Pig game. One player class HumanPigPlayer will provide functionality for a human player to play Pig. You will implement another class for a computer AI player. These player classes will both be subclasses of a base class PigPlayer. Note: as part of the grading process, your computer pig player classes will compete against each other. The team implementing the best AI player will receive one extra credit point on this lab.
Below is an overview of the files required for submitting this week's lab. Those highlighted in blue will require implementation on your part. Those in black are complete and should not be modified except for comments.


Program Requirements

I recommend implementing your program in 5 phases:

  1. Game and TicTacToe classes -- be able to play a game of TicTacToe.
  2. Pig class -- be able to play a game of Pig using given solution objects for PigPlayer and humanPigPlayer (see section 2 below).
  3. PigPlayer, and HumanPigPlayer classes -- be able to play a game of Pig using your own implementation of PigPlayer and HumanPigPlayer.
  4. Computer PigPlayer. Design your own PigPlayersubclass that uses artificial intelligence to make decisions.
  5. Final program. In main.cpp, you should create a program that lets user(s) play the games you created.
Your program should use proper design, and you should use defensive programming as you develop your code. Your program will be tested on your use of modular functions and understanding of how to define C++ classes and use inheritance. In addition, you should implement small test programs in between each phase.


I. Game and TicTacToe classes

Your program will need to handle a couple of different types of games. To get started, take a look at the Game and TicTacToe classes.

The Game class has been implemented for you. But you must start but understanding this class. The Game class encapsulates all information and methods common to all game types. In particular, it contains:

I have provided skeleton code for both game.h and game.cpp.

---------------------------
Key Concepts: virtual functions, pure virtual functions, abstract classes.
The play(), isGameOver(), and printGameState() are all what are called pure virtual functions -- there are no implementations for these functions in the Game class. Instead, each subclass must implement the function(s) themselves. In a class declaration, pure virtual functions are declared as folows:
      virtual bool isGameOver() = 0;
    
The virtual keyword makes isGameOver a virtual function. This allows subclasses to provide their own method definition. By adding = 0 the method becomes a pure virtual funciton. This means the subclasses must provide their own imlpementation of this method. A class that declares a pure virtual funciton is called an abstract class --- it forms the template for subclasses you want to define later. In this way, it is a contract: anything that inherits the Game class must complete unique play, isGameOver, and printGameState methods. And any user who wants to create Game objects must chose a specific subclass (e.g., TicTacToe, Pig, etc.). Use pointers to create these objects as follows.
      Game *mygame = new TicTacToe();
    
This creates a pointer to a Game object called mygame and makes it a new TicTacToe game. From the user's perspective, mygame is a Game pointer, even though "under the hood" it is a TicTacToe game.
---------------------------


The TicTacToe class should enable two people to play TicTacToe. It should specialize (i.e., inherit) Game and implement any information and methods particular to Tic Tac Toe. When you are finished imlementing this class, you and your partner should be able to play Tic Tac Toe using an object of this class. Much of the Tic Tac Toe implementation has been provided for you. In particular, I have provided the following data members: This gives you the square indexed by the ith row and jth column. Each entry contiains an int, which equals 0 if the square has not been selected, 1 if the square has been selected by the first player, and 2 if the square has been selected by the second player. In addition to the virtual functions from the Game class that must be implemented, there are three private functions: makeMove, getWinner, and checkLine: These are helper functions that get called from within other TicTacToe methods. They're private because they shouldn't be called from outside the class definition.
To complete the TicTacToe implementation, you must do the following: Before moving onto the second phase of this lab, modify testGame.cpp to test your class. This program should create a TicTacToe object and play a game of TicTacToe. You should test, at a minimum, all methods whether inherited or not, before moving forward. You should also test to ensure that the game ends correctly, in a win for the X player, for the Y player, or when there is a tie.

To compile incrementally, first compile the TicTacToe class by itself to make sure you have no errors:

$ g++ -c TicTacToe.cpp
We have provided a Makefile that automates the process. To compile one of the classes, simple follow this example:
$ make tictactoe.o
This executes the command given above. When you are ready to use testGame, run:
$ make testGame

II. The Pig class

Once you have successfully created a TicTacToe object and played a game or two of TicTacToe, you can begin creating the Pig game. Pig is a die-rolling game played between two or more players.

In one players turn, he or she rolls a six-sided die. If a six is rolled, the player immediately loses their turn. If the player does not roll a six, they accumulate a number of points equal to the die roll. The player then has two choices: they can either lock in these points but give up their turn, or they can roll again. When they roll again, they are presented with the same option. Note:If a player rolls a six, all accumulated points are lost. Players only gain points when they choose to end their turn and lock in the points accumulated during that turn. Play continues until one player gains a certain number of points. The normal game lasts until 101 points, but you should allow a user of your class to specify the points needed. Also, it will be a good idea to test this with a lower ending score, like 11 or 21, until you know you have a correct Pig game implementation.

In this section, you should complete and test an implementation of the Pig class. Note: the Pig and PigPlayer classes are coupled. (I will explain more below). To help you get started, I have provided you with object files pigPlayersoln.o and humanPigPlayersoln.o. These provide compiled implementations of the PigPlayer and HumanPigPlayer classes, which you can use to develop your Pig class. Once you have compiled and tested your Pig class, you can implement the PigPlayer and HumanPigPlayer classes.

Pig vs. PigPlayer. The Pig and PigPlayer classes work closely together. To minimize confusion, it might be helpful to describe how they should interact. Pig is the Game class that manages a single Pig game. This class has pointers to two PigPlayer objects. Each of these objects encapsulates a single Pig game player. When you play the Pig game (by calling the play() method), you will have each player take turns, by calling a takeTurn method on the player. This passes control to your PigPlayer object. Inside this method, the player should roll the dice and make choices associated with taking their turn. All game state should be stored in the Pig object. While a player is taking a turn, they will change the game state (by, for example, rolling the die and accumulating points). To enable that, you should pass in a pointer to your Pig game object, as follows:

  player1->takeTurn(this);
this calls the takeTurn method of the PigPlayer class, passing in the address of the Pig object. The PigPlayer should update game state by calling one of the public methods of Pig that you'll implement. Details of the data and methods required for the Pig class are below.
To recap, the only place the Pig class calls methods on a PigPlayer object is during the play() method, when it calls e.g. player1->takeTurn(...). It also might use PigPlayer::getName() when printing out whose turn it is, or who won the game. A PigPlayer calls public methods in Pig when they want to access or change game state.

The Pig class should specialize Game and implement additional data and members to implement the Pig game. In particular, you should provide the following data:

You should also provide the following methods:

Testing your Pig implementation. There is a lot of data and several methods in the Pig class. Before implementing your PigPlayer classes, test your pig implementation to make sure it works. To help you, I have provided pigPlayersoln.o and humanPigPlayersoln.o These are compiled implementations of the PigPlayer and HumanPigPlayer classes. In the next section, you'll provide your own implementations of these classes, but for now, use these objects.

Test your Pig implementation in testPig.cpp. Here, you can call public methods on the Pig class one-by-one and verify they do what they're supposed to do. To compile this file using the given PigPlayer objects, run

  make testJustPig
To compile your testPig.cpp program using your own PigPlayer objects, use
  make testPig

III. PigPlayer, HumanPigPlayer classes

In this section, you should complete implementations of the Pig, PigPlayer, and HumanPigPlayer classes.

The PigPlayer class. This is an abstract class representing Pig game players. It contains the following.

Your only task for the PigPlayer class is to finish implementing the takeTurn method. Note:This class uses random number generation, which is provided in the cstdlib library. The rand() function gives you a random number. I have provided the code for this.


Implement a HumanPigPlayer class. This should specialize PigPlayer, and has no additional data members. To implement the HumanPigPlayer class, implement the following:


IV. Computer PigPlayer class

Finally, you should implement a computer PigPlayer class. Like the HumanPigPlayer class, this class should specialize PigPlayer, implement a basic constructor, and implement the makeChoice method. However, rather than asking the user to make the choice, your class will make decisions automatically. For example, you could always choose to lock in points on the first roll, or you could always choose to stop rolling only when you've accumulated enough points to win.
Note: As part of the grading process, we will pit your computer PigPlayer classes against eachother. The group with the most victories will receive one extra credit point on this lab.

Put your comptuer PigPlayer class in a file fooPigPlayer.h/cpp, where foo is the user name of one of your partners. Your class should be called FooPigPlayer. To compile this class, make sure to


V. The Main program

Once you've completed all the components of this lab, create a gameEngine.cpp file which brings everything together. In your main program, you should create a list of games (including different players/settings for the Pig game. Then you should ask the user which one they want to play, and let the user play them. Much of this file has been provided for you. When you are ready to compile this program, uncomment out main in line 9 of the Makefile.
Check List and Sample Output

To recap, you are required to to the following:

I have provided executables for simple test versions of Tic Tac Toe and for the Pig game. To play Tic Tac Toe, run the following command:

$ ~brody/public/cs35/gameEngine/testTTT
To play the Pig game, run the following command:
$ ~brody/public/cs35/gameEngine/testPig
testPig asks the user whether each player is a human or a computer player. It also asks how many points are needed to win the game. Then, it plays the game. Note: this test class hard-codes names into the players. In your final program, you should prompt the user for names before you start the game.
Question: See if you can determine the strategy used by the AI player.


Submit

Once you are satisfied with your code, hand it in by typing handin35. This will copy the code from your cs35/labs/03 to my grading directory. You may run handin35 as many times as you like, and only the most recent submission will be recorded. Please record your partner one time using the 'p' option.