Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save linuxenko/f33d12b336d356b34372 to your computer and use it in GitHub Desktop.
Save linuxenko/f33d12b336d356b34372 to your computer and use it in GitHub Desktop.
Build the Game of Life [freeCodeCamp [Data Visualization]] (Challenge)

Build the Game of Life [freeCodeCamp [Data Visualization]] (Challenge)

If a cell (whether ON or OFF) has exactly 2 or 3 ON cells in the 8 cells surrounding it, then that cell becomes (or remains) ON. If an ON cell has fewer than 2 cells surrounding it as described above, it becomes OFF. If an ON cell has more than 3 cells surrounding it, it becomes OFF.

A Pen by Svetlana Linuxenko on CodePen.

License.

const ALIVE = 1;
const EMPTY = 0;
const PULSAR = [
[0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0],
[0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0],
[1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1],
[1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1],
[0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0],
[0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0],
[0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0],
[1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1],
[1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1],
[0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0],
[0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0]
];
class Board {
constructor(sizeX, sizeY) {
if (arguments.length < 1) {
throw new Error('You forget about board size again..');
}
this.width = sizeX;
this.height = sizeY;
this.generation = 0;
this.board = [];
this.refill();
}
refill() {
this.generation = 0;
for (let y = 0; y < this.height; y++) {
this.board[y] = [];
for (let x = 0; x < this.width; x++) {
this.board[y][x] = EMPTY;
}
}
}
randomize() {
this.board.map((row, y) => {
return row.map((col, x) => {
let point = this.checkPoint(x, y);
if (!!Math.round(Math.random()*2)) {
this.board[y][x] = !!Math.round(Math.random()*2)
? ALIVE : EMPTY;
}
});
});
return this.board;
}
createPattern() {
let startY = Math.round(this.height / 2 - PULSAR.length / 2);
let startX = Math.round(this.width / 2 - PULSAR.length / 2);
PULSAR.map((row, y) => {
row.map((col, x) => {
this.board[startY + y][startX + x] = row[x] === 0 ? EMPTY : ALIVE;
});
});
return this;
}
/*
If a cell (whether ON or OFF) has exactly 2 or 3 ON cells in the 8 cells surrounding it, then that cell becomes (or remains) ON.
If an ON cell has fewer than 2 cells surrounding it as described above, it becomes OFF.
If an ON cell has more than 3 cells surrounding it, it becomes OFF.
Any live cell with fewer than two live neighbours dies, as if caused by under-population.
Any live cell with two or three live neighbours lives on to the next generation.
Any live cell with more than three live neighbours dies, as if by over-population.
Any dead cell with exactly three live neighbours becomes a live cell, as if by reproduction.
*/
checkPoint(cx, cy) {
const x = cx;
const y = cy;
const ni = this.pointNeighbors(x, y);
const neighbors = ni.reduce((p,c) => c === ALIVE ? p + 1 : p, 0);
if (neighbors < 2) {
return EMPTY;
}
if (neighbors == 2) {
return this.board[y][x];
}
if (neighbors == 3) {
return ALIVE;
}
if (neighbors > 3) {
return EMPTY;
}
}
pointNeighbors(cx ,cy) {
let neighbors = new Array(8);
const x = Number.parseInt(cx, 10);
const y = Number.parseInt(cy, 10);
const w = this.width - 1;
const h = this.height - 1;
let shiftX = [x-1, x, x+1], shiftY = [y - 1, y + 1];
if (x === 0) { shiftX[0] = w; shiftX[1] = 0; shiftX[2] = 1; }
if (x === w) { shiftX[0] = w - 1; shiftX[1] = w; shiftX[2] = 0; }
if (y === 0) { shiftY[0] = h; shiftY[1] = 1; }
if (y === h) { shiftY[0] = 0; shiftY[1] = h -1; }
neighbors[3] = this.board[y][shiftX[0]];
neighbors[4] = this.board[y][shiftX[2]];
neighbors[0] = this.board[shiftY[0]][shiftX[0]];
neighbors[1] = this.board[shiftY[0]][shiftX[1]];
neighbors[2] = this.board[shiftY[0]][shiftX[2]];
neighbors[5] = this.board[shiftY[1]][shiftX[0]];
neighbors[6] = this.board[shiftY[1]][shiftX[1]];
neighbors[7] = this.board[shiftY[1]][shiftX[2]];
return neighbors;
}
next() {
let mutations = 0;
this.board = this.board.map((row, y) => {
return row.map((col, x) => {
let point = this.checkPoint(x, y);
if (this.board[y][x] !== point) {
mutations++;
}
return point;
});
});
if (mutations > 0) {
this.generation++;
}
return this.board;
}
}
class Screen extends React.Component {
constructor() {
super();
this.width = 29;
this.height = 25;
this.interval = null;
this.state = {
playing : false,
board : [],
generation : 0
};
}
componentDidMount() {
this.board = new Board(this.width, this.height);
//this.board.createPattern();
this.setState({ board : this.board.randomize() });
this.onStart();
}
onStart() {
if (this.interval !== null) {
return;
}
this.setState({playing : true});
this.interval = setInterval(() => {
this.setState({
board : this.board.next(),
generation : this.board.generation
});
}, 210);
}
onRandom() {
this.setState({ board : this.board.randomize() });
this.onStart();
}
onPattern() {
this.onClear();
this.board.createPattern();
this.setState({
board : this.board.board
});
}
onStop() {
this.setState({playing: false});
clearInterval(this.interval);
this.interval = null;
}
onClear() {
this.board.refill();
this.setState({
board : this.board.board,
generation: 0
});
}
selectItem(e) {
let idx = e.target.getAttribute('data-idx');
if (idx !== null) {
let x = Number.parseInt(idx.split(',')[0], 10);
let y = Number.parseInt(idx.split(',')[1], 10);
this.state.board[y][x] = ALIVE;
this.setState({ board : this.state.board });
}
}
representBoard() {
const classes = ['item-empty', 'item-alive'];
let items = [];
for (let y = 0; y < this.state.board.length; y++) {
let ys = [];
for (let x = 0; x < this.state.board[y].length; x++) {
let cls = classes[this.state.board[y][x]];
let idx = x +','+ y;
ys.push(
<div className={cls} data-idx={idx}></div>
);
}
items.push(
<div className="items-row">
{ys}
</div>
);
}
return items;
}
render() {
return (
<div className="board-wrapper">
<div className="board-container">
<div className="board-controls text-center">
<h2 className="pull-left">GoL</h2>
<span className="buttons-control">
{this.state.playing ?
<span className="control"
onClick={this.onStop.bind(this)}>
<i className="fa fa-pause"></i>
</span>
:
<span className="control"
onClick={this.onStart.bind(this)}>
<i className="fa fa-play"></i>
</span>
}
<span className="control addon"
onClick={this.onClear.bind(this)}>
<i className="fa fa-times"></i>
</span>
<span className="control addon"
onClick={this.onRandom.bind(this)}>
<i className="fa fa-refresh"></i>
</span>
<span className="control addon"
onClick={this.onPattern.bind(this)}>
<i className="fa fa-puzzle-piece"></i>
</span>
</span>
<h2 className="generation pull-right">{this.state.generation}</h2>
</div>
<div className="board" onClick={this.selectItem.bind(this)}>
{this.representBoard.call(this)}
</div>
</div>
<div className="copy"><a href="http://www.linuxenko.pro">&copy; Svetlana Linuxenko</a></div>
</div>
)
}
}
React.render(<Screen />, document.body);
<script src="//cdnjs.cloudflare.com/ajax/libs/react/0.14.3/react.min.js"></script>
$item-size : 16px
$item-alive : #fff
body,html
height: 100%
width: 100%
background-image: linear-gradient(transparent, #eee, transparent)
background-size: 5px
.board-wrapper
height: 100%
display: flex
flex-direction: column
.copy
display: flex
align-items: flex-end
justify-content: center
padding: 10px 0px
.board-container
display: flex
flex-direction: column
margin: auto
box-shadow: 0px 1px 8px #444
background: #924da3
border-radius: 5px
.board-controls
position: relative
padding: 5px 20px
border-bottom: 1px dotted #aaa
h2
color: #fff
.generation
position: absolute
top: 5px
right: 10px
//font-size: 28px
color: #fff
.buttons-control
display: inline-block
text-align: center
margin-top: 10px
.control
opactiy: 0.8
color: #fff
margin-right: 30px
font-size: 38px
cursor: pointer
.control.addon
position: relative
margin-right: 10px
top: -5px
font-size: 22px
.fa-times,.fa-puzzle-piece
font-size: 26px
.control:hover
opacity: 1
text-shadow: 0px 0px 1px #ddd
.board
.items-row
height: $item-size
padding: 0px
margin: 0px
display: block
white-space: nowrap
div
display: inline-block
width: $item-size
height: $item-size
border-bottom: 1px dotted #aaa
border-left: 1px dotted #aaa
div.item-alive
background: $item-alive
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet" />
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/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