Conway's Game of Life implementation
Last active
April 22, 2017 15:50
-
-
Save rfprod/34c96dadd916f063495c to your computer and use it in GitHub Desktop.
Game of Life
This file contains hidden or 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
<div class="container-fluid nopadding"> | |
<nav class="navbar navbar-inverse navbar-fixed-top topnav" role="navigation"> | |
<div class="navbar-header"> | |
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#toggle-nav" aria-expanded="false"> | |
<span class="sr-only">Toggle navigation</span> | |
<span class="icon-bar"></span> | |
<span class="icon-bar"></span> | |
<span class="icon-bar"></span> | |
</button> | |
<a class="navbar-brand font-effect-neon" target=_blank href="http://codepen.io/rfprod"><span class="glyphicon glyphicon-wrench"></span> RFProd</a> | |
</div> | |
<div class="collapse navbar-collapse" id="toggle-nav"> | |
<div class="container-fluid"> | |
<ul class="nav navbar-nav navbar-right font-effect-emboss"> | |
<li class="nav-tabs"><a href="#gameoflife"><span class="glyphicon glyphicon-th"></span> GAME OF LIFE</a></li> | |
<li class="nav-tabs"><a href="https://gist.github.com/rfprod/34c96dadd916f063495c" target=_blank><span class="glyphicon glyphicon-download-alt" ></span> GIST</a></li> | |
</ul> | |
</div> | |
</div> | |
</nav> | |
<a name="gameoflife"></a> | |
<div class="home"> | |
<div class="container-fluid"> | |
<div class="col-xs-12 col-sm-12 col-md-12 col-lg-12"> | |
<h2><span class="glyphicon glyphicon-th"></span> Game of Life</h2> | |
<div id="output"> | |
Output | |
</div> | |
<span class="credits">info <a href="https://en.wikipedia.org/wiki/Conway's_Game_of_Life" target=_blank>what is game of life</a><br/>licence <a href="http://www.gnu.org/licenses/gpl-3.0.en.html" target=_blank>GPL 3.0</a></span> | |
</div> | |
</div> | |
</div> | |
</div> |
This file contains hidden or 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
"use strict"; | |
$(document).on('ready',() => { | |
(function refreshAll(){ | |
const ControlsAndGridContainer = React.createClass({ | |
render: () => { | |
return ( | |
<span> | |
<div className="btn-group btn-group-justified"> | |
<a href="#" id="start" className="btn btn-success">Start</a> | |
<a href="#" id="pause" className="btn btn-warning">Pause</a> | |
<a href="#" id="reset" className="btn btn-danger">Reset</a> | |
</div> | |
<div id="random-patterns-selector"> | |
<h4>Random patterns</h4> | |
<div className="btn-group btn-group-justified"> | |
<a href="#" id="gliders" className="pattern-selector btn btn-info active">Gliders</a> | |
<a href="#" id="LWSS1" className="pattern-selector btn btn-info">LWSS1</a> | |
<a href="#" id="LWSS2" className="pattern-selector btn btn-info">LWSS2</a> | |
<a href="#" id="LWSS3" className="pattern-selector btn btn-info">LWSS3</a> | |
</div> | |
</div> | |
<div className="well well-sm">Generations: <span className="gen-counter">0</span> | |
<p id="user-tip">Cells can be added manually while the game plays.</p></div> | |
<table className="table table-bordered" id="grid"></table> | |
</span> | |
); | |
} | |
}); | |
ReactDOM.render(<ControlsAndGridContainer />,document.getElementById('output')); | |
const cellsInRow = 50; | |
const rowsCount = cellsInRow/2; | |
//console.log('cells in a row: '+cellsInRow+' | rows count: '+rowsCount); | |
let markup = [], rowMarkup = []; | |
const Grid = React.createClass({ | |
render: function() { | |
for(let j=0; j<cellsInRow; j++){ | |
rowMarkup.push(<td className="grid-unit" onClick={this.handleClick} id={j}></td>); | |
} | |
for (let i=0, cirHalved=cellsInRow/2; i<cirHalved; i++){ | |
markup.push(<tr className="row" id={i}>{rowMarkup}</tr>); | |
} | |
return ( | |
<tbody>{markup}</tbody> | |
); | |
} | |
}); | |
ReactDOM.render(<Grid />,document.getElementById('grid')); | |
const gridUnits = document.getElementsByClassName('grid-unit'); | |
const patternSelectors = document.getElementsByClassName('pattern-selector'); | |
let deadCells = [], liveCells = []; | |
// init live cells | |
let randomPatternName = "gliders"; | |
let gridUnitClass = undefined, addr = undefined; | |
for (let i=0, gul=gridUnits.length; i<gul; i++){ | |
gridUnits[i].addEventListener('click', function(){ | |
//console.log('clicked: '+this.className+" | row: "+this.parentNode.id+" col: "+this.id); | |
gridUnitClass = this.className; | |
if (gridUnitClass === 'grid-unit') { | |
this.className = 'grid-unit immature'; | |
liveCells.push(this.parentNode.id+"-"+this.id); | |
//console.log(JSON.stringify(liveCells)); | |
} | |
if (gridUnitClass === 'grid-unit immature'){ | |
this.className = 'grid-unit'; | |
addr = this.parentNode.id+"-"+this.id; | |
liveCells.splice(liveCells.indexOf(addr),1); | |
//console.log(JSON.stringify(liveCells)); | |
} | |
if (liveCells.length === 0){ | |
document.getElementById("random-patterns-selector").className = "visible"; | |
}else{ | |
document.getElementById("random-patterns-selector").className = "hidden"; | |
} | |
}); | |
} | |
let patternSelectorID = undefined; | |
for (let i=0, psl1=patternSelectors.length; i<psl1; i++){ | |
patternSelectors[i].addEventListener('click', function(){ | |
patternSelectorID = this.id; | |
this.className = 'pattern-selector btn btn-info active'; | |
for (let j=0, psl2=patternSelectors.length; j<psl2; j++){ | |
if (patternSelectors[j].id != patternSelectorID) patternSelectors[j].className = "pattern-selector btn btn-info"; | |
} | |
//console.log('selected pattern name: '+patternSelectorID); | |
randomPatternName = patternSelectorID; | |
}); | |
} | |
let cellsAddresses = [], neighboursAddresses = []; | |
let survivedCells = [], revivedCells = []; | |
function changeCellsStates(){ | |
// init dead and revived cells | |
deadCells.length = 0; | |
revivedCells.length = 0; | |
for (let cai=0, cal=cellsAddresses.length; cai<cal; cai++){ | |
deadCells.push(cellsAddresses[cai]); | |
} | |
let liveCellIndex = undefined; | |
for (let lci=0, lcl=liveCells.length; lci<lcl; lci++){ | |
liveCellIndex = deadCells.indexOf(liveCells[lci]); | |
if (liveCellIndex != -1) { | |
if (liveCellIndex == deadCells.length - 1) deadCells.pop(); | |
else if (liveCellIndex == 0) deadCells.shift(); | |
else deadCells.splice(liveCellIndex,1); | |
} | |
} | |
// raise dead - find index of cell address to revive if it has 3 alive neighbours | |
let raisingCellAddr = undefined, counter = undefined; | |
for (let nai = 0, nal = neighboursAddresses.length; nai<nal; nai++){ | |
counter = 0; | |
for (let naEli=0, naEll=neighboursAddresses[nai].length;naEli<naEll;naEli++){ | |
if (liveCells.indexOf(neighboursAddresses[nai][naEli]) != -1) counter++; | |
} | |
if (counter == 3){ | |
raisingCellAddr = cellsAddresses[nai].split("-"); | |
//console.log('neighboursAddresses: '+JSON.stringify(neighboursAddresses[wr])); | |
//console.log('neighbour address: '+neighboursAddresses[wr][wrel]+' cellsAddresses index: '+wr+' live neighbours counter: '+counter); | |
document.getElementsByClassName("row")[raisingCellAddr[0]].childNodes.item(raisingCellAddr[1]).className = "grid-unit immature"; | |
revivedCells.push(cellsAddresses[nai]); | |
} | |
} | |
// init survided cells | |
survivedCells.length = 0; | |
// kill live cells | |
let element = undefined, | |
cellAddr = undefined, | |
indexOfalive = undefined, | |
aliveNeighboursCounter = undefined, | |
currentNeighbours = undefined; | |
for (let index=0, lcln=liveCells.length; index<lcln; index++){ | |
element = liveCells[index]; | |
//console.log('element: '+element); | |
cellAddr = element.split("-"); | |
indexOfalive = cellsAddresses.indexOf(element); | |
//console.log('indexOfalive: '+indexOfalive); | |
if (indexOfalive != -1) { | |
aliveNeighboursCounter = 0; | |
currentNeighbours = neighboursAddresses[indexOfalive]; | |
//console.log('currentNeighbours: '+JSON.stringify(currentNeighbours)); | |
for (let h=0, cnl=currentNeighbours.length; h<cnl; h++){ | |
// indexOfAliveNeighbour | |
if (liveCells.indexOf(currentNeighbours[h]) != -1){ | |
aliveNeighboursCounter++; | |
} | |
} | |
if (aliveNeighboursCounter < 2){ | |
//console.log('underpopulation dying cell address: '+cellAddr); | |
document.getElementsByClassName("row")[cellAddr[0]].childNodes.item(cellAddr[1]).className = "grid-unit"; | |
}else | |
if (aliveNeighboursCounter == 2 || aliveNeighboursCounter == 3){ | |
//console.log('cell lives to next generation: '+cellAddr); | |
survivedCells.push(element); | |
}else | |
if (aliveNeighboursCounter > 3){ | |
//console.log('overpopulation dying cell address: '+cellAddr); | |
document.getElementsByClassName("row")[cellAddr[0]].childNodes.item(cellAddr[1]).className = "grid-unit"; | |
} | |
//console.log('survivedCells: '+JSON.stringify(survivedCells)); | |
//console.log('aliveNeighboursCounter: '+aliveNeighboursCounter); | |
} | |
} | |
// update live cells | |
liveCells.length = 0; | |
for (let sci=0, scl=survivedCells.length; sci<scl; sci++){ | |
liveCells.push(survivedCells[sci]); | |
} | |
for (let rcli=0, rclil=revivedCells.length; rcli<rclil; rcli++){ | |
liveCells.push(revivedCells[rcli]); | |
} | |
//console.log('liveCells: '+JSON.stringify(liveCells)); | |
} | |
function getGridAddresses(){ | |
if (cellsAddresses.length === 0 || neighboursAddresses.length === 0){ | |
let cell = undefined, | |
neighbourRows = undefined, | |
neighbourColumns = undefined, | |
tmpNeighboursAddresses = undefined, | |
rowElmnt = undefined, | |
colElmnt = undefined, | |
neighbour = undefined; | |
for (let r=0; r<rowsCount; r++){ // rows | |
for (let c=0; c<cellsInRow; c++){ // columns | |
cell = document.getElementsByTagName('tr')[r].getElementsByTagName('td')[c]; | |
neighbourRows = [], neighbourColumns = []; | |
if (r === 0) neighbourRows = [rowsCount-1,r,r+1]; | |
else if (r === rowsCount-1) neighbourRows = [r-1,r,0]; | |
else neighbourRows = [r-1,r,r+1]; | |
if (c === 0) neighbourColumns = [cellsInRow-1,c,c+1]; | |
else if (c === cellsInRow-1) neighbourColumns = [c-1,c,0]; | |
else neighbourColumns = [c-1,c,c+1]; | |
tmpNeighboursAddresses = []; | |
for (let y=0, yl=neighbourRows.length; y<yl; y++){ | |
rowElmnt = neighbourRows[y]; | |
for (let z=0, zl=neighbourColumns.length; z<zl; z++){ | |
colElmnt = neighbourColumns[z]; | |
neighbour = document.getElementsByTagName('tr')[rowElmnt].getElementsByTagName('td')[colElmnt]; | |
tmpNeighboursAddresses.push(neighbour.parentNode.id+"-"+neighbour.id); | |
} | |
} | |
cellsAddresses.push(tmpNeighboursAddresses[4]); | |
tmpNeighboursAddresses.splice(4,1); // exclude cell itself, consider only neighbours | |
neighboursAddresses.push(tmpNeighboursAddresses); | |
} | |
} | |
} | |
} | |
getGridAddresses(); | |
function autoInitLiveCells(arrayPointer){ | |
const fiveGliders = ['2-0','2-1','2-2','1-2','0-1','2-10','2-11','2-12','1-12','0-11','2-20','2-21','2-22','1-22','0-21','2-30','2-31','2-32','1-32','0-31','2-40','2-41','2-42','1-42','0-41']; | |
const threeLWSS1 = ['0-0','0-3','1-4','2-0','2-4','3-1','3-2','3-3','3-4','0-10','0-13','1-14','2-10','2-14','3-11','3-12','3-13','3-14','0-20','0-23','1-24','2-20','2-24','3-21','3-22','3-23','3-24']; | |
const threeLWSS2 = ['0-0','0-3','1-4','2-0','2-4','3-1','3-2','3-3','3-4','15-0','15-3','16-4','17-0','17-4','18-1','18-2','18-3','18-4']; | |
const threeLWSS3 = ['0-0','0-3','1-4','2-0','2-4','3-1','3-2','3-3','3-4','10-0','10-3','11-4','12-0','12-4','13-1','13-2','13-3','13-4','20-0','20-3','21-4','22-0','22-4','23-1','23-2','23-3','23-4']; | |
const figuresObject = {'gliders':fiveGliders,'LWSS1':threeLWSS1,'LWSS2':threeLWSS2,'LWSS3':threeLWSS3}; | |
let figureCellAddr = undefined; | |
for (let ic=0, icl=figuresObject[arrayPointer].length; ic<icl; ic++){ | |
figureCellAddr = figuresObject[arrayPointer][ic].split("-"); | |
liveCells.push(figuresObject[arrayPointer][ic]); | |
document.getElementsByClassName("row")[figureCellAddr[0]].childNodes.item(figureCellAddr[1]).className = "grid-unit immature"; | |
} | |
} | |
let turn = false, generation = 0, generationsCounter = $('.gen-counter'); | |
function startGame(){ | |
if (turn === false){ | |
if (generation === 0) console.log('game started'); | |
else console.log('game continued'); | |
document.getElementById("random-patterns-selector").className = "hidden"; | |
let turnTimeout = 250; | |
turn = setInterval(function(){ | |
generation++; | |
if (generation >= 150 && generation < 250) turnTimeout = 275; | |
if (generation >= 250 && generation < 350) turnTimeout = 300; | |
if (generation >= 350 && generation < 450) turnTimeout = 325; | |
if (generation >= 450 && generation < 550) turnTimeout = 350; | |
if (generation >= 550 && generation < 650) turnTimeout = 375; | |
if (generation >= 650 && generation < 750) turnTimeout = 400; | |
if (generation >= 750 && generation < 850) turnTimeout = 425; | |
if (generation >= 850 && generation < 950) turnTimeout = 450; | |
if (generation >= 950 && generation < 1050) turnTimeout = 475; | |
if (generation >= 1050) turnTimeout = 500; | |
generationsCounter.html(generation); | |
if (liveCells.length === 0) { | |
if (generation > 1) { | |
alert("GAME OVER: EVERYONE'S DEAD"); | |
console.log('game reset'); | |
clearInterval(turn); | |
turn = false; | |
generation = 0; | |
liveCells.length = 0; | |
deadCells.length = 0; | |
revivedCells.length = 0; | |
generationsCounter.html(generation); | |
document.getElementById("random-patterns-selector").className = "visible"; | |
}else autoInitLiveCells(randomPatternName); | |
} | |
changeCellsStates(); | |
},turnTimeout); | |
}else console.log('gameplay is already in progress'); | |
} | |
function pauseGame(){ | |
console.log('game paused'); | |
clearInterval(turn); | |
turn = false; | |
} | |
function resetGame(){ | |
console.log('game reset'); | |
clearInterval(turn); | |
turn = false; | |
generation = 0; | |
liveCells.length = 0; | |
deadCells.length = 0; | |
revivedCells.length = 0; | |
generationsCounter.html(generation); | |
document.getElementById("random-patterns-selector").className = "visible"; | |
for (let i=0, guln=gridUnits.length; i<guln; i++){ | |
gridUnits[i].className = 'grid-unit'; | |
} | |
} | |
$('#start').on('click', startGame); | |
$('#pause').on('click', pauseGame); | |
$('#reset').on('click', resetGame); | |
})(); | |
}); |
This file contains hidden or 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
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script> | |
<script src="//cdnjs.cloudflare.com/ajax/libs/react/0.14.7/react.min.js"></script> | |
<script src="//cdnjs.cloudflare.com/ajax/libs/react/0.14.7/react-dom.js"></script> | |
<script src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script> |
This file contains hidden or 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
$black: #000000 | |
$white: #ffffff | |
$lightred: #ffcccc | |
$red: #ff0000 | |
body | |
color: $black | |
font-family: 'Play', sans-serif | |
font-size: 2.2em | |
overflow-x:hidden | |
.nopadding | |
padding: 0 | |
.navbar-brand | |
font-size: 1em | |
.home | |
min-height: 100vh | |
padding-top: 8vh | |
height: auto !important | |
h2 | |
text-align: center | |
font-weight: bold | |
#output | |
text-align: center | |
width: 100% | |
height: auto !important | |
#user-tip | |
font-size: 0.75em | |
.hidden | |
display: none | |
#grid | |
background-color: $black | |
.row | |
.grid-unit | |
width: 5px | |
height: 5px | |
padding: 0px | |
.immature | |
background-color: $lightred | |
.mature | |
background-color: $red | |
.grid-unit:hover | |
background-color: $white | |
a | |
text-decoration: none | |
.credits | |
display: block | |
text-align: center | |
font-size: 0.75em | |
a:hover | |
text-decoration: none |
This file contains hidden or 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
<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet" /> | |
<link href="//cdnjs.cloudflare.com/ajax/libs/animate.css/3.2.3/animate.min.css" rel="stylesheet" /> | |
<link href="https://fonts.googleapis.com/css?family=Play&effect=neon" rel="stylesheet" /> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment