The Game of Life is easily amenable to object orientation. Each cell can be viewed as its own object, and the whole grid of cells can also be viewed as an object that contains the cell objects. At the outermost level is a static class that creates a grid of cells and then evolves them.
One of the goals of object orientation is to cleanly modularize a solution. Each class should perform a set of well encapsulated functions; there should be as little dependency as possible between different classes. In this case, it is not immediately clear which class should be responsible for carrying out the steps required to evolve a grid of cells from one generation to the next. Which class contains the code to determine liveness in the next generation? Which class should be responsible for traversing the neighbors to test their liveness?
While there is no single answer to this question, some choices will be better than others. In this assignment, you will need to redesign your code for the Game of Life from Project 1 by making it object oriented. Specifically, you must create the following classes:
A Life class that is static and drives the program. It is the highest-level component of your code. It should contain and use a Grid object (see below).
A Grid class that organizes Cell objects (see below) in a two dimensional arrangement.
A Cell class that stores the state and performs the task of a specific cell. This class must be abstract -- it should contain only those elements common to all cells, and define the interface that any subclass must use.
A ConwayCell class that is a subclass of Cell and implements the ``traditional'' cell rules used in the last assignment. Once again, these are:
An AmoebaCell is another subclass of Cell and implements slightly different rules:
A MorphingCell---yet another subclass of Cell---that has unusual rules. Specifically, under certain circumstances, a cell of this type can become another type of cell. Specifically:
Note that becoming another cell type does not imply that the cell merely begins to behave according to the rules of some other cell type. Rather, it implies that the MorphingCell at the given grid location gets replaced with a new ConwayCell or AmoebaCell object.
The main challenge here is to divide the tasks that need to be performed so that the role of each class is as clear as possible. While there clearly will be dependencies between classes, your goal is to minimize them. Here are some (but not necessarily all) of the key questions that you should consider in your design of these classes:
Should the Grid class be a ``dumb'' container of Cell objects, or should it actually perform some meaningful tasks?
Should the rules for evolving a particular cell type be implemented wholly within that cell class, or should any part of it be placed within the Grid or Life classes?
How can a cell get access to its neighbors? Should a cell have access to the Grid object that contains it? Or should it simply be handed the information it needs to perform its evolution?
How does the possibility of a cell that can change its type affect the interface for those methods responsible for evolution?
Implement the classes described above. Note that you must override the toString() method in each instantiable cell class as well as in the Grid class. These toString() methods should do the work of formatting the output as required for the output format. Note that the representation of liveness and deadness for each cell should depend on its type as follows:
A ConwayCell should be presented as a dash (-) if it is dead and with a plus (+) if it is alive.
A AmoebaCell should be presented as an underscore (_) if it is dead and with an asterisk (*) if it is alive.
A MorphingCell should be presented as an equals (=) if it is dead and with an exclamation (!) if it is alive.
The format of the initializer file is unchanged. However, an additional command line argument must be taken by the main() method in the Life class: the initial cell type to be used. The user can thus specify what type of cells should be used to populate the grid, where Conway will cause ConwayCell objects to be used, Amoeba will cause AmoebaCell objects to be used, and Morphing will cause MorphingCell objects to be used.
It is your reponsibility to create new initializing files to ensure that you are testing all of the capabilities of each cell type.
Use the cs12-submit program to submit only your source code, which should have been written as a number of .java files (one per class). Submit your work as project-2, something like this:
cs12-submit project-2 Life.java Grid.java Cell.java ConwayCell.java AmeobaCell.java MorphingCell.java