Last active
August 29, 2015 14:25
-
-
Save lhoworko/9653d9a64b4ef1dc868f to your computer and use it in GitHub Desktop.
Conway's Game of Life
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!doctype html> | |
<html> | |
<head> | |
<meta charset="utf-8"> | |
<title>Game of Life</title> | |
<style> | |
rect.square { | |
fill: #FFFFFF; | |
stroke: #000000; | |
stroke-width: 1; | |
shape-rendering: crispEdges; | |
} | |
rect.square.active { | |
fill: #555555; | |
} | |
button { | |
position: absolute; | |
top: 15px; | |
width: 80px; | |
padding: 2px 10px; | |
background-color: #CCCCCC; | |
opacity: 0.8; | |
border: none; | |
} | |
button:hover:not([disabled]) { | |
background-color: #AAAAAA; | |
} | |
button#toggle { | |
left: 15px; | |
} | |
button#step { | |
left: 100px; | |
} | |
button#reset { | |
left: 185px; | |
} | |
</style> | |
</head> | |
<body> | |
<div="container"> | |
<div id="board-pane"></div> | |
<button id="toggle" status="Start">Start</button> | |
<button id="step">Tick</button> | |
<button id="reset">Reset</button> | |
</div> | |
<script src="http://d3js.org/d3.v3.js"></script> | |
<script> | |
var box_width = 10; | |
var svg_width = 960; | |
var svg_height = 500; | |
var tick_time = 100; | |
var width = svg_width / box_width; | |
var height = svg_height / box_width; | |
var game_board; | |
var board_data; | |
var stepInterval; | |
init(); | |
function start() { | |
takeStep(); | |
function takeStep() { | |
step(); | |
stepInterval = setTimeout(takeStep, tick_time); | |
} | |
} | |
function stop() { | |
clearInterval(stepInterval); | |
} | |
function step() { | |
board_data = generateNextGeneration(); | |
update(); | |
} | |
function reset() { | |
board_data = get_empty_board(); | |
update(); | |
} | |
function init() { | |
d3.select('button#toggle') | |
.on('click', function() { | |
var type = d3.select(this).attr('status'); | |
var reset = d3.select('button#reset'); | |
var step = d3.select('button#step'); | |
if (type === 'Start') { | |
start(); | |
set_button(this, 'Stop'); | |
reset.attr('disabled', ''); | |
step.attr('disabled', ''); | |
} else if (type === 'Stop') { | |
stop(); | |
set_button(this, 'Start'); | |
reset.attr('disabled', null); | |
step.attr('disabled', null); | |
} | |
}); | |
d3.select('button#reset') | |
.on('click', reset); | |
d3.select('button#step') | |
.on('click', step); | |
game_board = d3.select('div#board-pane').append('svg') | |
.attr('width', svg_width + 1) | |
.attr('height', svg_height + 1) | |
.append('g') | |
.attr('class', 'game-board'); | |
reset(); | |
} | |
function set_button(button, new_state) { | |
d3.select(button) | |
.attr('status', new_state) | |
.text(new_state); | |
} | |
function get_empty_board() { | |
var data = []; | |
for (var x = 0; x < width; x++) { | |
data.push([]); | |
for (var y = 0; y < height; y++) { | |
data[x].push(false); | |
} | |
} | |
return data; | |
} | |
function update() { | |
var columns = game_board.selectAll('g.column') | |
.data(board_data); | |
columns.enter().append('g') | |
.attr('class', 'column') | |
.attr('xPos', function(d, i) { | |
return i; | |
}) | |
.attr('transform', function(d, i) { | |
return 'translate(' + (i * box_width) + ',0)'; | |
}); | |
var squares = columns.selectAll('rect') | |
.data(function(d) { | |
return d; | |
}) | |
.enter().append('rect') | |
.attr('yPos', function(d, i) { | |
return i; | |
}) | |
.attr('y', function(d, i) { | |
return i * box_width; | |
}) | |
.attr('class', 'square') | |
.attr('width', box_width) | |
.attr('height', box_width) | |
.on('click', click); | |
game_board.selectAll('rect.square') | |
.classed('active', function(d) { | |
return d; | |
}); | |
} | |
function click(active) { | |
var x = d3.select(this.parentNode).attr('xPos'); | |
var y = d3.select(this).attr('yPos'); | |
board_data[x][y] = !active; | |
d3.select(this) | |
.data([!active]) | |
.classed('active', !active); | |
} | |
function generateNextGeneration() { | |
var next_gen = get_empty_board(); | |
for (var x = 0; x < width; x++) { | |
for (var y = 0; y < height; y++) { | |
var neighbour_count = 0; | |
var neighbours = [ [-1, -1], [0, -1], [1, -1], | |
[-1, 0], [1, 0], [-1, 1], [0, 1], [1, 1] ]; | |
neighbours.forEach(function(d) { | |
var new_coor = add_points([x, y], d); | |
if (new_coor && board_data[new_coor[0]][new_coor[1]]) { | |
neighbour_count++; | |
} | |
}); | |
if (shouldBeAlive(board_data[x][y], neighbour_count)) { | |
next_gen[x][y] = true; | |
} | |
} | |
} | |
return next_gen; | |
} | |
function shouldBeAlive(alive, neighbour_count) { | |
if (alive && neighbour_count < 2) { | |
return false; | |
} else if (alive && (neighbour_count === 2 || neighbour_count === 3)) { | |
return true; | |
} else if (alive && neighbour_count > 3) { | |
return false; | |
} else if (!alive && neighbour_count === 3) { | |
return true; | |
} | |
return false; | |
} | |
function add_points(coor, offset) { | |
var x = (coor[0] + offset[0] + width) % width; | |
var y = (coor[1] + offset[1] + height) % height; | |
return [x, y]; | |
} | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment