var svg = d3.select("svg"); var rows = 60; var cols = 90; var marginBottom = 152; var totalWidth = tributary.sw; var totalHeight = tributary.sh - marginBottom; var width = parseInt(totalWidth / cols); var height = parseInt(totalHeight / rows); function GameOflife(options) { this.rows = options.rows; this.cols = options.cols; } GameOflife.prototype.setInitialState = function(init) { this.data = []; for(var i = 0; i < this.rows; i++) { for(var j = 0; j < this.cols; j++) { this.data.push({ x: j, y: i, live: 0 }); } } for(i = 0; i < init.length; i++) { var pos = this.getPos(init[i][0], init[i][1]); this.data[pos].live = 1; } }; GameOflife.prototype.getPos = function(x, y) { if ( x >= 0 && x < this.cols && y >= 0 && y < this.rows ) { return (y * this.cols) + x; } return -1; }; GameOflife.prototype.getCell = function(x, y) { var pos = this.getPos(x, y); if (pos >= 0 && pos < this.data.length ) { return this.data[pos]; } return {}; }; GameOflife.prototype.getNeighbours = function(cell) { var x = cell.x; var y = cell.y; var cells = []; cells.push(this.getCell(x - 1, y - 1)); cells.push(this.getCell(x - 1, y)); cells.push(this.getCell(x - 1, y + 1)); cells.push(this.getCell(x, y - 1)); cells.push(this.getCell(x, y + 1)); cells.push(this.getCell(x + 1, y - 1)); cells.push(this.getCell(x + 1, y)); cells.push(this.getCell(x + 1, y + 1)); return cells; }; GameOflife.prototype.nextGeneration = function() { var newData = []; for(var i = 0; i < this.data.length; i++) { var cell = this.data[i]; var live = 0; var livesCount = 0; var neighbours = this.getNeighbours(cell); for (var j = 0; j < neighbours.length; j++) { if (neighbours[j].live && neighbours[j].live == 1) { livesCount = livesCount + 1; } } //Rule 1; Any live cell with fewer than two live neighbours dies, as if //caused by under-population. if (livesCount < 2 && cell.live == 1) { //console.log('dies'); live = 0; } //Rule 2: Any live cell with two or three live neighbours lives on to the //next generation. if ((livesCount == 2 || livesCount == 3) && cell.live == 1) { //console.log('keep living'); live = 1; } //Rule 3; Any live cell with more than three live neighbours dies, //as if by overcrowding. if ((livesCount > 3) && cell.live == 1) { //console.log('dies'); live = 0; } //Rule 4: Any dead cell with exactly three live neighbours becomes a live cell, //as if by reproduction. if (livesCount == 3 && cell.live === 0) { //console.log('born!'); live = 1; } newData.push({x: cell.x, y: cell.y, live: live}); } this.data = newData; } GameOflife.prototype.draw = function() { d3.selectAll("rect") .data(this.data) .attr('class', function(d) { return "live" + d.live; }); } GameOflife.prototype.start = function() { this.nextGeneration(); this.draw(); } svg.attr("width", totalWidth). attr("height", totalHeight + marginBottom); var gameOfLife = new GameOflife({ rows: rows, cols: cols}); var graph = svg.append("g"). attr("width", totalWidth). attr("height", totalHeight); var x = d3.scale.linear().domain([0, cols]).range([0, totalWidth]); var y = d3.scale.linear().domain([0, rows]).range([0, totalHeight]); function addButton(x, y, text, callback) { var button = svg.append('g') .classed('btn', true) .attr('transform', 'translate('+[x,y]+')'); button.append('rect') .attr("width", 100) .attr("height", 50) .attr("x", 0) .attr("y", 0); button.append('text') .attr("x", 20) .attr("y", 30) .text(text) .on('click', callback); } var initialized = false; function init() { var state = []; var maxInitailcells = parseInt(Math.random()*(rows*cols)); for (var a = 0; a < maxInitailcells; a++) { var item = [ parseInt(Math.random()*cols), parseInt(Math.random()*rows) ]; state.push(item); } gameOfLife.setInitialState(state); if (!initialized) { var data = graph.selectAll("g.cell") .data(gameOfLife.data); data.enter().append("rect") .classed('cell',true) .attr("width", width - 0.3) .attr("height", height - 0.3) .attr("x", function(d) { return x(d.x); }) .attr("y", function(d) { return y(d.y); }); } gameOfLife.draw(); initialized = true; } var interval = null; addButton(10, totalHeight + 20, 'Start >>', function(){ interval = setInterval(function(){ gameOfLife.start(); }, 500); }); addButton(123, totalHeight + 20, 'RESET', function(){ clearInterval(interval); init(); }); addButton(250, totalHeight + 20, 'Stop', function(){ clearInterval(interval); }); init();