The agent in this example represents a cell embedded in a grid model. Thus, the agent has a position and a life indicator. The first step is to put this information into the agent’s state. An agent state has to implement the State
marker interface.
package org.simplesim.examples.gameoflife; import org.simplesim.model.State; public class CellState implements State { private int posX, posY; // the cell position private boolean alive; // is it alive? public int getPosX() { return posX; } public int getPosY() { return posY; } void setPosX(int x) { posX=x; } void setPosY(int y) { posY=y; } public boolean isAlive() { return alive; } public void setAlive(boolean value) { alive=value; } }
Next, we create the agent itself. The Cell
class is derived from BasicAgent
as base class of all agents. It has the state and the event type as class parameters. Since we use a time-step simulation and there are no local events, we can replace the event type by the general Object
type.
package org.simplesim.examples.gameoflife; import org.simplesim.core.messaging.Message; import org.simplesim.core.messaging.MultiPort; import org.simplesim.core.scheduling.Time; import org.simplesim.model.BasicAgent; public final class Cell extends BasicAgent<CellState, Object> { public Cell(int posX, int posY, boolean life) { super(null,new CellState()); getState().setPosX(posX); getState().setPosY(posY); getState().setAlive(life); // The inport is set to a SinglePort by the superclass constructor as default. setOutport(new MultiPort(this)); } @Override public Time doEvent(Time time) { if (getInport().hasMessages()) { int neighbours=0; while (getInport().hasMessages()) { if ((Boolean) getInport().poll().getContent()) neighbours++; } if ((getState().isAlive()&&(neighbours==2))||(neighbours==3)) getState().setAlive(true); else getState().setAlive(false); } getOutport().write(new Message(this,getState().isAlive())); return null; } @Override public String getName() { return "cell"; } }
The constructor initializes the state and its variables. Then it adds an outport to be connected to the cell’s neighbors later on. The inport is set to a SinglePort
as default by the parent contructor.
Note that there are various port types: A SinglePort
has only one connection and can serve as general inport or as outport. A MultiPort
sends the same message along all its connections in parallel. In this case, we want a cell to broadcast its life status to all its neighbors.
The method doEvent
is called by the simulator in each simulation step. It contains the agent’s strategy implementing the rules of the automaton: The cell stays alive if there are two living neighbors. If there are three living neighbors, it comes to life. In all other cases it dies.
The agent’s life status is communicated to the surrounding cells by sending a Message
. Consider the Message
and RoutedMessage
classes as envelopes wrapping a content and additional address data.
In the next step we take a look at how to set up the simulation model.