Skip to content

Instantly share code, notes, and snippets.

@b1nary
Created July 7, 2012 17:55
Show Gist options
  • Save b1nary/3067450 to your computer and use it in GitHub Desktop.
Save b1nary/3067450 to your computer and use it in GitHub Desktop.
Game of Life / Copyworld / Langton's Ant Simulation in HTML5 Canvas Element
<!DOCTYPE html>
<title>GameOfLife Html5 Canvas</title>
<style>
canvas { border:2px solid #555; box-shadow:0px 0px 4px #999; }
body { background-color:#666; font-family:Monospace; color:white; }
table { width: 800px; }
input[type=range] { width:680px; }
</style>
<script>
var map = null;
var xmap = null;
var runner = null;
var speed = null;
var mousePress = false;
var last = new Array(4);
var count = 0;
// Easily create 2d arrays
function Array2D(x,y) {
var out = new Array(x);
for (var i = 0; i < x; i++) {
out[i] = new Array(y);
}
return out;
}
// Draw Function gets called "always"
function draw() {
ctx.fillStyle="#333";
ctx.fillRect(0, 0, c.width, c.height);
ctx.fillStyle="#555";
ctx.shadowBlur = 5;
ctx.shadowColor = "#222";
// Recreating map
if (running == true) {
count = count += 1;
// Conways Orginal (23/3)
if (rules == "23-3"){
for (yy=0; yy<box_height; yy++){
for (xx=0; xx<box_width; xx++){
var found = 0;
// Count alive's around, kind of ugly ...
if (yy > 0 && xx > 0 && map[yy-1][xx-1] == true) { found = found+1; }
if (yy > 0 && map[yy-1][xx] == true) { found = found+1; }
if (yy > 0 && xx < box_height-1 && map[yy-1][xx+1] == true) { found = found+1; }
if (xx > 0 && map[yy][xx-1] == true) { found = found+1; }
if (xx < box_width-1 && map[yy][xx+1] == true) { found = found+1; }
if (yy < box_height-1 && xx > 0 && map[yy+1][xx-1] == true) { found = found+1; }
if (yy < box_height-1 && map[yy+1][xx] == true) { found = found+1; }
if (yy < box_height-1 && xx < box_width-1 && map[yy+1][xx+1] == true) { found = found+1; }
if (map[yy][xx] == true) {
if (found < 2){ mapx[yy][xx] = false; }
if (found > 3){ mapx[yy][xx] = false; }
} else {
if (found == 3){ mapx[yy][xx] = true; }
}
}
}
}// Copyworld (1357/1357)
else if (rules == "1357-1357") {
for (yy=0; yy<box_height; yy++){
for (xx=0; xx<box_width; xx++){
var found = 0;
if (yy > 0 && xx > 0 && map[yy-1][xx-1] == true) { found = found+1; }
if (yy > 0 && map[yy-1][xx] == true) { found = found+1; }
if (yy > 0 && xx < box_height-1 && map[yy-1][xx+1] == true) { found = found+1; }
if (xx > 0 && map[yy][xx-1] == true) { found = found+1; }
if (xx < box_width-1 && map[yy][xx+1] == true) { found = found+1; }
if (yy < box_height-1 && xx > 0 && map[yy+1][xx-1] == true) { found = found+1; }
if (yy < box_height-1 && map[yy+1][xx] == true) { found = found+1; }
if (yy < box_height-1 && xx < box_width-1 && map[yy+1][xx+1] == true) { found = found+1; }
if (found == 0 || found == 2 || found == 4 || found == 6 || found == 8){ mapx[yy][xx] = false; }
if (found == 1 || found == 3 || found == 5 || found == 7 ){ mapx[yy][xx] = true; }
}
}
}// Langton's ant
else if (rules = "ant") {
if (last[1] == null || last[1] == undefined){
mapx[Math.round(box_height/2)][Math.round(box_width/2)] = true;
last[0] = Math.round(box_height/2);
last[1] = Math.round(box_width/2);
}
if (last[2] == null || last[2] == 0 || last[2] == undefined){
last[2] = 1;
}
if (map[last[0]][last[1]] == false){
mapx[last[0]][last[1]] = true;
last[2] = last[2] + 1;
if(last[2] == 5){ last[2] = 1; }
} else {
mapx[last[0]][last[1]] = false;
last[2] = last[2] - 1;
if(last[2] == 0){ last[2] = 4; }
}
// up
if(last[2] == 1){ last[0] = last[0] - 1; if(last[0] < 0){ last[0] = 0; } }
// right
if(last[2] == 2){ last[1] = last[1] + 1; if(last[1] > c.width-1){ last[1] = c.width-1; } }
// down
if(last[2] == 3){ last[0] = last[0] + 1; if(last[0] > c.height-1){ last[0] = c.height-1; } }
// left
if(last[2] == 4){ last[1] = last[1] - 1; if(last[1] < 0){ last[1] = 0; } }
}
map = mapx;
document.getElementById('_count').innerHTML = count+"";
}
// Print new Map and create backupmap for remapping (above)
mapx = Array2D(box_height,box_width);
for (yy=0; yy<box_height; yy++){
for (xx=0; xx<box_width; xx++){
if (map[yy][xx] === true) {
ctx.fillStyle = color;
mapx[yy][xx] = true;
} else {
ctx.fillStyle = "#555";
mapx[yy][xx] = false;
}
ctx.fillRect(xx*(box_size)+1, yy*(box_size)+1, box_size-2, box_size-2);
}
}
}
// Draw on Canvas with mouse
function canvas_draw(ev) {
if(mousePress){
var x = Math.round( (ev.clientX - (box_size/2) - c.offsetLeft) / box_size);
var y = Math.round( (ev.clientY - (box_size/2) - c.offsetTop) / box_size);
if (y != last[0] || x != last[1]){
if(map[y][x] == true){
last[0] = y; last[1] = x;
map[y][x] = false;
} else {
last[0] = y; last[1] = x;
map[y][x] = true;
}
if(running == false){
draw();
}
}
}
}
// Init function, called from body load
function init() {
box_size = 10;
box_width = 80;
box_height = 60;
speed = 240;
rules = "23-3"; // Conway
c = document.getElementById("canvas");
ctx = c.getContext("2d");
map = Array2D(box_height,box_width);
mapx = Array2D(box_height,box_width);
running = false;
color = "#5f5";
var mousePress = true;
c.addEventListener('mousedown', mouseDown, false);
c.addEventListener('mousemove', canvas_draw, false);
c.addEventListener('mouseup', mouseUp, false);
draw();
}
function mouseDown(ev){
mousePress = true;
canvas_draw();
}
function mouseUp(ev){
mousePress = false;
}
// Run function, Toggle run button
function run() {
running = true;
runner = setInterval(draw, speed);
}
// Stop function, Toggle run button
function stop() {
running = false;
clearInterval(runner);
var btn = document.getElementById("_run");
btn.value = "Run";
}
// Toggle run button
function runtoggle() {
var btn = document.getElementById("_run");
if(btn.value == "Run") {
btn.value = "Stop";
run();
} else {
stop();
}
}
// Color Change function
function set_color() {
var mylist = document.getElementById("_color");
var val = mylist.options[mylist.selectedIndex].value;
color = val;
draw();
}
// Speed changed function
function set_speed() {
speed = document.getElementById("_speed").value;
document.getElementById("_speed_label").innerHTML = speed+"";
if(running == true){
stop();
run();
}
}
// Size changed function
function set_size() {
size = document.getElementById("_size").value.split(":");
box_size = parseInt(size[0]);
box_width = parseInt(size[1]);
box_height = parseInt(size[2]);
map = Array2D(box_height,box_width);
mapx = Array2D(box_height,box_width);
draw();
}
// Rules changed function
function set_rules() {
rules = document.getElementById("_rules").value;
}
// Fill map random function
function random_map() {
var cc = Math.round(Math.random()*(box_height*box_width));
for (var i = 0; i < cc; i++) {
try { map[ Math.round( Math.random() * box_height - 1 ) ][ Math.round( Math.random() * box_width - 1 ) ] = true; } catch(e) {}
}
draw();
}
// Clear Map function
function clear_map() {
last = new Array(4);
map = Array2D(box_height,box_width);
if(running == true){
stop();
}
draw();
count = 0;
}
// Next ;)
function next() {
running = true;
draw();
running = false;
}
</script>
<body onload="init();">
<center>
<table border="0">
<tr>
<td><h1>Game Of Life</h1></td>
<td>&nbsp;&nbsp;&nbsp;&nbsp;</td>
<td>
<input id="_next" type="button" value="Next" onclick="next();">
<input id="_run" type="button" value="Run" style="width:40px;" onclick="runtoggle();">
<input id="_randmap" type="button" value="Random" onclick="random_map();">
<input id="_clearmap" type="button" value="Clear" onclick="clear_map();">
<select id="_color" onchange="set_color();">
<option value="#5f5">Green</option>
<option value="#f55">Red</option>
<option value="#ff5">Yellow</option>
<option value="#55f">Blue</option>
<option value="#fff">White</option>
</select>
<select id="_size" onchange="set_size();">
<option value="40:20:15">Huge (20x15)</option>
<option value="20:40:30">Big (40x30)</option>
<option value="10:80:60" selected>Nice (80x60)</option>
<option value="5:160:120">Small (160x120)</option>
<option value="3:320:240">Mini (320x240)</option>
</select>
<select id="_rules" onchange="set_rules();">
<option value="23-3">Conway's Orginal (23/3)</option>
<option value="1357-1357">Copy World (1357/1357)</option>
<option value="ant">Langton's ant</option>
</select>
<span id="_count">0</span>
</td>
</tr>
</table>
<canvas id="canvas" width="800" height="600">
This text is displayed if your browser does not support HTML5 Canvas.
</canvas>
<br>
<table border="0">
<tr>
<td><big><strong>Speed&nbsp;</strong></big></td>
<td><input type="range" title="Delay in Milliseconds" width="800" min="2" id="_speed" max="800" value="240" step="5" onchange="set_speed();" /></td>
<td><big id="_speed_label">240</big></td>
</tr>
</table>
</center>
</body>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment