-1

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?

Mars
  • 8,689
  • 2
  • 42
  • 70
MrD
  • 4,986
  • 11
  • 48
  • 90
  • My suggestion would be to step through the code in a debugger and/or add print statements at strategic points, to see where exactly things are going wrong. – NPE Dec 28 '14 at 18:14
  • Your rules are not Conway's rules. They seem to be rules that would lead the whole matrix to die within a couple of generations or so. Is that what's happening? – RealSkeptic Dec 28 '14 at 18:31
  • @NPE that's the thing, when I echo statements cells turn on and off as appropriate but the changes are reflected in the GUI. But I can't understand where I've gone wrong in that. – MrD Dec 28 '14 at 18:31
  • @max0005: That last comment should be right at the top of your question. :) – NPE Dec 28 '14 at 18:35
  • @NPE uh apologies, I'll fix :) – MrD Dec 28 '14 at 18:37
  • Well, what about using the correct rules? As NPE has correctly pointed out... – laune Dec 28 '14 at 18:42
  • Your oval doesn't have anything that tells it to draw a different color for a different cell value. You should probably override its `draw` method. – RealSkeptic Dec 28 '14 at 19:12
  • @RealSkeptic I think that's it but I don't understand particularly well how that class works. Could you help by any chance? – MrD Dec 28 '14 at 19:27

1 Answers1

1

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 FieldPortrayal2Ds, this class uses an underlying SimplePortrayal2D to draw each separate element in the grid. A default SimplePortrayal2D 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 the setColorTable() and setLevels() 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 (use setPortrayalForAll(...) ) to draw elements as you see fit rather than as rectangles. Your SimplePortrayal2D should expect objects passed to its draw method to be of type MutableDouble.

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.

RealSkeptic
  • 33,993
  • 7
  • 53
  • 79
  • Hi @RealSkeptic, thanks. Hm, I'm afraid I don't have a constructur for Color taking four doubles, I've changed it to ints which seems to work? Also, all I get is a blue background :\ – MrD Dec 28 '14 at 20:45
  • Which version of Java are you using? There is a four-float constructor in Java 7 for sure. If you are using the four `int` constructor, you should be using `255` instead of `1.0`. – RealSkeptic Dec 28 '14 at 20:48
  • I have JRE7 on Eclipse but get an error on colorTable[0] = new java.awt.Color(1.0,0.0,0.0,0.0); telling me the constructor is undefined – MrD Dec 28 '14 at 20:56
  • Sorry, you're right. Since they are floats, they should be `1.0F` and `0.0F` rather than `1.0` and `0.0`. I edited the answer. – RealSkeptic Dec 28 '14 at 21:03
  • Also, I assume that the second element of the array should begin with a ( instead of a [? – MrD Dec 28 '14 at 21:04
  • Hm, Graphics2D and Info2D cannot be resolved to type nor is it suggesting anywhere to import from :\ – MrD Dec 28 '14 at 21:06
  • `java.awt.Graphics2D` and `sim.portrayal.DrawInfo2D`. And yes, I have corrected the bracket. BTW, my two suggestions are mutually exclusive - either you do the default (with color table) or the ovals. – RealSkeptic Dec 28 '14 at 21:09
  • Thank you so much, that worked! :D I'll have to look through properly and better understand the GUI, but in the meantime thanks so much :3 – MrD Dec 28 '14 at 21:12