• Hey Guest! Ever feel like entering a Game Jam, but the time limit is always too much pressure? We get it... You lead a hectic life and dedicating 3 whole days to make a game just doesn't work for you! So, why not enter the GMC SLOW JAM? Take your time! Kick back and make your game over 4 months! Interested? Then just click here!

GML Life Simulators (and feats of procrastination)

K

Kuro

Guest
What's your favorite life simulator and have you ever made one in Gamemaker? If so, how did it go?

I know these range in complexity from Conway's Game of life all the way up to massive simulations running in supercomputers. If you were going to make your own simulator how detailed would you want it to be?

Personally I'm quite fond of the ones that are based on genetics and try to predict how things would go if evolution started from scratch all over again such as on alien words, giving us a hint at what alien life might look like. But, I've always had a soft spot for Conways since that's where it all started. And recenty I got a raspberry Pi and when browsing Pi related sites I spotted some cool implimentations of conways on LEDs powered by a PI.

I noticed that I was inching closer and closer to my hundredth post. And that got me thinking about the implications of this. It means that I've procrastinated almost a hundred times by visiting this forum instead of coding. So I decided that if I'm going to procrastinate again to make a 100th post, then I owe it to myself to really really really really procrastinate properly for this one.

And so here it is, my most glorious feat of procrastination to date, built exclusively for this thread;
Kuro's Splendiferous Implimentation of Conway's Game of Life:



Because it was made for this post, I designed it so that if anyone does want it all they have to do is copy and paste the code from the various events into a single object in their game and it should work. The only requirement I couldn't figure out how to workaround is the need for a font named "std_font" of type Arial, and size 12, to be set up in the resource tree. Hopefuly asides from that it should work. If you want to use the code, feel free to do whatever you want with it. I'm only a couple of months into learning GML there are probably much better ways of implimenting it than I have here. And also me forcing it to stay in one object is opposite to the way I normally code. So that might not have helped. Sadly I built it in GMS:2, so it would need a few tweaks to make it work in 1.4 cause of the camera system.


And feel free to have a good laugh at my code. If you do find something hilariously bad please share it with me so I can both get a laugh out of it and also get better. Some of it is deliberately trying to be as explicit as possible, I may have went overboard. But in some places I am just being bad because I don't know any better.

Code:
/// @description initialize all the things

sim_update_delay = 5;
sim_update_counter = sim_update_delay;

enum g {
    paused,
    running,
    update,
    clear,
    draw,
    }
game_st = g.paused;

randomize();

global.mouse_click_b = false;


//** Careful to not exceed your screensize
//** Screen will be resized based on the following calculation:
//** SIM_WIDTH AND SIM_HEIGHT will be Multiplied by CELLSIZE
//**
//**
//How big cells are in Pixels
#macro CELLSIZE 8
//Define Width and Height in cells not pixels
#macro SIM_WIDTH 150
#macro SIM_HEIGHT 90
//Reserve room for sidebar
#macro SIDEBAR_WIDTH 200
//The Board
#macro BOARD_COLOR make_color_rgb(250,250,250)
#macro BOARD_COLOR_IN_DRAW_MODE make_color_rgb(170,170,240)
#macro DRAW_MODE_BOX_COL make_color_rgb(250,250,150)
#macro DRAW_MODE_CELL_COL make_color_rgb(150,150,250)

#macro BOARD_START_X 0
#macro BOARD_START_Y 0
#macro BOARD_END_X (SIM_WIDTH * CELLSIZE)
#macro BOARD_END_Y (SIM_HEIGHT * CELLSIZE)
//The Dividers
#macro DIVIDER_COLOR make_color_rgb(210,210,210)

//The Cells
#macro CELL_COLOR make_color_rgb(110,110,110)

enum cell {
    dead = 0,
    alive = 1
    }
enum cell_v {
    wait,
    deathbyunderpop = 1,
    liveby2or3neighbors = 2,
    deathbyoverpop = 3,
    bornof3neighbors = 4
    }
enum cell_d {
    clear,
    freshadd,
    freshsubtract
    }
 
