2009-12-29
Once again, Conway's Game of Life. This time written in Vala using SDL for the graphics.
In comparison to my previous Game of Life implementation, this version starts faster; much much faster. In the time it takes the Python/Clutter version to make 100 cells, the Vala/SDL version creates 100000 cells. I attribute the speed increase on the following factors:
To test the code, save the following as game_of_life.vala
The code is compiled with
As in the Python/Clutter example, pressing "q" will quit the game, and pressing "r" will reset the game.
Now stop reading, and get mesmerized by little green blocks.
In comparison to my previous Game of Life implementation, this version starts faster; much much faster. In the time it takes the Python/Clutter version to make 100 cells, the Vala/SDL version creates 100000 cells. I attribute the speed increase on the following factors:
- Vala code is compiled to machine code, while Python code needs to be executed by the Python runtime.
- SDL is a much lower level library than Clutter.
- Since Clutter utilizes OpenGL for graphics, my graphics card, which is rather old, my not be the best OpenGL renderer
To test the code, save the following as game_of_life.vala
or you can just download game_of_life.vala/*compile with valac --pkg sdl game_of_life.vala -o game_of_life */ using SDL; public class Cell { public Rect on_screen_rect; public Rect self_rect; public Surface surface; public weak Screen screen; public bool alive {get;set;} public bool updated; public int16 size; private uint32 life_color; private uint32 dead_color; public Cell(Screen s, uint32 flags,int16 x, int16 y, int16 size,uint32 lcolor,uint32 bg_color ) { life_color = lcolor; dead_color = bg_color; updated=false; screen = s; on_screen_rect.w = size; on_screen_rect.h = size; on_screen_rect.x = x; on_screen_rect.y = y; self_rect.w = size; self_rect.h = size; self_rect.x=0; self_rect.y=0; this.size = (int16)size; alive=false; //create the surface surface = new Surface.RGB(flags, size, size, 32, 0,0,0, 255); } public void set_life(bool life) { alive=life; if (alive) { surface.fill(null,life_color); }else{ surface.fill(null,dead_color); } } public void draw() { surface.blit(self_rect,screen,on_screen_rect); } } public class Game { private const int SCREEN_WIDTH = 800; private const int SCREEN_HEIGHT = 600; private const int SCREEN_BPP = 32; private const int DELAY = 100; private weak SDL.Screen screen; private bool do_loop; private Cell[,] cells; private int16 num_cols; private int16 num_rows; private uint32 bg_color; private int random_max; public Game(int16 cell_size, int random_max) { this.random_max = random_max; //initialize the video uint32 surface_flags = SurfaceFlag.DOUBLEBUF | SurfaceFlag.HWACCEL | SurfaceFlag.HWSURFACE; screen = Screen.set_video_mode (SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP, surface_flags); if (screen == null) { GLib.error ("Could not set video mode."); } //we have a screen, define some colors bg_color=screen.format.map_rgb(0,0,0); uint32 life_color=screen.format.map_rgb(0,255,0); SDL.WindowManager.set_caption ("Game of Life: Vala, SDL", ""); //make all of the cells //'''fill the screen with rectangles''' num_cols = (int16)SCREEN_WIDTH/cell_size; num_rows = (int16)SCREEN_HEIGHT/cell_size; uint32 cells_to_create = num_cols*num_rows; int16 y; int16 x; cells = new Cell[num_rows,num_cols]; stdout.printf( "%0.f cells to create\n",cells_to_create); for ( int16 row=0; row<num_rows; row++) { y = row*cell_size; for ( int16 column=0; column<num_cols; column++) { x=column*cell_size; //set the column index to a rect that is cell_size by cell_size cells[row,column] = new Cell(screen,surface_flags,x,y,cell_size,life_color,bg_color); } cells_to_create-=num_cols; } stdout.printf( "cells created\n" ); do_loop = true; } private void seed_life(bool reseed=false) { Rand rand = new Rand(); stdout.printf( "adding random life\n" ); //loop through the rows for(int r=0; r<num_rows;r++) { //loop through the columns for(int c=0; c<num_cols ; c++) { //generate a number int i = rand.int_range(0,random_max); if (i==0) { //r,c is alive cells[r,c].set_life(true); }else if (reseed) { cells[r,c].set_life(false); } } } } public void run() { //seed life into the cells seed_life(); //do stuff while (do_loop) { this.draw (); this.process_events (); detect_life(); SDL.Timer.delay (DELAY); } } private void quit() { do_loop = false; } private void process_events() { Event event = Event (); while (Event.poll (event) == 1) { switch (event.type) { case EventType.QUIT: quit(); break; case EventType.KEYDOWN: this.on_keyboard_event (event.key); break; } } } private void on_keyboard_event (KeyboardEvent event) { if(event.keysym.sym==KeySymbol.q) { quit(); } if(event.keysym.sym==KeySymbol.r) { seed_life(true); } } private void draw() { screen.fill (null, bg_color); foreach( Cell c in cells ) { c.draw(); } screen.update_rect(0,0,screen.w,screen.h); } private int living_neighbors(int r, int c) { int alive = 0; int r_min = r-1; int r_max = r+1; int c_min = c-1; int c_max = c+1; if(r==0) { r_min=r; } else if (r == num_rows-1) { r_max=r; } if ( c==0 ) { c_min=c; } else if (c == num_cols-1) { c_max=c; } int tr = r_min; int tc = c_min; while(tr <= r_max) { tc = c_min; while(tc <= c_max) { if ( tr!=r || tc!=c ) { if ( cells[tr,tc].alive) { alive+=1; } } tc+=1; } tr+=1; } return alive; } public void detect_life() { int r; int c; int index; Array<int>? dead_array = new Array<int>(false,false,(uint)sizeof(int)); Array<int>? alive_array = new Array<int>(false,false,(uint)sizeof(int)); bool alive; int ln; for(r=0; r<num_rows; r++) { for(c=0; c<num_cols; c++) { alive = cells[r,c].alive; ln = living_neighbors(r,c); if( alive && ( ln<2 || ln>3 )) { //this cell will die; boohoo dead_array.append_val(r); dead_array.append_val(c); } else if(!alive && ln==3) { //this cell is now alive; damn zombies alive_array.append_val(r); alive_array.append_val(c); } } } //kill what needs killing uint final_index = dead_array.length-1; for(index=0 ; index<final_index ; index+=2) { r = dead_array.index(index); c= dead_array.index(index+1); cells[r,c].set_life(false); } //frankenstein what needs life final_index = alive_array.length-1; for(index=0 ; index<final_index ; index+=2) { r = alive_array.index(index); c= alive_array.index(index+1); cells[r,c].set_life(true); } } } public static void main(string[] args) { int16 cell_size = 2; int random_max = 15; SDL.init (InitFlag.VIDEO); Game game = new Game(cell_size,random_max); game.run(); SDL.quit(); }
The code is compiled with
valac --pkg sdl game_of_life.vala -o game_of_life
As in the Python/Clutter example, pressing "q" will quit the game, and pressing "r" will reset the game.
Now stop reading, and get mesmerized by little green blocks.
I'd mucho appreciate it if you put it up in a way where we can get a raw version out easily.
:)