Cellular Automata – Conway’s Game of Life in HTML 5

I had to do a school project involving automata so, after searching what this means .. I came to “cellular automaton” and then to the “Conway’s Game of Life” on wikipedia. I remember doing such a project on RentACoder few years ago in Visual Basic but I didn’t knew this fancy name for it … “cellular automata” 🙂

The rules are pretty simple :

  • A cell with 0 or 1 neighbor dies because of underpopulation
  • A cell with 4 or more neighbors dies because of overpopulation
  • If an empty space has exactly 3 cells then a new cell is born in that empty space
  • If none of the above rules apply then the space does not change (a live cell remanins alive, and an empty space is still empty)

For implementing it .. I choosed the canvas object from HTML 5 specs as I wanted to learn a little bit about it.

The first step was writing the canvas tags with a default message in case the user’s browser does not support it.

  1. <canvas id="drawBoard" width="400" height="400">
  2. Your browser does not support canvas.
  3. </canvas>

After this .. came the fun part .. javascript 🙂

The code below is pretty simple :

  • We declare how many cells we want on the X/Y axes, how many pixels wide should be a cell and the delay between drawing the next generation of cells
  • We create 2 arrays, one for storing the initial colony and one for the next generation
  • We randomly fill with live cells the initial colony
  • We draw the initial colony and then we process the colony using the game’s rules
JAVASCRIPT [Show Plain Code]:
  1. <script language="javascript">
  2. // Settings
  3. var x = 160; // How many cells on the X axis
  4. var y = 140; // How many cells on the Y axis
  5. var pixels_per_value = 4; // How many pixels wide should be a cell
  6. var delay_between_frames = 400;// In ms
  7.  
  8.  
  9. // Calculate the canvas width and height
  10. var canvas_x = x * pixels_per_value;
  11. var canvas_y = y * pixels_per_value;
  12.  
  13. // Create our matrix
  14. var matrix = new Array(y);
  15. var matrix_tmp = new Array(y);
  16. for (i = 0; i < y; i++)
  17. {
  18.         matrix[i] = new Array(x);
  19.         matrix_tmp[i] = new Array(x);
  20. }
  21.  
  22. // Fill with some random points
  23. var random = 1600;
  24. var m,n;
  25. for (i = 0; i < random; i++)
  26. {
  27.         m = Math.floor(Math.random()*y);
  28.         n = Math.floor(Math.random()*x);
  29.         matrix[m][n] = 1;
  30. }
  31.  
  32. // Get the canvas reference and the 2D context
  33. var canvas = document.getElementById('drawBoard');
  34. // Set canvas size
  35. canvas.width = canvas_x;
  36. canvas.height = canvas_y;
  37. var context = canvas.getContext('2d');
  38.  
  39. // Draw the first cell generation
  40. draw_matrix();
  41.  
  42. // Call for the next generation of the cell colony
  43. setTimeout("process_life()", delay_between_frames);
  44.  
  45.  
  46. function process_life()
  47. {
  48.         var neighbours = 0;
  49.         matrix_tmp = matrix.clone();
  50.         for (i = 0; i < y; i++)
  51.         {
  52.                 for (j = 0; j < x; j++)
  53.                 {
  54.                         neighbours = count_neighbours(i,j);
  55.                         // Underpopulation
  56.                         if (neighbours < 2)
  57.                         {
  58.                                 matrix_tmp[i][j] = 0;
  59.                         }
  60.                         // Overcrowding
  61.                         if (neighbours > 3)
  62.                         {
  63.                                 matrix_tmp[i][j] = 0;
  64.                         }
  65.                         // Birth
  66.                         if (neighbours == 3)
  67.                         {
  68.                                 matrix_tmp[i][j] = 1;
  69.                         }
  70.                 }
  71.         }
  72.         matrix = matrix_tmp.clone();
  73.         draw_matrix();
  74.         setTimeout("process_life()", delay_between_frames);
  75. }
  76.  
  77. // Added so we can copy the array between steps
  78. Array.prototype.clone = function ()
  79. {
  80.         var tmp = new Array();
  81.         for (var property in this)
  82.         {
  83.                 tmp[property] = typeof (this[property]) == 'object'  ? this[property].clone() : this[property];
  84.         }
  85.         return tmp;
  86. }
  87.  
  88.  
  89. function count_neighbours(i, j)
  90. {
  91.         var count = 0;
  92.         // Check for its maximum 8 neighbours
  93.         if (matrix[i][j-1] == 1) count++;
  94.         if (matrix[i][j+1] == 1) count++;
  95.         //
  96.         if (matrix[i-1] != undefined)
  97.         {
  98.                 if (matrix[i-1][j] == 1) count++;
  99.                 if (matrix[i-1][j-1] == 1) count++;
  100.                 if (matrix[i-1][j+1] == 1) count++;
  101.         }
  102.         if (matrix[i+1] != undefined)
  103.         {
  104.                 if (matrix[i+1][j] == 1) count++;
  105.                 if (matrix[i+1][j-1] == 1) count++;
  106.                 if (matrix[i+1][j+1] == 1) count++;
  107.         }
  108.         return count;
  109. }
  110.  
  111. function draw_matrix()
  112. {
  113.         for (i = 0; i < y; i++)
  114.         {
  115.                 for (j = 0; j < x; j++)
  116.                 {
  117.                         draw_matrix_point(j, i, matrix[i][j]);
  118.                 }
  119.         }
  120. }
  121.  
  122.  
  123. function draw_matrix_point(i, j, status)
  124. {
  125.         var origin_x = i * pixels_per_value;
  126.         var origin_y = j * pixels_per_value;
  127.         if (status == 1)
  128.         {
  129.                 context.fillStyle = '#00FF00';
  130.         }
  131.         else
  132.         {
  133.                 context.fillStyle = '#FF0000';
  134.         }
  135.         draw_rectangle(context, origin_x, origin_y, pixels_per_value, pixels_per_value);
  136. }
  137.  
  138. // Draw a rectangle based on x/y origin point and having a specified width and height
  139. function draw_rectangle(context, origin_x, origin_y, width, height)
  140. {
  141.         context.beginPath();
  142.         context.rect(origin_x, origin_y, width, height);
  143.         context.closePath();
  144.         context.fill();
  145. }
  146. </script>

If you have any questions .. feel free to ask 🙂

Click here to see the working project.


Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.