//Define Width and Height in cells not pixels
room_width = BOARD_END_X + SIDEBAR_WIDTH;
room_height = BOARD_END_Y;

//Set up view
view_enabled = true;
view_set_visible(0, true);
//Set the port bounds of view 0 to 640x480
//view_set_wport(0, BOARD_END_X+SIDEBAR_WIDTH);
//view_set_hport(0, BOARD_END_Y);
view_set_wport(0, room_width);
view_set_hport(0, room_height);

//Resize and center
window_set_rectangle((display_get_width() - view_wport[0]) * 0.5, (display_get_height() - view_hport[0]) * 0.5, view_wport[0], view_hport[0]);
surface_resize(application_surface,view_wport[0],view_hport[0]);
//Camera creation
//Build a camera at (0,0), with size 640x480, 0 degrees of angle, no target instance, instant-jupming hspeed and vspeed, with a 32 pixel border
camera = camera_create_view(0, 0, view_wport[0], view_hport[0], 0, -1, -1, -1, 32, 32);
//Set view0 to use the camera "camera"
view_set_camera(0, camera);


//sim is the world, and what gets shown on screen
sim = ds_grid_create(SIM_WIDTH,SIM_HEIGHT);
ds_grid_clear(sim,cell.dead);

//sim_v maps the 4 different transitions Conway laid out. Could have just...
//...switched between alive or dead states immediately. But wanted a temp record...
//...of specific transitions in case I decide to use that data somehow.
sim_v = ds_grid_create(SIM_WIDTH,SIM_HEIGHT);
ds_grid_clear(sim_v,cell_v.wait);

//sim_u is the updated grid.
sim_u = ds_grid_create(SIM_WIDTH,SIM_HEIGHT);
ds_grid_clear(sim_u,cell.dead);

sim_d = ds_grid_create(SIM_WIDTH,SIM_HEIGHT);
ds_grid_clear(sim_d,cell_d.clear);


for (var xx = 1; xx < SIM_WIDTH; xx++) {
    for (var yy = 1; yy < SIM_HEIGHT; yy++)  {
        var chance = irandom(8);
        if (chance = 1) sim[# xx, yy] = cell.alive;
        }
    }


//Interface
#macro INTERFACE_COLOR make_color_rgb(210,210,40)
#macro BUTTONLIT_COLOR make_color_rgb(180,180,90)
#macro BUTTONLIT_DRAW_MODE_COLOR make_color_rgb(90,90,190)
#macro BUTTONLITTXT_COLOR make_color_rgb(10,10,10)


#macro BUTTONS_START_X (BOARD_END_X + 2)
#macro BUTTONS_START_Y 70
#macro BUTTONS_END_X (BOARD_END_X + SIDEBAR_WIDTH)-2
#macro BUTTONS_END_y (room_height - 2)
#macro BUTTONS_PADDING 4
#macro BUTTON_HEIGHT 30
#macro BUTTON_WIDTH (BUTTONS_END_X - BUTTONS_START_X)
#macro TXT_PADX 18
#macro TXT_PADY 7
 

enum b {
    startx,
    starty,
    width,
    height,
    endx,
    endy,
    text,
    pressed
    }

#macro NO_OF_BUTTONS 4
var spacer = BUTTON_HEIGHT + BUTTONS_PADDING;
var start_y = 0;

var i = 0;
button[i,b.startx] = BUTTONS_START_X;
button[i,b.starty] = BUTTONS_START_Y;
button[i,b.width] = BUTTON_WIDTH;
button[i,b.height] = BUTTON_HEIGHT;
button[i,b.endx] = BUTTONS_END_X;
button[i,b.endy] = BUTTONS_START_Y + BUTTON_HEIGHT;
button[i,b.text] = "START THE SIM";
button[i,b.pressed] = false;

i = 1;
start_y = BUTTONS_START_Y + (spacer * i);
button[i,b.startx] = BUTTONS_START_X;
button[i,b.starty] = start_y;
button[i,b.width] = BUTTON_WIDTH;
button[i,b.height] = BUTTON_HEIGHT;
button[i,b.endx] = BUTTONS_END_X;
button[i,b.endy] = start_y + BUTTON_HEIGHT;
button[i,b.text] = "PAUSE THE SIM";
button[i,b.pressed] = false;

i = 2;
start_y = BUTTONS_START_Y + (spacer * i);
button[i,b.startx] = BUTTONS_START_X;
button[i,b.starty] = start_y;
button[i,b.width] = BUTTON_WIDTH;
button[i,b.height] = BUTTON_HEIGHT;
button[i,b.endx] = BUTTONS_END_X;
button[i,b.endy] = start_y + BUTTON_HEIGHT;
button[i,b.text] = "CLEAR THE GRID";
button[i,b.pressed] = false;

i = 3;
start_y = BUTTONS_START_Y + (spacer * i);
button[i,b.startx] = BUTTONS_START_X;
button[i,b.starty] = start_y;
button[i,b.width] = BUTTON_WIDTH;
button[i,b.height] = BUTTON_HEIGHT;
button[i,b.endx] = BUTTONS_END_X;
button[i,b.endy] = start_y + BUTTON_HEIGHT;
button[i,b.text] = "DRAW ON GRID MODE";
button[i,b.pressed] = false;

Code:
/// @description Run all the things

switch (game_st) {
    case g.running:
        sim_update_counter-=1;
        if( sim_update_counter < 0) game_st = g.update;
        break;
    case g.update:
        //Clear the grids data's going to be pushed into
        ds_grid_clear(sim_u,cell.dead);
        ds_grid_clear(sim_v,cell_v.wait);
        //Check neighbors for every cell
        for (var xx = 1; xx < SIM_WIDTH; xx++) {
            for (var yy = 1; yy < SIM_HEIGHT; yy++)  {
                //sim[# xx, yy] is the cell in question
                var cell_n = 0;
                if ((xx-1 > 0) and (yy-1 > 0)) {
                    //Safe to look in the top left neighbor of this cell
                    if(sim[# xx-1, yy-1] == cell.alive) cell_n++;
                    }
                if (yy-1 > 0) {
                    //Safe to look in the top neighbor of this cell
                    if(sim[# xx, yy-1] == cell.alive) cell_n++;
                    }
                if ((xx+1 < SIM_WIDTH) and (yy-1 > 0)) {
                    //Safe to look in the top right corner neighbor of this cell
                    if(sim[# xx+1, yy-1] == cell.alive) cell_n++;
                    }
                if (xx+1 < SIM_WIDTH) {
                    //Safe to look in the right neighbor of this cell
                    if(sim[# xx+1, yy] == cell.alive) cell_n++;
                    }
                if ((xx+1 < SIM_WIDTH) and (yy+1 < SIM_HEIGHT)) {
                    //Safe to look in the bottom right corner neighbor of this cell
                    if(sim[# xx+1, yy+1] == cell.alive) cell_n++;
                    }
                if (yy+1 < SIM_HEIGHT) {
                    //Safe to look in the bottom neighbor of this cell
                    if(sim[# xx, yy+1] == cell.alive) cell_n++;
                    }
                if ((xx-1 > 0) and (yy+1 < SIM_HEIGHT)) {
                    //Safe to look in the bot left corner neighbor of this cell
                    if(sim[# xx-1, yy+1] == cell.alive) cell_n++;
                    }
                if (xx-1 > 0) {
                    //Safe to look in the left neighbor of this cell
                    if(sim[# xx-1, yy] == cell.alive) cell_n++;
                    }
                //Process Rules. Being explicit.
                //Mapping 4 states to sim_v for future use.
                //And Mapping alive or dead to the update grid sim_u
     
                if (sim[# xx, yy] == cell.alive) {
                    if (cell_n <= 2) {
                        sim_v[# xx, yy] = cell_v.deathbyunderpop;
                        sim_u[# xx, yy] = cell.dead;
                        }
                    if ((cell_n == 2) or (cell_n == 3)) {
                        sim_v[# xx, yy] = cell_v.liveby2or3neighbors;
                        sim_u[# xx, yy] = cell.alive;
                        }
                    if (cell_n > 3) {
                        sim_v[# xx, yy] = cell_v.deathbyunderpop;
                        sim_u[# xx, yy] = cell.dead;
                        }
                    }
                if (sim[# xx, yy] == cell.dead) {
                    if (cell_n == 3) {
                        sim_v[# xx, yy] = cell_v.bornof3neighbors;
                        sim_u[# xx, yy] = cell.alive;
                        }
                    }
                }//for (var yy = 1; yy < SIM_HEIGHT; yy++)
            }//for (var xx = 1; xx < SIM_WIDTH; xx++)
        ds_grid_copy(sim,sim_u);
        sim_update_counter = sim_update_delay;
        game_st = g.running;
        break;
    case g.clear:
        //Simple clearing, and go back to paused.
        ds_grid_clear(sim,cell.dead);
        game_st = g.paused;
        break;
    case g.draw:
        //Mostly drawing so handle in Draw event.
        break;
    }

Code:
/// @description Draw all the things

//Draw board
if (game_st == g.draw) {
    //Draw mode
    draw_set_color(BOARD_COLOR_IN_DRAW_MODE);
    draw_rectangle(BOARD_START_X,BOARD_START_Y,BOARD_END_X,BOARD_END_Y, false);
    }
else {
    //normal modes
    draw_set_color(BOARD_COLOR);
    draw_rectangle(BOARD_START_X,BOARD_START_Y,BOARD_END_X,BOARD_END_Y, false);
    }



//Draw Cell Dividers
draw_set_color(DIVIDER_COLOR);

for (var xx = 1; xx < SIM_WIDTH; xx++) {
    for (var yy = 1; yy < SIM_HEIGHT; yy++)  {
        var right_edge_x = xx * CELLSIZE;
        var bot_edge_y = yy * CELLSIZE;
        //Draw a whole row
        draw_line(BOARD_START_X, bot_edge_y ,BOARD_END_X, bot_edge_y);
        //Draw a whole column
        draw_line(right_edge_x, BOARD_START_Y, right_edge_x, BOARD_END_Y);
 
        }
    }
 
//Draw Cell Contents
draw_set_color(CELL_COLOR);

for (var xx = 1; xx < SIM_WIDTH; xx++) {
    for (var yy = 1; yy < SIM_HEIGHT; yy++)  {
        var left_edge_x = xx * CELLSIZE - (CELLSIZE-1);
        var top_edge_y = yy * CELLSIZE - (CELLSIZE-1);
        var right_edge_x = xx * CELLSIZE - 1;
        var bot_edge_y = yy * CELLSIZE - 1;
        //Draw a square if there's life
        if ( sim[# xx, yy] == cell.alive ) {
            draw_rectangle(left_edge_x,top_edge_y,right_edge_x,bot_edge_y,false);
            }
 
        }
    }

//Draw Interface
draw_set_font(std_font);
draw_set_color(INTERFACE_COLOR);

var intface_x = BUTTONS_START_X + 10;
draw_text(intface_x,5,"Kuro's Splendiferous");
draw_text(intface_x,26,"Implimentation of");
draw_text(intface_x,47,"Conway's Game of Life");
draw_text(intface_x,BUTTONS_END_y-25,"Mouse: x: "
        + string(mouse_x) + " y: " + string(mouse_y));
var butstart = BUTTONS_START_Y;
var butend = butstart + BUTTON_HEIGHT;
for(i = 0; i < NO_OF_BUTTONS; i++) {
    draw_rectangle(button[i,b.startx],button[i,b.starty],button[i,b.endx],button[i,b.endy],true);
    var InBounds = 0;
    if ( mouse_x > button[i,b.startx] ) InBounds++;
    if ( mouse_x < button[i,b.endx] ) InBounds++;
    if ( mouse_y > button[i,b.starty] ) InBounds++;
    if ( mouse_y < button[i,b.endy] ) InBounds++;
    if ( InBounds == 4 ) {
        draw_set_color(BUTTONLIT_COLOR);
        draw_rectangle(button[i,b.startx]+2,button[i,b.starty]+2,button[i,b.endx]-2,button[i,b.endy]-2,false);
        draw_set_color(BUTTONLITTXT_COLOR);
        draw_text(button[i,b.startx]+TXT_PADX,button[i,b.starty]+TXT_PADY,button[i,b.text]);
        if (global.mouse_click_b) {
            button[i,b.pressed] = true;
            var msg = string(button[i,b.text]);
            show_debug_message(msg);
            alarm_set(1,2);
            switch (i) {
                case 0:
                    game_st = g.running;
                    break;
                case 1:
                    game_st = g.paused;
                    break;
                case 2:
                    game_st = g.clear;
                    break;
                case 3:
                    if(game_st == g.draw) {
                        game_st = g.paused;
                        }
                    else {
                        game_st = g.draw;
                        }
         
                    break;
                }
     
            }
        }//if ( InBounds == 4 )
    else {
        draw_set_color(INTERFACE_COLOR);
        draw_text(button[i,b.startx]+TXT_PADX,button[i,b.starty]+TXT_PADY,button[i,b.text]);
        }
    }//for(i = 0; i < NO_OF_BUTTONS; i++)
if ( game_st == g.draw ) {
        var i = 3;
        draw_set_color(BUTTONLIT_DRAW_MODE_COLOR);
        draw_rectangle(button[i,b.startx]+2,button[i,b.starty]+2,button[i,b.endx]-2,button[i,b.endy]-2,false);
        draw_set_color(BUTTONLITTXT_COLOR);
        draw_text(button[i,b.startx]+TXT_PADX,button[i,b.starty]+TXT_PADY,button[i,b.text]);
    //If we're over the board
    if ((mouse_x < BOARD_END_X) and (mouse_y < BOARD_END_Y)) {
        //Calculate the cell
        var thiscellsim_x = floor(mouse_x/CELLSIZE)+1;
        var thiscellsim_y = floor(mouse_y/CELLSIZE)+1;
        var thiscell_x = floor(mouse_x/CELLSIZE)*CELLSIZE;
        var thiscell_y = floor(mouse_y/CELLSIZE)*CELLSIZE;
        var thiscell_xx = thiscell_x + CELLSIZE;
        var thiscell_yy    = thiscell_y + CELLSIZE;
        draw_set_color(DRAW_MODE_BOX_COL);
        draw_rectangle(thiscell_x,thiscell_y,thiscell_xx,thiscell_yy,true);
        if(mouse_check_button(mb_left)) {
            var sim_d_state = sim_d[# thiscellsim_x, thiscellsim_y];
            if(    sim[# thiscellsim_x, thiscellsim_y] == cell.alive) {
                //From aliveness
                if(sim_d_state == cell_d.clear) {
                    sim[# thiscellsim_x, thiscellsim_y] = cell.dead;
                    sim_d[# thiscellsim_x, thiscellsim_y] = cell_d.freshsubtract;
                    }
                }
            else {
                if(    sim[# thiscellsim_x, thiscellsim_y] == cell.dead) {
                    //From deadness
                    if(sim_d_state == cell_d.clear) {
                        sim[# thiscellsim_x, thiscellsim_y] = cell.alive;
                        sim_d[# thiscellsim_x, thiscellsim_y] = cell_d.freshadd;
                        }
                    }
                }
 
 
            }
        if (mouse_check_button_released(mb_left)) {
            ds_grid_clear(sim_d,cell_d.clear);
            }
        }
    }

Code:
/// @description Insert description here
// You can write your code in this editor

ds_grid_destroy(sim);
ds_grid_destroy(sim_v);
ds_grid_destroy(sim_u);
sim = undefined;
sim_v = undefined;
sim_u = undefined;
camera_destroy(camera);

Code:
/// @description Insert description here
// You can write your code in this editor

show_debug_message("ClicketyCLick");
global.mouse_click_b = true;
alarm_set(0,1);

Code:
/// @description Reset Mouse Click

global.mouse_click_b = false;

Code:
/// @description clean button presses

for(i = 0; i < NO_OF_BUTTONS; i++) {
    button[i,b.pressed] = false;
}
 
Last edited by a moderator:
Top