Update: Just to specify, depending on how I change the rules I can set it so within a couple of generations all cells are either permanently alive or dead. I have checked this by echoing statements to console. HOWEVER, this doesn't reflect in the GUI which shows all cells as always the same color.
I am trying to implement a simple Cellular Automaton to replicate the game of life. This uses the MASON library. My three classes:
Cell.java
package sim.app.gol;
import sim.engine.SimState;
import sim.engine.Steppable;
import sim.field.grid.IntGrid2D;
import sim.util.IntBag;
public class Cell implements Steppable {
public IntGrid2D grid = new IntGrid2D(0,0);
public void step(SimState state) {
Matrix matrix = (Matrix) state;
grid.setTo(matrix.matrix);
for(int x = 0; x < grid.getWidth(); x++) {
for(int y = 0; y < grid.getHeight(); y++) {
IntBag nei = grid.getMooreNeighbors(x, y, 2, 0, false, new IntBag(), new IntBag(), new IntBag());
int count = 0;
for(int i = 0; i < nei.size(); i++) {
count += nei.get(i);
}
int currentState = grid.get(x, y);
if(currentState == 0) {
if(count > 3)
matrix.matrix.set(x, y, 1);
} else if(currentState == 1) {
matrix.matrix.set(x,y,0);
}
}
}
}
}
Matrix.java
package sim.app.gol;
import ec.util.MersenneTwisterFast;
import sim.engine.SimState;
import sim.field.grid.IntGrid2D;
public class Matrix extends SimState {
public final int HEIGHT = 10;
public final int WIDTH = 10;
public IntGrid2D matrix = new IntGrid2D(HEIGHT, WIDTH);
public final int NUM_CELLS = 80;
public Matrix(long seed) {
super(seed);
}
public void start() {
super.start();
// Utils for random number generator
MersenneTwisterFast g = new MersenneTwisterFast();
// We set everything to 0, no cells are active
matrix.setTo(0);
// Populating
for(int i = 0; i < NUM_CELLS; i++) {
int x = 0;
int y = 0;
// We don't want to mark as 'active' a cell that is already active
do {
x = g.nextInt(WIDTH);
y = g.nextInt(HEIGHT);
} while(matrix.get(x, y) == 1);
matrix.set(x, y, 1);
}
schedule.scheduleRepeating(new Cell());
}
public static void main(String[] args) {
doLoop(Matrix.class, args);
System.exit(0);
}
}
MatrixWithUI.java
package sim.app.gol;
import java.awt.Color;
import javax.swing.JFrame;
import sim.app.students.Students;
import sim.display.Console;
import sim.display.Controller;
import sim.display.Display2D;
import sim.display.GUIState;
import sim.engine.SimState;
import sim.portrayal.continuous.ContinuousPortrayal2D;
import sim.portrayal.grid.ObjectGridPortrayal2D;
import sim.portrayal.grid.ValueGridPortrayal2D;
import sim.portrayal.simple.OvalPortrayal2D;
public class MatrixWithUI extends GUIState {
public Display2D display;
public JFrame displayFrame;
public ValueGridPortrayal2D matrixPortrayal = new ValueGridPortrayal2D();
public static void main(String[] args) {
MatrixWithUI mwu = new MatrixWithUI();
Console c = new Console(mwu);
c.setVisible(true);
}
public void start() {
super.start();
setupPortrayals();
}
public void load(SimState state) {
super.load(state);
setupPortrayals();
}
public void setupPortrayals() {
Matrix matrix = (Matrix) state;
matrixPortrayal.setField(matrix.matrix);
matrixPortrayal.setPortrayalForAll(new OvalPortrayal2D());
display.reset();
display.setBackdrop(Color.white);
display.repaint();
}
public void init(Controller c) {
super.init(c);
display = new Display2D(600,600,this);
display.setClipping(true);
displayFrame = display.createFrame();
displayFrame.setTitle("Schoolyard Display");
c.registerFrame(displayFrame);
displayFrame.setVisible(true);
display.attach(matrixPortrayal, "Yard");
}
public void quit() {
super.quit();
if (displayFrame != null) displayFrame.dispose();
displayFrame = null;
display = null;
}
public MatrixWithUI() {
super(new Matrix (System.currentTimeMillis()));
}
public MatrixWithUI(SimState state) {
super(state);
}
public static String getName() {
return "Student Schoolyard Cliques";
}
}
However, for some reason all cells are continuously set to 0 (or off). Any thoughts?
Note: this is a tentative answer as I have no way of verifying it at the moment.
First, let's look at the documentation of ValueGridPortrayal2D
. It says:
Like other
FieldPortrayal2D
s, this class uses an underlyingSimplePortrayal2D
to draw each separate element in the grid. A defaultSimplePortrayal2D
is provided which draws squares. In the default, the color for the square is determined by looking up the value of the square in a user-provided color-table, or if there is none, by interpolating it between two user-provided colors. See thesetColorTable()
andsetLevels()
methods.
So, if you settle for squares rather than ovals, you can drop this line:
matrixPortrayal.setPortrayalForAll(new OvalPortrayal2D());
And instead, add:
java.awt.Color[] colorTable = new java.awt.Color[2];
colorTable[0] = new java.awt.Color(1.0F,0.0F,0.0F,0.0F);
colorTable[1] = new java.awt.Color(1.0F,0.0F,0.0F,1.0F);
matrixPortrayal.setMap( new SimpleColorMap(colorTable) );
This should give you white squares (transparent on a white backdrop) for 0
, and red squares for 1
.
If you want to draw ovals, this default implementation of a SimplePortrayal2D
that uses a map is not available. The documentation goes further to say:
You can also provide your own custom
SimplePortrayal2D
(usesetPortrayalForAll(...)
) to draw elements as you see fit rather than as rectangles. YourSimplePortrayal2D
should expect objects passed to its draw method to be of typeMutableDouble
.
So we need to override the draw()
method and treat the passed object - the cell value - as a MutableDouble
(by which I assume they mean the one from org.apache.commons.lang
):
matrixPortrayal.setPortrayalForAll(new OvalPortrayal2D() {
public void draw(Object object, Graphics2D graphics, DrawInfo2D info) {
MutableDouble valueObj = (MutableDouble)object;
if ( valueObj.intValue() == 0 ) {
paint = new java.awt.Color(1.0F,0.0F,0.0F,0.0F);
} else {
paint = new java.awt.Color(1.0F,0.0F,0.0F,1.0F);
}
filled = true;
super.draw(object, graphics, info);
}
});
So we have created an anonymous subclass of OvalPortrayal2D
. It inherits the fields paint
, filled
and scale
from AbstractShapePortrayal2D
. So we override paint (java.awt.Paint
, which java.awt.Color
extends) with the color we need for the particular value, and make sure the oval is filled.