Skip to content

Instantly share code, notes, and snippets.

@Kornil
Last active June 15, 2016 17:19
Show Gist options
  • Save Kornil/fd381ab9c684a57c761ad6eda4a52a6b to your computer and use it in GitHub Desktop.
Save Kornil/fd381ab9c684a57c761ad6eda4a52a6b to your computer and use it in GitHub Desktop.
Game of Life in React

Game of Life in React

A full page game of life implementation(I've never seen one done in browser), starts slow but it gains speed :)

A Pen by Francesco Agnoletto on CodePen.

License.

ScreenShot

<div id="app"></div>
// save every alive cells so we can update only them and neighbours
let aliveCells = [],
time = 0;
const rowInt = Math.floor((screen.height / 10) + 1),
colInt = Math.floor((screen.width / 10) + 1),
col = Array(rowInt).fill().map((x, i) => i),
row = Array(colInt).fill().map((x, i) => i);
/* cell component */
var GameCell = React.createClass({
getInitialState: function() {
return {
cellState: 'dead'
};
},
handleClick: function(x, y) { // toggle between alive and dead
let coord = x + "x" + y;
if (this.state.cellState === 'dead') {
this.setState({
cellState: 'alive'
});
aliveCells.push(coord);
} else {
this.setState({
cellState: 'dead'
});
let index = aliveCells.indexOf(coord);
aliveCells.splice(index, 1);
}
},
render: function() {
return (
<td onClick={this.handleClick.bind(this, this.props.k, this.props.i)}
id={this.props.k+'x'+this.props.i}
className={this.state.cellState}></td>
);
}
});
/* random button */
var RandomButton = React.createClass({
randomClick: function() {
let cells = $("td").get();
for (var i = 0; i < cells.length; i++) {
let rand = Math.floor(Math.random() * 10);
if (rand < 2) {
/*$(cells[i]).removeClass("dead");
$(cells[i]).addClass("alive");*/
let cellId = $(cells[i]).attr("id");
document.getElementById(cellId).className = 'alive';
aliveCells.push(cellId);
}
}
},
render: function() {
return (
<i className="fa fa-random" onClick={this.randomClick} aria-hidden="true"></i>
)
}
});
/* play button */
var Buttons = React.createClass({
getInitialState: function(){
return {
time: 0,
running: true
}
},
playClick: function() {
if(this.state.running === true){
// if empty, random
if(aliveCells.length === 0){
$(".fa-random").click();
}
let neighboursArray = [];
for (var j = 0; j < aliveCells.length; j++) {
let coordsCell = aliveCells[j].split("x");
let y = Number(coordsCell[0]),
x = Number(coordsCell[1]);
neighboursArray.push(
(x - 1) + "x" + (y - 1), (x - 1) + "x" + y, (x - 1) + "x" + (y + 1),
x + "x" + (y - 1), x + "x" + y, x + "x" + (y + 1),
(x + 1) + "x" + (y - 1), (x + 1) + "x" + y, (x + 1) + "x" + (y + 1));
}
let uniquesArray = Array.from(new Set(neighboursArray));
let nextGenCells = [],
nextDeadCells = [];
// this is too slow
for (var i = 0; i < uniquesArray.length; i++) {
let neighbours = 0;
let coords = uniquesArray[i].split("x");
let y = Number(coords[0]),
x = Number(coords[1]);
//top
if ($("#" + (x - 1) + "x" + (y - 1)).hasClass("alive")) {neighbours++}
if ($("#" + (x - 1) + "x" + (y)).hasClass("alive")) {neighbours++}
if ($("#" + (x - 1) + "x" + (y + 1)).hasClass("alive")) {neighbours++}
//middle
if ($("#" + (x) + "x" + (y - 1)).hasClass("alive")) {neighbours++}
if ($("#" + (x) + "x" + (y + 1)).hasClass("alive")) {neighbours++}
//bottom
if ($("#" + (x + 1) + "x" + (y - 1)).hasClass("alive")) {neighbours++}
if ($("#" + (x + 1) + "x" + (y)).hasClass("alive")) {neighbours++}
if ($("#" + (x + 1) + "x" + (y + 1)).hasClass("alive")) {neighbours++}
// check each active cell and neighbours, array for dead && array for living
if ((neighbours < 2 || neighbours > 3) && $("#" + x + "x" + y).hasClass("alive")) {
let index = nextDeadCells.indexOf(x+"x"+y);
if (index === -1) {
nextDeadCells.push(x+"x"+y);
}
}
else if ((neighbours == 3 && $("#" + x + "x" + y).hasClass("dead")) ||
(neighbours < 4 || neighbours > 1) && $("#" + x + "x" + y).hasClass("alive")) {
let index = nextGenCells.indexOf(x+"x"+y);
if (index === -1) {
nextGenCells.push(x+"x"+y);
}
}
}
for(var a = 0;a < nextDeadCells.length; a++){
document.getElementById(nextDeadCells[a]).className = "dead";
}
for(var b = 0; b < nextGenCells.length;b++){
document.getElementById(nextGenCells[b]).className = "alive";
}
aliveCells = nextGenCells;
time++;
this.setState({time: time})
//setTimeout(this.playClick);
requestAnimationFrame(this.playClick);
}else{
this.setState({running: true})
$(".fa-start").click();
}
},
pauseClick: function() {
this.setState({running: false})
},
stopClick: function() {
$(".fa-pause").click();
time = 0;
this.setState({time: time})
for (var i = 0; i <= aliveCells.length; i++) {
document.getElementById(aliveCells[i]).className = "dead";
}
aliveCells = [];
},
render: function() {
return (
<div className="menu">
<i className="fa fa-play" onClick={this.playClick} aria-hidden="true"></i>
<i className="fa fa-pause" onClick={this.pauseClick} aria-hidden="true"></i>
<i className="fa fa-stop" onClick={this.stopClick} aria-hidden="true"></i>
<RandomButton />
<p>Gen: {this.state.time}</p>
</div>
)
}
});
/* generate board */
var GenerateBoard = React.createClass({
render: function() {
return (
<table>
{col.map((i) => {
return (
<tr key={i} id={i}>{
row.map((k) => {
return <GameCell key={k} k={k} i={i} />
})}
</tr>
);
})}
</table>
);
}
});
/* main component */
var LifeBoard = React.createClass({
render: function() {
return (
<div className="board">
<Buttons />
<GenerateBoard />
</div>
);
}
});
ReactDOM.render(<LifeBoard />,
document.getElementById('app'));
<script src="https://code.jquery.com/jquery-2.2.4.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
<script src="https://fb.me/react-15.1.0.min.js"></script>
<script src="https://fb.me/react-dom-15.1.0.min.js"></script>
body
overflow: hidden
font-size: 12px
#app, .board
width: 100vw
height: 100vh
background: rgb(20,20,20)
table
table-layout: fixed
width: 100%
height: 100%
.menu
width: 180px
height: 23px
background: #003434
position: fixed
top: 0
right: 0
border-bottom-left-radius: 5px
color: rgb(20,20,20)
i
margin-left: 10px
margin-top: 6px
&:hover
color: #008080
cursor: pointer
p
display: inline-block
margin-left: 10px
font-weight: bold
cursor: default
-webkit-touch-callout: none
-webkit-user-select: none
-khtml-user-select: none
-moz-user-select: none
-ms-user-select: none
user-select: none
td
height: 10px
width: 10px
// 1px real border
border: 1px solid rgb(0,20,20)
border-right: 0
border-bottom: 0
.alive
background: #00a5a5
.dead
background: rgb(20,20,20)
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet" />
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.6.2/css/font-awesome.min.css" rel="stylesheet" />
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment