Game Of Life

HTML
<link href="https://fonts.googleapis.com/css?family=Geo" rel="stylesheet"> <div class="wrapper"> <div id="game"> <div class="control-panel"> <div class="logo-container"> <div class="logo"></div> </div> <a href="#" class="ctrl-button" id="ctrl_1">Start</a> <a href="#" class="ctrl-button" id="ctrl_2">Generate world</a> <a href="#" class="ctrl-button" id="ctrl_3">Reset</a> </div> <canvas id="world"> <!-- Hello World :) --> </canvas> <div class="info-panel"> <span class="info_generation">Generation: </span> <span id="info_generation">0</span> <span class="separator">|</span> <span class="info_life-cell">Life cell: </span> <span id="info_life-cell">0</span> <span class="separator">|</span> <span class="info_speed">Speed: </span> <span id="info_speed">0</span> <span>ms</span> </div> </div> <div id="rules"> <div class="title">Rules</div> <div class="text"> <p>The universe of the Game of Life is an infinite two-dimensional orthogonal grid of square cells, each of which is in one of two possible states, alive or dead, or "populated" or "unpopulated" (the difference may seem minor, except when viewing it as an early model of human/urban behaviour simulation or how one views a blank space on a grid). Every cell interacts with its eight neighbours, which are the cells that are horizontally, vertically, or diagonally adjacent. At each step in time, the following transitions occur:</p> <ul> <li>Any live cell with fewer than two live neighbours dies, as if caused by underpopulation.</li> <li>Any live cell with two or three live neighbours lives on to the next generation.</li> <li>Any live cell with more than three live neighbours dies, as if by overpopulation.</li> <li>Any dead cell with exactly three live neighbours becomes a live cell, as if by reproduction.</li> </ul> </div> </div> </div>
STYLUS
* *:before *:after margin: 0 padding: 0 body background: rgba(33,33,33,1) font-family: Sans-serif .wrapper padding: 24px &:after display: block content: '' clear: both canvas display: block border: 2px solid rgba(224,224,224,1) .control-panel &:after display: block content: '' clear: both .logo-container width: 32px height: @width border: 2px solid rgba(224,224,224,1) float: left $logo-color = rgba(139,195,74,1) .logo width: 4px height: @width margin: @width box-shadow: 10px 5px 0 $logo-color, 15px 10px 0 $logo-color, 5px 15px 0 $logo-color, 10px 15px 0 $logo-color, 15px 15px 0 $logo-color .ctrl-button font-size: 18px font-family: 'Geo', sans-serif color: rgba(224,224,224,1) text-decoration: none text-transform: uppercase display: block line-height: 32px padding: 0 8px border: 2px solid rgba(224,224,224,1) float: left &:hover background: rgba(224,224,224,1) &#ctrl_1 color: rgba(236,64,122,1) &#ctrl_2 color: rgba(104,159,56,1) &#ctrl_3 color: rgba(0,172,193,1) .info &-panel font-size: 16px font-family: 'Geo', sans-serif background: rgba(224,224,224,1) color: rgba(33,33,33,1) line-height: 24px padding: 0 8px cursor: default &_generation color: rgba(104,159,56,1) &_life-cell color: rgba(236,64,122,1) &_speed color: rgba(0,172,193,1) .separator margin: 0 8px #game float: left #rules float: left font-size: 16px font-family: 'Geo', sans-serif color: rgba(224,224,224,1) display: block line-height: 1.3 border: 2px solid rgba(224,224,224,1) overflow: auto .title padding: 0 8px line-height: 32px font-size: 18px border-bottom: 4px solid rgba(224,224,224,1) .text padding: 8px p margin: 0 0 8px 0 ul padding-left: 16px list-style: none li position: relative margin-bottom: 4px &:before display: block content: '' position: absolute width: 4px height: @width background: rgba(104,159,56,1) left: -12px top: 8px
JAVASCRIPT
GameOfLife = (function() { function GameOfLife(config) { this.canvas = config.canvas; this.unitSize = config.unitSize; this.columns = config.columns; this.lines = config.lines; this.drawRate = config.drawRate; this.gridSize = config.gridSize; this.width = this.canvas.width = this.unitSize * this.columns; this.height = this.canvas.height = this.unitSize * this.lines; this.ctx = this.canvas.getContext('2d'); this.infoPanel = config.infoPanel; this.infoPanel.style.width = this.width - 12 + 'px'; this.infoGeneration = config.infoGeneration; this.infoLifeCell = config.infoLifeCell; this.infoSpeed = config.infoSpeed; this.oldState = []; this.newState = []; this.useState = []; this.gameOn = false; this.counter = 0; this.lifeCell = 0; this.gameBox = config.gameBox; this.rulesBox = config.rulesBox; this.gameBoxSize = {w: this.gameBox.clientWidth, h: this.gameBox.clientHeight}; if ( window.innerWidth > 800 ) { this.rulesBox.style.width = '680px'; this.rulesBox.style.height = this.gameBoxSize.h - 4 + 'px'; } else { this.rulesBox.style.width = this.gameBoxSize.w - 4 + 'px'; } } GameOfLife.prototype.init = function() { for (var i = 0; i < this.columns; i++) { this.oldState[i] = []; this.newState[i] = []; this.useState[i] = []; for (var j = 0; j < this.lines; j++) { this.newState[i][j] = false; this.oldState[i][j] = false; this.useState[i][j] = false; } } }; GameOfLife.prototype.randomDraft = function() { for (var i = 0; i < this.columns; i++) { this.oldState[i] = []; this.newState[i] = []; this.useState[i] = []; for (var j = 0; j < this.lines; j++) { var result = Math.random() < .075; this.newState[i][j] = result; this.oldState[i][j] = result; this.useState[i][j] = false; } } this.update(); this.counter = 0; }; GameOfLife.prototype.update = function() { for (var i = 0; i < this.columns; i++) { for (var j = 0; j < this.lines; j++) { this.newState[i][j] = this.updateState( i, j ); } } for (var i = 0; i < this.newState.length; i++) { for (var j = 0; j < this.newState[i].length; j++) { this.oldState[i][j] = this.newState[i][j] ? true : false } } this.counter++; }; GameOfLife.prototype.updateState = function(i, j){ var adyacentAlive = 0, iMinus = i - 1 >= 0, iPlus = i + 1 < this.columns, jMinus = j - 1 >= 0, jPlus = j + 1 < this.lines; if (iMinus && jMinus && this.oldState[i-1][j-1]){ adyacentAlive++ }; if (iMinus && this.oldState[i-1][j]){ adyacentAlive++ }; if (iMinus && jPlus && this.oldState[i-1][j+1]){ adyacentAlive++ }; if (iPlus && jMinus && this.oldState[i+1][j-1]){ adyacentAlive++ }; if (iPlus && this.oldState[i+1][j]){ adyacentAlive++ }; if (iPlus && jPlus && this.oldState[i+1][j+1]){ adyacentAlive++ }; if (jMinus && this.oldState[i][j-1]){ adyacentAlive++ }; if (jPlus && this.oldState[i][j+1]){ adyacentAlive++ }; return (this.oldState[i][j] && adyacentAlive === 2) || (this.oldState[i][j] && adyacentAlive === 3) || (!this.oldState[i][j] && adyacentAlive === 3); }; GameOfLife.prototype.draw = function() { this.lifeCell = 0; this.ctx.clearRect(0, 0, this.width, this.height); this.drawGrid(); for (var i = 0; i < this.columns; i++) { for (var j = 0; j < this.lines; j++) { if (this.useState[i][j]) { this.ctx.beginPath(); this.ctx.fillStyle = 'rgba(104,159,56,.1)'; // cell color this.ctx.fillRect(i*this.unitSize, j*this.unitSize, this.unitSize, this.unitSize); this.ctx.closePath(); }; } } for (var i = 0; i < this.columns; i++) { for (var j = 0; j < this.lines; j++) { if (this.newState[i][j]) { this.lifeCell++; this.useState[i][j] = true; this.ctx.beginPath(); this.ctx.fillStyle = 'rgba(236,64,122,1)'; // cell color this.ctx.fillRect(i*this.unitSize, j*this.unitSize, this.unitSize, this.unitSize); this.ctx.closePath(); }; } } }; GameOfLife.prototype.addUnit = function(x, y) { var i = Math.floor(x/this.unitSize); var j = Math.floor(y/this.unitSize); this.newState[i][j] = !this.newState[i][j]; this.oldState[i][j] = !this.oldState[i][j]; } GameOfLife.prototype.gg = function() { this.gameOn = !this.gameOn; }; GameOfLife.prototype.start = function() { this.init(); this.draw(); setInterval(this.tick, this.drawRate, this); }; GameOfLife.prototype.tick = function(self) { if (self.gameOn) self.update(); self.infoGeneration.innerHTML = self.getGeneration(); self.infoLifeCell.innerHTML = self.getLifeCell(); self.infoSpeed.innerHTML = self.getSpeed(); self.draw(); self.grid = self.state; }; GameOfLife.prototype.getWorldSize = function() { return { w: this.width, h: this.height }; }; GameOfLife.prototype.getGeneration = function() { return this.counter; }; GameOfLife.prototype.getLifeCell = function() { return this.lifeCell; }; GameOfLife.prototype.getSpeed = function() { return Math.round(this.drawRate); }; GameOfLife.prototype.drawGrid = function() { var hLines = this.height/this.unitSize/this.gridSize; var wLines = this.width/this.unitSize/this.gridSize; for(var i = 0; i < hLines; i++) { this.ctx.beginPath(); this.ctx.moveTo(0, i*this.gridSize*this.unitSize-.5); this.ctx.lineTo(this.width, i*this.gridSize*this.unitSize-.5); if (i%5) { this.ctx.strokeStyle = 'rgba(66,66,66,.2)'; } else { this.ctx.strokeStyle = 'rgba(66,66,66,.7)'; } this.ctx.stroke(); this.ctx.closePath(); }; for(var i = 0; i < wLines; i++) { this.ctx.beginPath(); this.ctx.moveTo(i*this.gridSize*this.unitSize-.5, 0); this.ctx.lineTo(i*this.gridSize*this.unitSize-.5, this.height); if (i%5) { this.ctx.strokeStyle = 'rgba(66,66,66,.2)'; } else { this.ctx.strokeStyle = 'rgba(66,66,66,.7)'; } this.ctx.stroke(); this.ctx.closePath(); }; }; GameOfLife.prototype.reset = function() { this.init(); this.gameOn = false; this.counter = 0; this.lifeCell = 0; }; return GameOfLife; })(); var c = document.getElementById('world'); var startButton = document.getElementById('ctrl_1'); var generateWorld = document.getElementById('ctrl_2'); var resetWorld = document.getElementById('ctrl_3'); var infoPanel = document.getElementsByClassName('info-panel')[0]; var infoGeneration = document.getElementById('info_generation'); var infoLifeCell = document.getElementById('info_life-cell'); var infoSpeed = document.getElementById('info_speed'); var gameBox = document.getElementById('game'); var rulesBox = document.getElementById('rules'); var gameOfLife = new GameOfLife({ canvas : c, unitSize : 2, columns : 240, lines : 180, drawRate : 1000/16, gridSize : 4, infoPanel : infoPanel, infoGeneration : infoGeneration, infoLifeCell : infoLifeCell, infoSpeed : infoSpeed, gameBox : gameBox, rulesBox : rulesBox }); gameOfLife.start(); c.addEventListener('click', function(e) { gameOfLife.addUnit(e.offsetX, e.offsetY); }); startButton.addEventListener('click', function(e) { gameOfLife.gg(); }); generateWorld.addEventListener('click', function(e) { gameOfLife.randomDraft(); }); resetWorld.addEventListener('click', function(e) { gameOfLife.reset(); });
Expand for more options Login