cs205: engineering software? |
(none) 25 June 2008 |
Problem Set 1 Game of Life |
Out: 23 August Due: Monday, 28 August (beginning of class) |
Collaboration Policy - Read Carefully
For this problem set, you should work alone, but feel free to ask other students for help and offer help to other students.Purpose
Despite their simplicity, cellular automata can produce complex and interesting patterns. We have provided you with code that implements a cellular automata simulator. You are encouraged to look over the code, but you do not need to understand this code now and should not be concerned if it doesn't all make sense yet as it uses concepts that we will not encounter until later in the course.
The cellular automata simulator takes a Java class that defines the rules for each step, and simulates a grid of cells. Each cell can be in one of two states: dead or alive. A dead cell is shown as a white square and a live cell is shown as a green square. Each cell has neighbors which are the eight cells that surround it.
Although this is a fairly small program, there are many different ways it might be done. The provided implementation uses many programming techniques and Java features that we will not cover until later in the class including subtyping, parameterized types, interfaces, inner classes, exceptions, event handlers, and threads. It is not necessary (or expected) for you to understand all of the provided code for this assignment. This document will explain the most important parts of the code you need to understand for this assignment. You are encouraged to be curious about the rest of the code, and feel free to explore it and make changes to it, and think about better ways to implement this program.
Eclipse automatically compiles Java projects when they are imported. Also, whenever you make a change to a Java source file and save it, the changes are automatically compiled. If you want to force Eclipse to rebuild the entire project, select Project | Clean.
Try running the cellular automata simulator using the provided ExtremeLifeCell class to model each cell. To do so:
Eclipse will run the Java virtual machine, which starts by calling the main method of the CellAutomata class. The command line arguments (ExtremeLifeCell) are passed as a parameter to the main method, which loads the ExtremeLifeCell class and initializes the grid to contain an ExtremeLifeCell object in each square.
When the simulator starts, click on a cell and wait for it to turn green. Cells that are alive appear green; non-alive cells are displayed in white. Then press start and observe what happens. You can also use Step to run the simulation one step at a time. Try to figure out what rules the ExtremeLifeCell is following just by observing its behavior. (You'll see the code later.)
CellAutomata.java
This is the main class for the implementation of the cellular automata. It sets up the user interface using Swing. Swing provides classes for displaying windows and handling input events.The CellAutomata.main method initializes a grid of cells, each containing an object instance of the class passed in as the first parameter to main. Then, it displays the grid. The actionPerformed method makes the appropriate things happen when the user clicks on the Start, Stop, and Step buttons.
To load a specific cell class, you specify the cell class as a program argument when running the Java application. To test the class your create, replace ExtremeLifeCell with the name of your cell class. (Note that the .class extension should not be included.)
CellGrid.java — This class keeps track of all the states of the cells on the board. The most important method of the CellGrid class is step which excutes one step in the simulation:
CellState.java — implements a datatype for recording the state of a cell:public void step() { // Because we need to update all cells synchronously, we // first calculate the next state for each cell, and store it // in a temporary array: CellState[][] nextStates = new CellState[x_max + 1][y_max + 1]; for (int x = 0; x <= x_max; x++) { for (int y = 0; y <= y_max; y++) { Cell cell = (Cell) getObjectAt(x, y); nextStates[x][y] = cell.getNextState(); } } // Then, we update all the cells: for (int x = 0; x <= x_max; x++) { for (int y = 0; y <= y_max; y++) { Cell cell = (Cell) getObjectAt(x, y); cell.setState(nextStates[x][y]); } } }
The methods with static modifiers are class methods, used to create new states. They are not invoked on an object (there is no this). Instead, invoke them using CellState.createAlive() and CellState.createDead(). Cell.java — This class provides a basic Cell implementation. It includes the methods specified below:import java.awt.Color; public class CellState { // OVERVIEW: A CellState is an immutable object that represents // the state of a cell, either alive or dead. private boolean alive; private CellState(boolean isalive) // EFFECTS: Initializes this to alive if isalive is true, // otherwise initializes this to the dead state. { this.alive = isalive; } static public/* nonnull */CellState createAlive() // EFFECTS: Returns an alive cell state. { return new CellState(true); } static public/* nonnull */CellState createDead() // EFFECTS: Returns a dead cell state. { return new CellState(false); } public Color getColor() // EFFECTS: Returns the display color for this state { if (alive) return Color.green; else return Color.white; } public boolean isAlive() // EFFECTS: Returns true iff this is alive. { return alive; } }
public class Cell extends SimObject { private/* nonnull */CellState state; public Cell() // EFFECTS: Initializes this to a dead cell. public/* nonnull */java.awt.Color getColor() // EFFECTS: Returns the display color for this. public boolean setState(/* nonnull */CellState s) // MODIFIES: this // EFFECTS: Sets the cell's current state to s. Returns true iff the new // state is different from the current state. public/* nonnull */CellState getState() // EFFECTS: Returns the cell's current state. boolean isAlive() // EFFECTS: Returns true if the cell is alive, false otherwise. public/* nonnull */CellState getNextState() // REQUIRES: init has previously been called for this. // EFFECTS: Returns next state value for this. // Note: For the underlying Cell class, the next state is always the // current state. Override this method to implement // different cell automata rules. }
The getNextState method replaces the Cell.getNextState method. The while loop goes through the cell's neighbors (returned by the call to getNeighbors() (which is defined in SimObject, and inherited through the Cell class). If a neighbor is found that is in the alive state (cell.isAlive()), it returns immediately with the result an alive state. If the loop finishes without finding an alive neighbor, the cell maintains its current state.public class ExtremeLifeCell extends Cell { public CellState getNextState() // EFFECTS: Returns the next state for this cell. // The next state will be alive if this cell or any of its neighbors // is currently alive. { Enumerationneighbors = getNeighbors(); while (neighbors.hasMoreElements()) { SimObject neighbor = neighbors.nextElement(); if (neighbor instanceof Cell) { Cell cell = (Cell) neighbor; if (cell.isAlive()) { // If the cell has at least one neighboring cell // that is alive, this cell should become alive. return CellState.createAlive(); } } } // No alive neighbor found, next state is current state return getState(); } }
With these simple rules, it is possible to produce many interesting patters. For example, if you start with three cells in a row it should produce a blinker that alternates between three horizontal and three vertical live cells. Experiment with different initial conditions to see what happens.