If it won't be simple, it simply won't be. [Hire me, source code] by Miki Tebeka, CEO, 353Solutions

Monday, February 02, 2009

"Game of Life" in JavaScript


<html>
<head>
<title>Game Of Life</title>
<style>
table {
border: 5px solid black;
}
td {
width: 15px;
height: 15px;
}
td.dead {
background: green;
}
td.alive {
background: red;
}
</style>
</head>
<body>
<b>Game of Life<b>
<table id="board">
</table>
<button id="run">Run</button>
<button id="step">Step</button>
<button id="clear">Clear</button>

</body>
<script src="jquery.js"></script>
<script>
var NUM_ROWS = 30;
var NUM_COLS = 30;
var DEAD = 'dead';
var ALIVE = 'alive';
var RUNNING = 0;
/* We keep this for fast access since jQuery selectors
$('#board tr:nth-child(' + row + 1 + ') td:nth-child(' + col + 1 + ')'
are slow
*/
var BOARD = [];

function copy_board() {
var board = [];
for (var row = 0; row < NUM_ROWS; ++row) {
var cells = [];
for (col = 0; col < NUM_COLS; ++col) {
var state = get_cell(row, col).hasClass(ALIVE) ?
ALIVE : DEAD;
cells.push(state);
}
board.push(cells);
}

return board;
}

function cell_neighbours(row, col) {
var uprow = (row + 1) % NUM_ROWS;
var downrow = (row - 1) % NUM_ROWS;
var leftcol = (col - 1) % NUM_COLS;
var rightcol = (col + 1) % NUM_COLS;

/* FIXME: Find a better way (since -1 % 10 => -1) */
downrow = (downrow < 0) ? NUM_ROWS - 1 : downrow;
leftcol = (leftcol < 0) ? NUM_COLS - 1 : leftcol;

return [
[uprow, leftcol], [uprow, col], [uprow, rightcol],
[row, leftcol], [row, rightcol],
[downrow, leftcol], [downrow, col], [downrow, rightcol]
];
}

/* http://en.wikipedia.org/wiki/Conway%27s_Game_of_Life#Rules */
function calc_new_state(current_state, num_alive) {
if (current_state == ALIVE) {
if (num_alive < 2) {
return DEAD;
}
else if (num_alive > 3) {
return DEAD;
}
else {
return ALIVE;
}
}
else {
if (num_alive == 3) {
return ALIVE;
}
else {
return DEAD;
}
}
}


function on_step() {
/* Copy old board since we're going to change the current */
var board = copy_board();

function is_alive(cell) {
return board[cell[0]][cell[1]] == ALIVE;
}

for (var row = 0; row < NUM_ROWS; ++row) {
for (var col = 0; col < NUM_COLS; ++col) {
var neighbours = cell_neighbours(row, col);
var num_alive = $.grep(neighbours, is_alive).length;
var current_state = board[row][col];
var new_state = calc_new_state(current_state, num_alive);
if (new_state != current_state) {
toggle(row, col);
}
}
}
}

function run() {
if (!RUNNING) {
return;
}
on_step();
setTimeout(run, 200);
}

function on_run() {
var button = $('#run');
if (button.text() == 'Run') {
button.text('Stop');
RUNNING = 1;
run();
}
else {
button.text('Run');
RUNNING = 0;
}
}

function get_cell(row, col) {
return BOARD[row][col];
}

function toggle(row, col) {
var cell = get_cell(row, col);

if (cell.hasClass(ALIVE)) {
var add = DEAD;
var remove = ALIVE;
}
else {
var add = ALIVE;
var remove = DEAD;
}

cell.removeClass(remove).addClass(add);
}

function make_handler(row, col) {
return function() {
toggle(row, col);
}
}

function initiaize_board() {
var table = $('#board');
for (var row = 0; row < NUM_ROWS; ++row) {
var tr = $('<tr />');
var cells = [];
for (var col = 0; col < NUM_COLS; ++col) {
var td = $('<td />');
td.addClass(DEAD);
td.click(make_handler(row, col));
tr.append(td);
cells.push(td);
}
table.append(tr);
BOARD.push(cells);
}
}

function on_clear() {
$('.' + ALIVE).removeClass(ALIVE).addClass(DEAD);
}

function on_ready()
{
initiaize_board();
$('#step').click(on_step);
$('#run').click(on_run);
$('#clear').click(on_clear);
}

$(document).ready(on_ready);
</script>
</html>

No comments:

Blog Archive