Skip to content

Instantly share code, notes, and snippets.

@blvrd
Created August 1, 2016 14:51
Show Gist options
  • Save blvrd/758d7d4f9b8630488fddd12190315de1 to your computer and use it in GitHub Desktop.
Save blvrd/758d7d4f9b8630488fddd12190315de1 to your computer and use it in GitHub Desktop.
Game of Life
var gameOfLife = {
width: 12,
height: 12,
stepInterval: 100,
playing: false,
intervalObject: null,
createAndShowBoard: function(state) {
// create <table> element
var goltable = document.createElement("tbody");
// build Table HTML
var tablehtml = '';
for (var h=0; h<this.height; h++) {
tablehtml += "<tr id='row+" + h + "'>";
for (var w=0; w<this.width; w++) {
if (state && state[h][w] === 'O') {
tablehtml += "<td data-status='alive' class='alive' id='" + w + "-" + h + "'></td>";
} else {
tablehtml += "<td data-status='dead' id='" + w + "-" + h + "'></td>";
}
}
tablehtml += "</tr>";
}
goltable.innerHTML = tablehtml;
// add table to the #board element
var board = document.getElementById('board');
var oldgoltable = document.getElementsByTagName('tbody')[0]
if (oldgoltable) {
board.replaceChild(goltable, oldgoltable);
} else {
board.appendChild(goltable);
}
// once html elements are added to the page, attach events to them
this.setupBoardEvents();
},
forEachCell: function (iteratorFunc) {
/*
Write forEachCell here. You will have to visit
each cell on the board, call the "iteratorFunc" function,
and pass into func, the cell and the cell's x & y
coordinates. For example: iteratorFunc(cell, x, y)
*/
var cells = [].slice.call(document.getElementsByTagName('td'))
cells.forEach(function(cell) {
iteratorFunc.call(gameOfLife, cell)
})
},
setupBoardEvents: function() {
// each board cell has an CSS id in the format of: "x-y"
// where x is the x-coordinate and y the y-coordinate
// use this fact to loop through all the ids and assign
// them "on-click" events that allow a user to click on
// cells to setup the initial state of the game
// before clicking "Step" or "Auto-Play"
// clicking on a cell should toggle the cell between "alive" & "dead"
// for ex: an "alive" cell be colored "blue", a dead cell could stay white
// EXAMPLE FOR ONE CELL
// Here is how we would catch a click event on just the 0-0 cell
// You need to add the click event on EVERY cell on the board
var onCellClick = function (e) {
// QUESTION TO ASK YOURSELF: What is "this" equal to here?
// how to set the style of the cell when it's clicked
if (e.target.dataset.status === 'dead') {
this.setCellAlive(e.target)
} else {
this.setCellDead(e.target)
}
};
var tbody = document.getElementsByTagName('tbody')[0];
tbody.onclick = onCellClick.bind(this);
var stepButton = document.getElementById('step_btn')
stepButton.onclick = this.step.bind(this)
var playButton = document.getElementById('play_btn')
playButton.onclick = this.enableAutoPlay.bind(this)
var clearButton = document.getElementById('clear_btn')
clearButton.onclick = this.clear.bind(this)
var randomButton = document.getElementById('reset_btn')
randomButton.onclick = this.resetRandom.bind(this)
var uploadButton = document.getElementById('uploader')
uploadButton.onchange = this.handleFileUpload.bind(this)
},
setCellAlive: function(cell) {
cell.className = 'alive'
cell.dataset.status = 'alive'
cell.dataset.nextStatus = null
},
setCellDead: function(cell) {
cell.className = 'dead'
cell.dataset.status = 'dead'
cell.dataset.nextStatus = null
},
getCoordinates: function(cell) {
var coordinates = {}
var coordinateArrary = cell.id.split('-')
coordinates.x = parseInt(coordinateArrary[0])
coordinates.y = parseInt(coordinateArrary[1])
return coordinates
},
setNextCellState: function(cell) {
var aliveNeighborsCount = this.getLiveNeighborsCount(cell)
var aliveWithLessThanTwoNeighbors = cell.dataset.status === 'alive' && aliveNeighborsCount < 2
var aliveWithGreaterThanThreeNeighbors = aliveNeighborsCount > 3 && cell.dataset.status === 'alive'
var aliveWithTwoOrThreeNeighbors = cell.dataset.status === 'alive' && (aliveNeighborsCount === 2 || aliveNeighborsCount === 3)
var deadWithThreeNeighbors = cell.dataset.status === 'dead' && aliveNeighborsCount === 3
if (aliveWithLessThanTwoNeighbors || aliveWithGreaterThanThreeNeighbors) {
cell.dataset.nextStatus = 'dead'
} else if (aliveWithTwoOrThreeNeighbors || deadWithThreeNeighbors) {
cell.dataset.nextStatus = 'alive'
}
},
getLiveNeighborsCount(cell) {
var coordinates = this.getCoordinates(cell)
var neighbors = []
neighbors.push(this.getCellValue(coordinates.x, coordinates.y-1))
neighbors.push(this.getCellValue(coordinates.x+1, coordinates.y-1))
neighbors.push(this.getCellValue(coordinates.x+1, coordinates.y))
neighbors.push(this.getCellValue(coordinates.x+1, coordinates.y+1))
neighbors.push(this.getCellValue(coordinates.x, coordinates.y+1))
neighbors.push(this.getCellValue(coordinates.x-1, coordinates.y+1))
neighbors.push(this.getCellValue(coordinates.x-1, coordinates.y))
neighbors.push(this.getCellValue(coordinates.x-1, coordinates.y-1))
return neighbors.filter(function(cell) {
return cell === 1
}).length
},
getCellValue: function(x, y) {
var cell = document.getElementById(x + '-' + y)
if (cell !== null && cell.dataset.status === 'alive') {
return 1
} else {
return 0
}
},
updateCell: function(cell) {
if (cell.dataset.nextStatus === 'alive') {
this.setCellAlive(cell)
} else {
this.setCellDead(cell)
}
},
step: function () {
// Here is where you want to loop through all the cells
// on the board and determine, based on it's neighbors,
// whether the cell should be dead or alive in the next
// evolution of the game.
//
// You need to:
// 1. Count alive neighbors for all cells
// 2. Set the next state of all cells based on their alive neighbors
this.forEachCell(this.setNextCellState)
this.forEachCell(this.updateCell)
},
enableAutoPlay: function () {
// Start Auto-Play by running the 'step' function
// automatically repeatedly every fixed time interval
if (this.playing === false) {
this.playing = true
this.intervalObject = setInterval(this.step.bind(this), this.stepInterval)
} else {
this.playing = false
this.pause()
}
},
pause: function() {
clearInterval(this.intervalObject)
},
clear: function() {
this.pause()
this.forEachCell(this.setCellDead)
},
resetRandom: function() {
this.pause()
this.forEachCell(function(cell) {
if (Math.random() > 0.5) {
this.setCellAlive(cell)
} else {
this.setCellDead(cell)
}
})
},
handleFileUpload: function(e) {
var file = e.target.files[0]
var reader = new FileReader()
reader.onload = function(event) {
console.log(event.target.result)
var rows = event.target.result.split('\n').splice(2)
this.width = rows[0].length
this.height = rows.length
this.createAndShowBoard(rows)
}.bind(this)
reader.readAsText(file)
}
};
gameOfLife.createAndShowBoard();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment