diff --git a/.gitignore b/.gitignore index e920c16..5ea2c4b 100644 --- a/.gitignore +++ b/.gitignore @@ -23,8 +23,9 @@ coverage # Compiled binary addons (http://nodejs.org/api/addons.html) build/Release -# Dependency directory +# Dependency directories node_modules +bower_components # Optional npm cache directory .npm diff --git a/app/app.controller.js b/app/app.controller.js new file mode 100644 index 0000000..a643f84 --- /dev/null +++ b/app/app.controller.js @@ -0,0 +1,23 @@ + + +game.controller("app", ['$scope', '$window', 'cellFactory', 'gameFactory', function($scope, $window, Cell, gameOfLife){ + $scope.gameOfLife = gameOfLife; + var FileReader = new $window.FileReader() + // document.getElementById('step_btn').addEventListener('click', $scope.gameOfLife.step.bind($scope.gameOfLife)) + // document.getElementById('clear_btn').addEventListener('click', $scope.gameOfLife.clear.bind($scope.gameOfLife)) + // document.getElementById('play_btn').addEventListener('click', $scope.gameOfLife.enableAutoPlay.bind($scope.gameOfLife)) + // document.getElementById('reset_btn').addEventListener('click', $scope.gameOfLife.randomize.bind($scope.gameOfLife)) + document.getElementById('submit_file').addEventListener('click', function() { + var file = document.getElementById('input').files[0], + content = FileReader.readAsText(file) + while (FileReader.readyState !== 2) { + console.log(FileReader.readyState) + } + console.log(content.result) + }) + + $scope.gameOfLife.createAndShowBoard(); + + + +}]); diff --git a/app/components/game/controls/buttons/button.directives.js b/app/components/game/controls/buttons/button.directives.js new file mode 100644 index 0000000..8179aa6 --- /dev/null +++ b/app/components/game/controls/buttons/button.directives.js @@ -0,0 +1,98 @@ + +game.directive("clear", function(forEachCell, cellFactory){ + return { + restrict: "E", + replace: true, + template: "", + link: function(scope){ + scope.clear = function(){ + forEachCell( function(cell){ + debugger; + let cellObj = angular.element(cell).scope() + cellObj.$$childHead.cellObj.makeDead(true)} + )} + } + } +}) + +game.directive("pause", function(){ + return { + restrict: "E", + replace: true, + template: "" + } +}) + +game.directive("play", ['gameFactory', function(game){ + return { + restrict: "E", + replace: true, + template: ``, + link: function(scope, playBtn){ + scope.autoPlayOn = false; + scope.intervalName = 'playPause'; + scope.enableAutoPlay = function(){ + if (scope.autoPlayOn) { + scope.autoPlayOn = false + clearInterval(scope.setIntervalID) + playBtn[0].innerHTML = 'Play' + } else { + debugger; + scope.autoPlayOn = true + scope.setIntervalID = setInterval(function(){game.step()}, document.getElementById('step_amount').value || 100) + playBtn[0].innerHTML = 'Pause' + } + } + } + } +}]) + +game.directive("reset", function(forEachCell){ + return { + restrict: "E", + replace: true, + template: "", + link: function(scope){ + scope.random = function () { + forEachCell(function (cell, x, y) { + debugger; + let cellObj = angular.element(document.getElementById( x + '-' + y)).scope().$$childHead.cellObj; + var state = Math.floor(Math.random() * 2) + if (state === 1) { + cellObj.makeDead(true); + } else { + cellObj.makeAlive(true); + } + }) + + } + } + } +}) + +game.directive("save", function(){ + return { + restrict: "E", + replace: true, + template: "" + } +}) + +game.directive("step", ['gameFactory', function(gameFactory){ + return { + restrict: "E", + replace: true, + template: "", + link: function(scope){ + scope.step = gameFactory.step; + } + } +}]) + +game.directive("submit", function(){ + return { + restrict: "E", + replace: true, + template: "" + } +}) diff --git a/app/components/game/controls/controlPanel/controlPanel.directive.js b/app/components/game/controls/controlPanel/controlPanel.directive.js new file mode 100644 index 0000000..b1460b3 --- /dev/null +++ b/app/components/game/controls/controlPanel/controlPanel.directive.js @@ -0,0 +1,22 @@ +'use strict'; +game.directive("controlPanel", function(){ + return { + restrict: "E", + replace: true, + template: controlPanelTemplate() + } +}) + + +function controlPanelTemplate(){ + return ` +
+
+ ` +} diff --git a/app/components/game/game.factory.js b/app/components/game/game.factory.js new file mode 100644 index 0000000..32f26c9 --- /dev/null +++ b/app/components/game/game.factory.js @@ -0,0 +1,344 @@ + + + +var color = null + +var synth = T("OscGen", (T("reverb", { + room: 0.95, + damp: 0.1, + mix: 0.75 + }, T("sin", {mul:0.25})))).play(); + +// let currentStep = 0; + +game.factory('gameFactory',['synthFactory', 'stepFactory', 'forEachCell', function(synth, step, forEachCell){ + return { + width: 50, + height: 50, + cellCount: 2500, + stepInterval: null, + autoPlayOn: false, + setIntervalID: 0, + currentStep: 0, + + createAndShowBoard: function () { + // create element + // var goltable = document.createElement("tbody"); + + // // build Table HTML + // var tablehtml = ''; + // for (var h = 0; h < this.height; h++) { + // tablehtml += ""; + // for (var w = 0; w < this.width; w++) { + // tablehtml += ""; + // } + // tablehtml += ""; + // } + // goltable.innerHTML = tablehtml; + + // // add table to the #board element + // var board = document.getElementById('board'); + // 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 board = document.getElementById('board').firstChild + // Array.prototype.slice.call(board.children).forEach(function (row) { + // Array.prototype.slice.call(row.children).forEach(function (cell) { + // var coords = cell.id.split("-") + // var x = parseInt(coords[0]) + // var y = parseInt(coords[1]) + // iteratorFunc(cell, x, y) + // }) + // }) + // + // }, + + 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 (this.getAttribute('data-status') == 'dead') { + this.className = "alive"; + this.setAttribute('data-status', 'alive'); + this.setAttribute('style', 'background-color:' + color) + // console.log(color) + } else { + this.className = "dead"; + this.setAttribute('data-status', 'dead'); + this.setAttribute('style', 'background-color:#FFFFFF') + } + }; + forEachCell(function (cell) { + cell.onclick = onCellClick + }) + // var cell00 = document.getElementById('0-0'); + // cell00.onclick = onCellClick; +}, + +// sound: function (sines, interval) { +// var env = T("adsr", { +// d: 3000, +// s: 0, +// r: 600 +// }); +// +// T("perc", { +// r: interval * 0.5 +// }, T("reverb", { +// room: 0.95, +// damp: 0.1, +// mix: 0.75 +// }, sines)).on("ended", function () { +// this.pause(); +// }).bang().play(); +// }, + +// makeSine: function (cell) { +// return T("saw", { +// freq: pentatonicFrequencies[freq], +// mul: amplitude +// }); +// }, + +step: step, +// step: function () { +// color = '#'+Math.floor(Math.random()*16777215).toString(16) +// // console.log(this.color) +// // 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 +// // (x, y) -> (1, 1) +// // check: +// // (0, 0) , (1, 0) , (2, 0) +// // (0, 1) , , (2, 1) +// // (0, 2) , (1, 2) , (2, 2) +// // +// // (x-1, y-1), ( x , y-1), (x+1, y-1) +// // (x-1, y ), ( x , y ), (x+1, y ) +// // (x-1, y+1), ( x , y+1), (x+1, y+1) +// // +// // +// var self = this; +// var start = Date.now(), +// cells = [], +// boardWidth = this.width, +// boardHeight = this.height +// this.forEachCell(function (cell, x, y) { +// // console.log(cell) +// var cellObj = document.getElementById(x + '-' + y); +// for (var i = cellObj.x - 1; i < cellObj.x + 2; i++) { +// // if (i < 0 || i > boardWidth - 1) continue +// var col = i < 0 ? boardWidth - 1 : i % boardWidth +// for (var j = cellObj.y - 1; j < cellObj.y + 2; j++) { +// if (i === cellObj.x && j === cellObj.y) continue +// var row = j < 0 ? boardHeight - 1 : j % boardHeight +// var currCell = document.getElementById(col + '-' + row) +// if (currCell.dataset.status === 'alive') cellObj.aliveCount++ +// if (currCell.dataset.status === 'dead') cellObj.deadCount++ +// } +// } +// cells.push(cellObj) +// +// }) +// +// var sines = []; +// +// cells.forEach(function (cellObj) { +// if (cellObj.status === 'alive') { +// if (cellObj.aliveCount < 2 || cellObj.aliveCount > 3) { +// cellObj.cell.click() +// } +// synth.noteOnWithFreq(cellObj.freq, cellObj.velocity); +// } +// if (cellObj.status === 'dead') { +// if (cellObj.aliveCount === 3) { +// cellObj.cell.click() +// } +// synth.noteOffWithFreq(cellObj.freq); +// } +// }) +// }, + + // enableAutoPlay: function () { + // // Start Auto-Play by running the 'step' function + // // automatically repeatedly every fixed time interval + // if (this.autoPlayOn) { + // this.autoPlayOn = false + // clearInterval(this.setIntervalID) + // document.getElementById('play_btn').innerHTML = 'Play' + // } else { + // this.autoPlayOn = true + // this.setIntervalID = setInterval(this.step.bind(this), document.getElementById('step_amount').value || 100) + // document.getElementById('play_btn').innerHTML = 'Pause' + // } + // }, + + clear: function () { + forEachCell(function(cell) { + cell.className = "dead"; + cell.setAttribute('data-status', 'dead'); + }) + }, + + randomize: function () { + forEachCell(function (cell, x, y) { + debugger; + let cellObj = angular.element(document.getElementById( x + '-' + y)).scope().$$childHead.cellObj; + var state = Math.floor(Math.random() * 2) + if (state === 1) { + cellObj.makeDead(true); + } else { + cellObj.makeAlive(true); + } + }) + + } + }; + }] +) + +game.factory('cellStepFactory', [function(){ + return function(cell){ + let color = '#'+Math.floor(Math.random()*16777215).toString(16); + let self = cell; + + } +}]) + +game.factory('stepFactory', ['forEachCell', '$rootScope', function(forEachCell, rootScope){ + let previousStep = 0; + return function () { + debugger; + let nextStep = previousStep+1; + rootScope.$broadcast('step', previousStep, nextStep); + previousStep++; + // color = '#'+Math.floor(Math.random()*16777215).toString(16) + // console.log(this.color) + // 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 + // (x, y) -> (1, 1) + // check: + // (0, 0) , (1, 0) , (2, 0) + // (0, 1) , , (2, 1) + // (0, 2) , (1, 2) , (2, 2) + // + // (x-1, y-1), ( x , y-1), (x+1, y-1) + // (x-1, y ), ( x , y ), (x+1, y ) + // (x-1, y+1), ( x , y+1), (x+1, y+1) + // + // + // var self = this; + // var start = Date.now(), + // cells = [], + // boardWidth = this.width, + // boardHeight = this.height + // forEachCell(function (cell, x, y) { + // // console.log(cell) + // var cellObj = document.getElementById(x + '-' + y); + // for (var i = cellObj.x - 1; i < cellObj.x + 2; i++) { + // // if (i < 0 || i > boardWidth - 1) continue + // var col = i < 0 ? boardWidth - 1 : i % boardWidth + // for (var j = cellObj.y - 1; j < cellObj.y + 2; j++) { + // if (i === cellObj.x && j === cellObj.y) continue + // var row = j < 0 ? boardHeight - 1 : j % boardHeight + // var currCell = document.getElementById(col + '-' + row) + // if (currCell.dataset.status === 'alive') cellObj.aliveCount++ + // if (currCell.dataset.status === 'dead') cellObj.deadCount++ + // } + // } + // cells.push(cellObj) + // + // }) + // + // var sines = []; + // + // cells.forEach(function (cellObj) { + // if (cellObj.status === 'alive') { + // if (cellObj.aliveCount < 2 || cellObj.aliveCount > 3) { + // cellObj.cell.click() + // } + // synth.noteOnWithFreq(cellObj.freq, cellObj.velocity); + // } + // if (cellObj.status === 'dead') { + // if (cellObj.aliveCount === 3) { + // cellObj.cell.click() + // } + // synth.noteOffWithFreq(cellObj.freq); + // } + // }) + }; +}]) + +game.factory('forEachCell', function(){ + return 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 board = document.getElementById('board').firstChild + Array.prototype.slice.call(board.children).forEach(function (row) { + Array.prototype.slice.call(row.children).forEach(function (cell) { + var coords = cell.id.split("-") + var x = parseInt(coords[0]) + var y = parseInt(coords[1]) + iteratorFunc(cell, x, y) + }) + }) + + } +}) + +// Cell Click Factory should be replaced by a method on the cell factory. It can be called by other factories/directives/controllers to execute a "cell click" +// +game.factory('cellClickFactory', function(){ + return function(scope, element, attrs){ + if (scope.status == 'dead') { + element.className = "alive"; + element.setAttribute('status', 'alive'); + element.setAttribute('style', 'background-color:' + color) + // console.log(color) + } else { + element.className = "dead"; + element.setAttribute('data-status', 'dead'); + element.setAttribute('style', 'background-color:#FFFFFF') + } + }; +}) diff --git a/app/components/game/grid/cell/cell.directive.js b/app/components/game/grid/cell/cell.directive.js new file mode 100644 index 0000000..87c1a12 --- /dev/null +++ b/app/components/game/grid/cell/cell.directive.js @@ -0,0 +1,29 @@ +'use strict'; +game.directive('cell', ['cellClickFactory', '$log', 'cellFactory', 'gameFactory', function(cellClick, $log, cellFactory, gameFactory){ + return { + restrict: 'E', + template: ``, + replace: true, + scope: { + "status": '@', + }, + link: function(scope, cell, attrs){ + let DOMcell = cell[0]; + scope.cellObj = new cellFactory(DOMcell, scope.$parent.$index, scope.$parent.$parent.$index, 'dead', gameFactory.cellCount, 0, gameFactory.height, gameFactory.width, scope); + let cellObj = scope.cellObj; + scope.$on('step', function(e, previousStep, nextStep){ + cellObj.makeAliveOrDead(previousStep) + }) + scope.toggleStatus = function(){ + //cellClick(scope, cell[0], attrs) + cellObj.click(); + }; + } + } +}]) + + +// +// function onClick (scope, cell, attrs, cellClick){ +// return cellClick.bind(scope, cell, attrs); +// } diff --git a/app/components/game/grid/cell/cell.factory.js b/app/components/game/grid/cell/cell.factory.js new file mode 100644 index 0000000..cc84a38 --- /dev/null +++ b/app/components/game/grid/cell/cell.factory.js @@ -0,0 +1,184 @@ +'use strict'; +var pentatonicFrequencies = [ + /*16.35, + 18.35, + 20.6, + 24.5, + 27.5, + 32.7, + 36.71, + 41.2, + 49, + 55, + 65.41, + 73.42, + 82.41, + 98, + 110, + 130.81, + 146.83, + 164.81, + 196, + 220,*/ + 261.63, + 293.66, + 329.63, + 392, + 440, + 523.25, + 587.33, + 659.25, + 783.99, + 880, + 1046.5, + 1174.66, + 1318.51, + 1567.98, + 1760 + /*2093, + 2349.32, + 2637.02, + 3135.96, + 3520, + 4186.01, + 4698.63, + 5274.04, + 6271.93, + 7040*/ +]; + +game.factory('cellFactory', function(gameFactory, synthFactory){ + +/** + * [Cell constructor] + * @type {[type]} + */ +const Cell = class { + constructor(cell, x, y, status, deadCount, aliveCount, height, width, scope) { + this.cell = cell; + this.scope = scope; + this.x = x + this.y = y + this.status = status + this.states = [this.status] //zero-indexed to step + this.deadCount = deadCount + this.aliveCount = aliveCount + this.freq = pentatonicFrequencies[x % pentatonicFrequencies.length]; + this.velocity = 500* (y / width); + this.parent = {height, width} + } + generateFrequency(){} + //generateRandomFrequency(){} + get neighbors(){ + let x = this.x, y = this.y; + let neighbors = [ + Cell.getNeighbor(x-1, y-1, this.parent), + Cell.getNeighbor(x , y-1, this.parent), + Cell.getNeighbor(x+1, y-1, this.parent), + Cell.getNeighbor(x-1, y , this.parent), + Cell.getNeighbor(x+1, y , this.parent), + Cell.getNeighbor(x-1, y+1, this.parent), + Cell.getNeighbor(x , y+1, this.parent), + Cell.getNeighbor(x+1, y+1, this.parent) + ]; + return neighbors; + } + static getNeighbor(x, y, parent){ + if( x < 0 ) x = parent.width-1; + if( x >= parent.width ) x = 0; + if( y < 0 ) y = parent.height-1; + if( y >= parent.height ) y = 0; + if( x<0 || + x >= parent.width || + y <0 || + y >= parent.height ) { + return null + } else { + let cellToReturn = Cell.getCell(x, y); + let toReturn = cellToReturn.scope().$$childHead.cellObj; + return toReturn; + } + // return x < 0 || x >= parent.width || y < 0 || y >= parent.height ? null : document.getElementById( x+'-'+y ).scope.cellObj; + } + static getCell(x, y){ + return angular.element(document.getElementById( x + '-' + y )) + } + /** + * [checkNeighbors returns the number of alive neighbors for a cell] + * @param {[integer]} step [step should be the previous step during a step event] + * @return {[integer]} [the number of alive neighbors] + */ + checkNeighbors(step){ + debugger; + let aliveNeighbors = 0; + this.neighbors.forEach( function(neighbor){ + if(!!neighbor && neighbor.states[step] === 'alive'){ + aliveNeighbors++ + } + }) + return aliveNeighbors; + } + makeAliveOrDead(step){ + let neighborCount = this.checkNeighbors(step); + if(this.isAlive(step)){ + synthFactory.noteOnWithFreq(this.freq, this.velocity); + if(neighborCount < 2 || neighborCount > 3) { + this.makeDead(); + } + } + else{ + synthFactory.noteOffWithFreq(this.freq); + if(neighborCount === 3){ + this.makeAlive(); + } + } + this.registerState(); + } + changeColor(){ + return '#'+Math.floor(Math.random()*16777215).toString(16); + } + toggleState(){} + randomState(){} + isAlive(step = null){ + return step === null ? this.status === 'alive' : this.states[step] === 'alive'; + } + toggleNote(){} + makeAlive(register = false){ + debugger; + this.status = 'alive'; + if(register) this.states[gameFactory.currentStep] = this.status; + this.cell.className = "alive"; + this.cell.setAttribute('status', 'alive'); + this.cell.setAttribute('style', 'background-color:' + this.changeColor()) + // console.log(color) + } + makeDead(register = false){ + debugger; + this.status = 'dead'; + if(register) this.states[gameFactory.currentStep] = this.status; + this.cell.className = "dead"; + this.cell.setAttribute('data-status', 'dead'); + this.cell.setAttribute('style', 'background-color:#FFFFFF') + } + registerState(){ + this.states.push(this.status); + }; + click(){ + if (this.status == 'dead') { + this.makeAlive(true); + // this.cell.className = "alive"; + // this.cell.setAttribute('status', 'alive'); + // this.cell.setAttribute('style', 'background-color:' + color) + // // console.log(color) + } else { + this.makeDead(true); + // this.cell.className = "dead"; + // this.cell.setAttribute('data-status', 'dead'); + // this.cell.setAttribute('style', 'background-color:#FFFFFF') + } + } +} + + + return Cell; +}); diff --git a/app/components/game/grid/cell/row.directive.js b/app/components/game/grid/cell/row.directive.js new file mode 100644 index 0000000..84e96d0 --- /dev/null +++ b/app/components/game/grid/cell/row.directive.js @@ -0,0 +1,20 @@ +'use strict'; + +game.directive('row', function(){ + return { + restrict: 'EA', + template: ``, + // scope: { + // row: '@' + // }, + link: function(scope){ + scope.cells = new Array(50) + }, + controller: ['$scope', function($scope){ + // $scope.row = $scope.id; + }] + } +}) + + + // template: ``, diff --git a/app/components/game/grid/grid.directive.js b/app/components/game/grid/grid.directive.js new file mode 100644 index 0000000..61378a2 --- /dev/null +++ b/app/components/game/grid/grid.directive.js @@ -0,0 +1,25 @@ +'use strict'; +game.directive('gameGrid',['cellFactory', function(Cell){ + return { + restrict: "EA", + template: `
`, + replace: true, + link: function(scope){ + // scope.rows = makeRows(50, 50, Cell) + scope.rows = new Array(50); + } + } +}]) + +// function makeRow(j, width, Cell){ +// return Array.from({length: width}, function(cell, i){ +// let DOMcell = document.getElementById(i + '-' + j); +// return new Cell(DOMcell, i, j, 0, 0, 50, 50) +// }) +// } +// +// function makeRows(height, width, Cell){ +// return Array.from({length: height}, function(row, j){ +// return makeRow(j, width, Cell) +// }) +// } diff --git a/table/table.directive.js b/app/components/game/grid/table.directive.js similarity index 100% rename from table/table.directive.js rename to app/components/game/grid/table.directive.js diff --git a/app/components/game/synth/synth.factory.js b/app/components/game/synth/synth.factory.js new file mode 100644 index 0000000..da511a7 --- /dev/null +++ b/app/components/game/synth/synth.factory.js @@ -0,0 +1,23 @@ +'use strict'; + + +var Synth = class{ + constructor(){ + this.sines = []; + return T("OscGen", (T("reverb", { + room: 0.95, + damp: 0.1, + mix: 0.75 + }, T("sin", {mul:0.25})))).play(); + } +} + +function createSynth(T){ + return new Synth() +} + +game.factory('synthFactory',function($window){ + debugger; + var synth = createSynth($window.T); + return createSynth($window.T); +}); diff --git a/app/game.js b/app/game.js new file mode 100644 index 0000000..06a4f53 --- /dev/null +++ b/app/game.js @@ -0,0 +1,308 @@ +// =========================== +// =========================== +// === USE APP.CONTROLLER ==== +// =========================== +// =========================== + + + +// var pentatonicFrequencies = [ +// /*16.35, +// 18.35, +// 20.6, +// 24.5, +// 27.5, +// 32.7, +// 36.71, +// 41.2, +// 49, +// 55, +// 65.41, +// 73.42, +// 82.41, +// 98, +// 110, +// 130.81, +// 146.83, +// 164.81, +// 196, +// 220,*/ +// 261.63, +// 293.66, +// 329.63, +// 392, +// 440, +// 523.25, +// 587.33, +// 659.25, +// 783.99, +// 880, +// 1046.5, +// 1174.66, +// 1318.51, +// 1567.98, +// 1760 +// /*2093, +// 2349.32, +// 2637.02, +// 3135.96, +// 3520, +// 4186.01, +// 4698.63, +// 5274.04, +// 6271.93, +// 7040*/ +// ]; +// +// var color = null +// +// var synth = T("OscGen", (T("reverb", { +// room: 0.95, +// damp: 0.1, +// mix: 0.75 +// }, T("sin", {mul:0.25})))).play(); +// +// var gameOfLife = { +// width: 20, +// height: 20, +// stepInterval: null, +// autoPlayOn: false, +// setIntervalID: 0, +// +// +// createAndShowBoard: function () { +// // create element +// var goltable = document.createElement("tbody"); +// +// // build Table HTML +// var tablehtml = ''; +// for (var h = 0; h < this.height; h++) { +// tablehtml += ""; +// for (var w = 0; w < this.width; w++) { +// tablehtml += ""; +// } +// tablehtml += ""; +// } +// goltable.innerHTML = tablehtml; +// +// // add table to the #board element +// var board = document.getElementById('board'); +// 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 board = document.getElementById('board').firstChild +// Array.prototype.slice.call(board.children).forEach(function (row) { +// Array.prototype.slice.call(row.children).forEach(function (cell) { +// var coords = cell.id.split("-") +// var x = parseInt(coords[0]) +// var y = parseInt(coords[1]) +// iteratorFunc(cell, x, y) +// }) +// }) +// +// }, +// +// 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 (this.getAttribute('data-status') == 'dead') { +// this.className = "alive"; +// this.setAttribute('data-status', 'alive'); +// this.setAttribute('style', 'background-color:' + color) +// // console.log(color) +// } else { +// this.className = "dead"; +// this.setAttribute('data-status', 'dead'); +// this.setAttribute('style', 'background-color:#FFFFFF') +// } +// }; +// this.forEachCell(function (cell) { +// cell.onclick = onCellClick +// }) +// // var cell00 = document.getElementById('0-0'); +// // cell00.onclick = onCellClick; +// }, +// +// // sound: function (sines, interval) { +// // var env = T("adsr", { +// // d: 3000, +// // s: 0, +// // r: 600 +// // }); +// // +// // T("perc", { +// // r: interval * 0.5 +// // }, T("reverb", { +// // room: 0.95, +// // damp: 0.1, +// // mix: 0.75 +// // }, sines)).on("ended", function () { +// // this.pause(); +// // }).bang().play(); +// // }, +// +// // makeSine: function (cell) { +// // return T("saw", { +// // freq: pentatonicFrequencies[freq], +// // mul: amplitude +// // }); +// // }, +// +// step: function () { +// color = '#'+Math.floor(Math.random()*16777215).toString(16) +// // console.log(this.color) +// // 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 +// // (x, y) -> (1, 1) +// // check: +// // (0, 0) , (1, 0) , (2, 0) +// // (0, 1) , , (2, 1) +// // (0, 2) , (1, 2) , (2, 2) +// // +// // (x-1, y-1), ( x , y-1), (x+1, y-1) +// // (x-1, y ), ( x , y ), (x+1, y ) +// // (x-1, y+1), ( x , y+1), (x+1, y+1) +// // +// // +// var self = this; +// var start = Date.now(), +// cells = [], +// boardWidth = this.width, +// boardHeight = this.height +// this.forEachCell(function (cell, x, y) { +// // console.log(cell) +// var cellObj = new Cell(cell, x, y, cell.dataset.status, 0, 0, boardHeight, boardWidth) +// for (var i = cellObj.x - 1; i < cellObj.x + 2; i++) { +// // if (i < 0 || i > boardWidth - 1) continue +// var col = i < 0 ? boardWidth - 1 : i % boardWidth +// for (var j = cellObj.y - 1; j < cellObj.y + 2; j++) { +// if (i === cellObj.x && j === cellObj.y) continue +// var row = j < 0 ? boardHeight - 1 : j % boardHeight +// var currCell = document.getElementById(col + '-' + row) +// if (currCell.dataset.status === 'alive') cellObj.aliveCount++ +// if (currCell.dataset.status === 'dead') cellObj.deadCount++ +// } +// } +// cells.push(cellObj) +// }) +// +// var sines = []; +// +// // var cssRuleCode = document.all ? 'rules' : 'cssRules'; //account for IE and FF +// // var rule = document.styleSheets[1][cssRuleCode]; +// // document.styleSheets[1].insertRule('td.alive' + ' {'+'background-color'+':'+ +// // '#'+Math.floor(Math.random()*16777215).toString(16) +'}', 1) +// // var selector = rule.selectorText; //maybe '#tId' +// // console.log(selector) +// // var value = rule.value; //both selectorText and value are settable +// +// cells.forEach(function (cellObj) { +// if (cellObj.status === 'alive') { +// if (cellObj.aliveCount < 2 || cellObj.aliveCount > 3) { +// cellObj.cell.click() +// } +// synth.noteOnWithFreq(cellObj.freq, cellObj.velocity); +// } +// if (cellObj.status === 'dead') { +// if (cellObj.aliveCount === 3) { +// cellObj.cell.click() +// } +// synth.noteOffWithFreq(cellObj.freq); +// } +// }) +// }, +// +// enableAutoPlay: function () { +// // Start Auto-Play by running the 'step' function +// // automatically repeatedly every fixed time interval +// if (this.autoPlayOn) { +// this.autoPlayOn = false +// clearInterval(this.setIntervalID) +// document.getElementById('play_btn').innerHTML = 'Play' +// } else { +// this.autoPlayOn = true +// this.setIntervalID = setInterval(this.step.bind(this), document.getElementById('step_amount').value || 100) +// document.getElementById('play_btn').innerHTML = 'Pause' +// } +// }, +// +// clear: function () { +// this.forEachCell(function (cell) { +// cell.className = "dead"; +// cell.setAttribute('data-status', 'dead'); +// }) +// }, +// +// randomize: function () { +// this.forEachCell(function (cell, x, y) { +// var state = Math.floor(Math.random() * 2) +// if (state === 1) { +// cell.className = "dead" +// cell.setAttribute('data-status', 'dead'); +// } else { +// cell.className = 'alive' +// cell.setAttribute('data-status', 'alive'); +// } +// }) +// +// } +// }; +// var FileReader = new FileReader() +// document.getElementById('step_btn').addEventListener('click', gameOfLife.step.bind(gameOfLife)) +// document.getElementById('clear_btn').addEventListener('click', gameOfLife.clear.bind(gameOfLife)) +// document.getElementById('play_btn').addEventListener('click', gameOfLife.enableAutoPlay.bind(gameOfLife)) +// document.getElementById('reset_btn').addEventListener('click', gameOfLife.randomize.bind(gameOfLife)) +// document.getElementById('submit_file').addEventListener('click', function () { +// debugger; +// var file = document.getElementById('input').files[0], +// content = FileReader.readAsText(file) +// while (FileReader.readyState !== 2) { +// console.log(FileReader.readyState) +// } +// console.log(content.result) +// }) +// +// gameOfLife.createAndShowBoard(); +// +// function Cell(cell, x, y, status, deadCount, aliveCount, height, width) { +// this.cell = cell +// this.x = x +// this.y = y +// this.status = status +// this.deadCount = deadCount +// this.aliveCount = aliveCount +// this.freq = pentatonicFrequencies[x % pentatonicFrequencies.length]; +// this.velocity = 1000* (y / width); +// } diff --git a/app/index.html b/app/index.html new file mode 100644 index 0000000..fc0ee59 --- /dev/null +++ b/app/index.html @@ -0,0 +1,65 @@ + + + + + + Grains of Life + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Game of Life

+ + + +
+ +

+ + + + + + + + diff --git a/table/module.js b/app/module.js similarity index 100% rename from table/module.js rename to app/module.js diff --git a/bower.json b/bower.json new file mode 100644 index 0000000..7eb803f --- /dev/null +++ b/bower.json @@ -0,0 +1,28 @@ +{ + "name": "grains-of-life", + "description": "", + "main": "", + "authors": [ + "thejohnbackes " + ], + "license": "ISC", + "homepage": "https://github.com/Octowl/GrainsOfLife", + "moduleType": [ + "es6", + "node" + ], + "ignore": [ + "**/.*", + "node_modules", + "bower_components", + "test", + "tests" + ], + "dependencies": { + "bootstrap-slider": "*", + "angular": "^1.5.6", + "bootstrap": "^3.3.6", + "jquery": "^2.2.4", + "timbre.js": "timbre#^14.11.25" + } +} diff --git a/game.js b/game.js deleted file mode 100644 index d243cd2..0000000 --- a/game.js +++ /dev/null @@ -1,300 +0,0 @@ -var pentatonicFrequencies = [ - /*16.35, - 18.35, - 20.6, - 24.5, - 27.5, - 32.7, - 36.71, - 41.2, - 49, - 55, - 65.41, - 73.42, - 82.41, - 98, - 110, - 130.81, - 146.83, - 164.81, - 196, - 220,*/ - 261.63, - 293.66, - 329.63, - 392, - 440, - 523.25, - 587.33, - 659.25, - 783.99, - 880, - 1046.5, - 1174.66, - 1318.51, - 1567.98, - 1760 - /*2093, - 2349.32, - 2637.02, - 3135.96, - 3520, - 4186.01, - 4698.63, - 5274.04, - 6271.93, - 7040*/ -]; - -var color = null - -var synth = T("OscGen", (T("reverb", { - room: 0.95, - damp: 0.1, - mix: 0.75 - }, T("sin", {mul:0.25})))).play(); - -var gameOfLife = { - width: 20, - height: 20, - stepInterval: null, - autoPlayOn: false, - setIntervalID: 0, - - - createAndShowBoard: function () { - // create
element - var goltable = document.createElement("tbody"); - - // build Table HTML - var tablehtml = ''; - for (var h = 0; h < this.height; h++) { - tablehtml += ""; - for (var w = 0; w < this.width; w++) { - tablehtml += ""; - } - tablehtml += ""; - } - goltable.innerHTML = tablehtml; - - // add table to the #board element - var board = document.getElementById('board'); - 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 board = document.getElementById('board').firstChild - Array.prototype.slice.call(board.children).forEach(function (row) { - Array.prototype.slice.call(row.children).forEach(function (cell) { - var coords = cell.id.split("-") - var x = parseInt(coords[0]) - var y = parseInt(coords[1]) - iteratorFunc(cell, x, y) - }) - }) - - }, - - 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 (this.getAttribute('data-status') == 'dead') { - this.className = "alive"; - this.setAttribute('data-status', 'alive'); - this.setAttribute('style', 'background-color:' + color) - // console.log(color) - } else { - this.className = "dead"; - this.setAttribute('data-status', 'dead'); - this.setAttribute('style', 'background-color:#FFFFFF') - } - }; - this.forEachCell(function (cell) { - cell.onclick = onCellClick - }) - // var cell00 = document.getElementById('0-0'); - // cell00.onclick = onCellClick; - }, - - // sound: function (sines, interval) { - // var env = T("adsr", { - // d: 3000, - // s: 0, - // r: 600 - // }); - // - // T("perc", { - // r: interval * 0.5 - // }, T("reverb", { - // room: 0.95, - // damp: 0.1, - // mix: 0.75 - // }, sines)).on("ended", function () { - // this.pause(); - // }).bang().play(); - // }, - - // makeSine: function (cell) { - // return T("saw", { - // freq: pentatonicFrequencies[freq], - // mul: amplitude - // }); - // }, - - step: function () { - color = '#'+Math.floor(Math.random()*16777215).toString(16) - // console.log(this.color) - // 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 - // (x, y) -> (1, 1) - // check: - // (0, 0) , (1, 0) , (2, 0) - // (0, 1) , , (2, 1) - // (0, 2) , (1, 2) , (2, 2) - // - // (x-1, y-1), ( x , y-1), (x+1, y-1) - // (x-1, y ), ( x , y ), (x+1, y ) - // (x-1, y+1), ( x , y+1), (x+1, y+1) - // - // - var self = this; - var start = Date.now(), - cells = [], - boardWidth = this.width, - boardHeight = this.height - this.forEachCell(function (cell, x, y) { - // console.log(cell) - var cellObj = new Cell(cell, x, y, cell.dataset.status, 0, 0, boardHeight, boardWidth) - for (var i = cellObj.x - 1; i < cellObj.x + 2; i++) { - // if (i < 0 || i > boardWidth - 1) continue - var col = i < 0 ? boardWidth - 1 : i % boardWidth - for (var j = cellObj.y - 1; j < cellObj.y + 2; j++) { - if (i === cellObj.x && j === cellObj.y) continue - var row = j < 0 ? boardHeight - 1 : j % boardHeight - var currCell = document.getElementById(col + '-' + row) - if (currCell.dataset.status === 'alive') cellObj.aliveCount++ - if (currCell.dataset.status === 'dead') cellObj.deadCount++ - } - } - cells.push(cellObj) - }) - - var sines = []; - - // var cssRuleCode = document.all ? 'rules' : 'cssRules'; //account for IE and FF - // var rule = document.styleSheets[1][cssRuleCode]; - // document.styleSheets[1].insertRule('td.alive' + ' {'+'background-color'+':'+ - // '#'+Math.floor(Math.random()*16777215).toString(16) +'}', 1) - // var selector = rule.selectorText; //maybe '#tId' - // console.log(selector) - // var value = rule.value; //both selectorText and value are settable - - cells.forEach(function (cellObj) { - if (cellObj.status === 'alive') { - if (cellObj.aliveCount < 2 || cellObj.aliveCount > 3) { - cellObj.cell.click() - } - synth.noteOnWithFreq(cellObj.freq, cellObj.velocity); - } - if (cellObj.status === 'dead') { - if (cellObj.aliveCount === 3) { - cellObj.cell.click() - } - synth.noteOffWithFreq(cellObj.freq); - } - }) - }, - - enableAutoPlay: function () { - // Start Auto-Play by running the 'step' function - // automatically repeatedly every fixed time interval - if (this.autoPlayOn) { - this.autoPlayOn = false - clearInterval(this.setIntervalID) - document.getElementById('play_btn').innerHTML = 'Play' - } else { - this.autoPlayOn = true - this.setIntervalID = setInterval(this.step.bind(this), document.getElementById('step_amount').value || 100) - document.getElementById('play_btn').innerHTML = 'Pause' - } - }, - - clear: function () { - this.forEachCell(function (cell) { - cell.className = "dead"; - cell.setAttribute('data-status', 'dead'); - }) - }, - - randomize: function () { - this.forEachCell(function (cell, x, y) { - var state = Math.floor(Math.random() * 2) - if (state === 1) { - cell.className = "dead" - cell.setAttribute('data-status', 'dead'); - } else { - cell.className = 'alive' - cell.setAttribute('data-status', 'alive'); - } - }) - - } -}; -var FileReader = new FileReader() -document.getElementById('step_btn').addEventListener('click', gameOfLife.step.bind(gameOfLife)) -document.getElementById('clear_btn').addEventListener('click', gameOfLife.clear.bind(gameOfLife)) -document.getElementById('play_btn').addEventListener('click', gameOfLife.enableAutoPlay.bind(gameOfLife)) -document.getElementById('reset_btn').addEventListener('click', gameOfLife.randomize.bind(gameOfLife)) -document.getElementById('submit_file').addEventListener('click', function () { - debugger; - var file = document.getElementById('input').files[0], - content = FileReader.readAsText(file) - while (FileReader.readyState !== 2) { - console.log(FileReader.readyState) - } - console.log(content.result) -}) - -gameOfLife.createAndShowBoard(); - -function Cell(cell, x, y, status, deadCount, aliveCount, height, width) { - this.cell = cell - this.x = x - this.y = y - this.status = status - this.deadCount = deadCount - this.aliveCount = aliveCount - this.freq = pentatonicFrequencies[x % pentatonicFrequencies.length]; - this.velocity = 1000* (y / width); -} diff --git a/package.json b/package.json index 24d4755..cf557f1 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "version": "0.0.1", "description": "", "scripts": { - "start": "echo 'please select start file'", + "start": "nodemon server.js", "test": "nodemon -x mocha ./tests/index.spec.js" }, "repository": { @@ -13,7 +13,11 @@ "author": "", "license": "ISC", "dependencies": { - "nodemon": "^1.9.2" + "body-parser": "^1.15.1", + "express": "^4.13.4", + "express-session": "^1.13.0", + "nodemon": "^1.9.2", + "timbre": "^14.11.25" }, "devDependencies": { "mocha": "^2.5.3", @@ -22,8 +26,5 @@ "bugs": { "url": "https://github.com/Octowl/GrainsOfLife/issues" }, - "homepage": "https://github.com/Octowl/GrainsOfLife#readme", - "dependencies": { - "timbre": "^14.11.25" - } + "homepage": "https://github.com/Octowl/GrainsOfLife#readme" } diff --git a/libraries/bootstrap.min.css b/public/css/bootstrap.min.css similarity index 100% rename from libraries/bootstrap.min.css rename to public/css/bootstrap.min.css diff --git a/game.css b/public/css/game.css similarity index 100% rename from game.css rename to public/css/game.css diff --git a/server.js b/server.js new file mode 100644 index 0000000..683cb19 --- /dev/null +++ b/server.js @@ -0,0 +1,20 @@ +'use strict'; +var express = require("express"); +var app = express(); +var bodyParser = require("body-parser"); +var session = require("express-session"); + +// PUBLIC ROUTES + app.use('/app', express.static(__dirname + "/app")) + app.use('/files/libraries', express.static(__dirname + "/libraries")) + app.use('/files', express.static(__dirname + "/public")) + app.use('/files', express.static(__dirname + "/bower_components")) + app.use('/files/bootstrap', express.static(__dirname + "/bower_components/bootstrap/dist")) + app.use('/files/bootstrap/slider', express.static(__dirname + "/bower_components/bootstrap-slider")) + +// dynamic routes +app.get("/", (req, res, next) => res.status(200).sendFile(__dirname + "/app/index.html")) + +app.listen(1133, ()=>console.log("listening on 1133")); + +module.exports = app; diff --git a/tests/index.spec.js b/tests/index.spec.js index 498b5e1..669cefd 100644 --- a/tests/index.spec.js +++ b/tests/index.spec.js @@ -1,58 +1,121 @@ const should = require('chai').should(); -xdescribe("Grains of Life", function(){ +describe("Grains of Life", function(){ + it("is refactored to Angular best practices", function () { + throw 'fail'; + }) describe("Grid", function () { describe("Game Logic", function () { //Game of Life Rules: https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life#Rules - it("follows game of life rules") + it("follows game of life rules", function(){ + //throw 'false'; + }) }) describe("Node", function () { describe("State", function () { - it("can be 'alive' or 'dead'") - it("is 'dead' by default") + it("can be 'alive' or 'dead'", function(){ + //throw 'fail'; + }) + it("is 'dead' by default", function(){ + //throw 'fail'; + }) }) describe("Wave", function () { - it("outputs when Node is alive") - it("does not output when Node is dead") - it("is function of coordinates") - it("frequency is controlled by x coords by default") - it("amplitude (volume) is controlled by y coords by default") - it("has a different form for every node") - it("outputs to a processor chain") + it("outputs when Node is alive", function(){ + throw 'fail'; + }) + it("does not output when Node is dead", function(){ + throw 'fail'; + }) + it("is function of coordinates", function(){ + throw 'fail'; + }) + it("frequency is controlled by x coords by default", function(){ + throw 'fail'; + }) + it("amplitude (volume) is controlled by y coords by default", function(){ + throw 'fail'; + }) + it("has a different form for every node", function(){ + throw 'fail'; + }) + it("outputs to a processor chain", function(){ + throw 'fail'; + }) }) describe("Click", function () { - it("toggles between 'alive' and 'dead'") - it("plays a short tone") + it("toggles between 'alive' and 'dead'", function(){ + //throw 'fail'; + }) + it("plays a short tone", function(){ + throw 'fail'; + }) }) }) describe("Play Button", function () { - it("plays the sounds continuously") - it("iterates the game state") - it("changes to pause button when running") + it("plays the sounds continuously", function(){ + throw 'fail'; + }) + it("iterates the game state", function(){ + throw 'fail'; + }) + it("changes to pause button when running", function(){ + throw 'fail'; + }) describe("Pause Button", function () { - it("pauses both playback and game play") + it("pauses both playback and game play", function(){ + throw 'fail'; + }) }) }) describe("Save Button", function () { - it("creates a persistent route the the current grid state") - it("gives some sort of visual feedback") + it("creates a persistent route the the current grid state", function(){ + throw 'fail'; + }) + it("gives some sort of visual feedback", function(){ + throw 'fail'; + }) }) describe("UI Controller", function () { - it("controls width") - it("controls height") - it("controls wave function") - it("controls the playback speed") + it("controls width", function(){ + throw 'fail'; + }) + it("controls height", function(){ + throw 'fail'; + }) + it("controls wave function", function(){ + throw 'fail'; + }) + it("controls the playback speed", function(){ + throw 'fail'; + }) + it("allows user to choose waveform", function(){ + throw 'fail'; + }) }) describe("Audio Output", function () { - it("uses web audio api") - it("combines the sounds of all nodes") + xit("uses web audio api", function(){ + throw 'fail'; + }) + it("combines the sounds of all nodes", function(){ + //throw 'fail'; + }) }) }) // wish list is for features that would be cool but aren't for the first round describe("Wish List", function () { - it("is multiplayer") - it("could have other custom 'alive' states (not by default)") - it("can sample an mp3") + it("is multiplayer", function(){ + throw 'fail'; + }) + it("could have other custom 'alive' states (not by default)", function(){ + throw 'fail'; + }) + it("can sample an mp3", function(){ + throw 'fail'; + }) + it("does not slow down with more live nodes", function(){ + throw 'fail'; + }) }) })