#include "adevs.h" #include #include #include #include using namespace std; using namespace adevs; /* An implementation of the game of life using the message passing api. This model will evolve entirely in integer time because this is the easy choice for most discrete time models. Otherwise, we must introduce distinct output and state transitions, such as is done by Parallel DEVS. */ /// Cellspace dimensions #define WIDTH 800 #define HEIGHT 600 /// Possible cell phases. typedef enum { Dead, Alive } Phase; /// A cell in the Game of Life. class Cell: public Model { public: /** * Create a cell and set the initial state. * The width and height fields are used to determine if a * cell is an edge cell. The last phase pointer is used to * visualize the cell space. */ Cell(int x, int y, Phase phase, Phase* vis_phase): Model(),x(x),y(y),phase(phase), nalive(0),vis_phase(vis_phase){} int init(SimEnv* env); int update(SimEnv* env); int update(SimEnv* env, vector& x); void fini(int){} /// The array of all models static Cell* space[WIDTH][HEIGHT]; private: /// Location of the cell in the 2D space const int x, y; /// Current phase of this cell Phase phase; /// The number of living neighbors int nalive; /// Output variable for visualization Phase* vis_phase; /// Returns true if the cell will be born bool check_born_rule() const { return (phase == Dead && nalive == 3); } /// Return true if the cell will die bool check_death_rule() const { return (phase == Alive && (nalive < 2 || nalive > 3)); } void send_phase(SimEnv* env, bool alive_only = false); }; Cell* Cell::space[WIDTH][HEIGHT]; // Send our phase to our eight neighbors void Cell::send_phase(SimEnv* env, bool alive_only) { for (int dx = -1; dx <= 1; dx++) { int xdst = x+dx; if (xdst < 0) xdst = WIDTH-1; if (xdst >= WIDTH) xdst = 0; for (int dy = -1; dy <= 1; dy++) { int ydst = y+dy; if (ydst < 0) ydst = HEIGHT-1; if (ydst >= HEIGHT) ydst = 0; // Only send if we are living when starting the simulation if ((x != xdst || y != ydst) && (phase == Alive || !alive_only)) { env->send(this,space[xdst][ydst],phase); } } } } int Cell::init(SimEnv* env) { // Send our phase to all of our neighbors. The neighboring // state at time 1 will be a function of our state and theirs // at time 0. send_phase(env,true); // If a phase change should occur if (check_death_rule() || check_born_rule()) return adevs_epsilon(); // = 1 // Otherwise, do nothing return adevs_inf(); } int Cell::update(SimEnv* env) { Phase old_phase = phase; // Change state if (check_death_rule()) phase = Dead; else if (check_born_rule()) phase = Alive; // Propagate this change of state. The model state // at time now()+1 will be a function of the state // at time now(). if (phase != old_phase) { *vis_phase = phase; send_phase(env); } // If we need to change state again schedule that event. if (check_death_rule() || check_born_rule()) return 1; return adevs_inf(); } int Cell::update(SimEnv* env, vector& x) { // Update the count if living neighbors for (unsigned i = 0; i < x.size(); i++) nalive += (x[i] == Dead) ? -1 : 1; // Calculate our new state and report our time of next event return update(env); } // Phase space to visualize. Also used as our initial state. Phase phase[WIDTH][HEIGHT]; // Window and cell dimensions. #define CELL_SIZE 1 const GLint win_width = WIDTH*CELL_SIZE; const GLint win_height = HEIGHT*CELL_SIZE; void drawSpace() { static bool init = true; if (init) { init = false; glutUseLayer(GLUT_NORMAL); glClearColor(0.0,0.0,1.0,1.0); glColor3f(1.0,1.0,1.0); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(0.0,(float)win_width,0.0,(float)win_height,1.0,-1.0); } glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); for (int x = 0; x < WIDTH; x++) { for (int y = 0; y < HEIGHT; y++) { if (phase[x][y] == Alive) { GLint wx = CELL_SIZE*x; GLint wy = CELL_SIZE*y; glRecti(wx,wy,wx+CELL_SIZE,wy+CELL_SIZE); } } } glutSwapBuffers(); } void simulateSpace() { // Seed the random number generator srand(time(NULL)); while (true) { // Randomize the initial state and create the cells for (int x = 0; x < WIDTH; x++) { for (int y = 0; y < HEIGHT; y++) { if (rand()%8 == 0) phase[x][y] = Alive; else phase[x][y] = Dead; Cell::space[x][y] = new Cell(x,y,phase[x][y],&(phase[x][y])); } } drawSpace(); // Create the simulator and add the models to it Simulator sim; for (int x = 0; x < WIDTH; x++) for (int y = 0; y < HEIGHT; y++) sim.add(Cell::space[x][y]); // While the space is evolving while (sim.next_event_time() < adevs_inf()) { // Run the next simulation step sim.exec_next_event(); // Draw the updated display drawSpace(); } // Clean up for (int x = 0; x < WIDTH; x++) for (int y = 0; y < HEIGHT; y++) delete Cell::space[x][y]; } } int main(int argc, char** argv) { // Setup the display glutInit(&argc,argv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA); glutInitWindowSize(win_width,win_height); glutCreateWindow("glife"); glutPositionWindow(0,0); glutDisplayFunc(drawSpace); glutIdleFunc(simulateSpace); glutMainLoop(); // Done return 0; }