Robots and polymorphism

In this lab you will change a completed Robots game to include polymorphism.

Play the game once and view the code

To obtain directory robots containing source code to your current directory, type
  cp -r ~mc38/labs/robots-polymorph/robots/ robots
Compile and play the game once to observe its behavior.

Next, familiarize yourself with the code. First, skim the README file. I recommend you look at the Unit class, the GameState class and the Game class.

Add polymorphism

Your goal is to incorporate polymorphism into the Robots program. There are several places where polymorphism is appropriate in this program. In order to help you incorporate polymorphism into the Robots program, we give you the following step-by-step approach:

  1. Make draw(), moveTowards() and attacks() virtual in the Unit class. Update subclasses as appropriate. For this part of the lab, the Unit class need not be pure-virtual. Just leave the most common case for these procedures here. Subclasses will only need to override these definitions if they need to. You'll need to add a procedure draw() to Unit.cc which does nothing (or, if you prefer, flags an error). Recompile and run.

  2. Make Junk a subclass of Robot. Override methods moveTowards() and attacks() to behave appropriately for Junk. (You'll need to add a constructor to the Robot class which is called by Junk's constructor.) Recompile and run.

  3. Add a virtual function isJunk() to the Robot class. The function should return bool. Update subclass Junk appropriately. Recompile.

  4. Quite a few changes are required to the GameState class so that only one vector of vector<Robot *> is maintained. (There won't be a vector of Junk.) Make the appropriate changes. Most changes are simple. More significant changes to the logic are required in the constructor and in countCollisions(). The latter procedure can be vastly simplified to make no reference to junkAt(). In fact, you should be able to remove the procedure junkAt() from the GameState class.

  5. Review GameState procedure by procedure once more to be sure all required changes were made. Then, try compiling and debugging.

  6. Unfortunately, your robots program now has a memory leak. There are lots of robots that get created, but never get destroyed. If a player would do really really well in the game, and play for hours, eventually the program would run out of space, bringing it to a grinding halt.

    Add a static variable count to Unit.cc by placing the following line at the top of Unit.cc

      static int count = 0;
    This variable will have file scope.

    Edit Unit.cc so that every call to any of the three Unit::Unit(...) constructors should increment count. Make a virtual destructor Unit::~Unit() whose only purpose is to decrement count. (The destructor needs to be virtual since the class is virtual.)

    Recompile and run your program in gdb. After playing two levels, hit C-c C-c in Emacs's gdb buffer, set a breakpoint in one of the Unit classes, continue, and (lastly) inspect the variable. Is the value reasonable? If it keeps growing as you go from level to level, your have a memory leak.

  7. In both the GameState(int numberOfRobots() constructor and the CountCollisions() method you should be able to place a call to delete to return some old robots to the heap. Do so.

    This won't solve the memory leak mentioned above, but it will reduce it. To completely solve it, you'd need to write an appropriate copy constructors, destructor and operator= for the class.

  8. Check-off Be sure to get checked off for this lab.

``Optional'' lab assignments

Listed below are three optional assignments, two of which introduce advanced C++ concepts. Do any or all of the three. They need not be done in order.

You should not treat this as an opportunity to take the afternoon off. If you're done with the day's lab, try to learn something new!

Destructors and copy constructors (optional)

  1. Review the Advanced Topic on pages 583-585 of the text. Feel free to ask us questions about their code. Implement a copy constructor, destructor and overloaded operator= for the GameState class.

  2. Test to be sure there is no memory leak.

Pure virtual classes (optional)

If you have yet to get checked off for the last part, be sure to save a backup copy of your working code.

A pure virtual class declares virtual functions but does not define them. The virtual function declaration needs to include the bizarre incantation = 0 as follows:

  virtual void draw() = 0;	// Declaration in Unit.h
This says, ``I have no intention an object of class Unit, only objects of subclasses of class Unit. Subclasses must define the procedure draw().'' You can still accept Unit's as parameters by reference or by pointer.

  1. Make the Unit class pure virtual by making any changes you feel appropriate. You may need to add a new class, since currently the click is a Unit. Note that Unit's can only be passed as arguments through reference parameters (or pointers), since a Unit object can't exist on it's own; only subclasses of the Unit's class exist.

  2. Clean up the code a bit. Some functionality in the Unit class really belongs in the Robot class. Move those methods which only Robot's use to the Robot class. (Remember that the hero uses the moveTowards method.)

Changing the game (optional)

With virtual classes, it's quite easy to enhance the game by adding, say, a few robots with strange behavior. Do so.