From 020ee10c659f53e2d29d8ae8dc6e4df3da62bd40 Mon Sep 17 00:00:00 2001 From: thejohnbackes Date: Fri, 3 Jun 2016 15:21:30 -0400 Subject: [PATCH 1/8] initialized angular and reorganized dependencies --- .gitignore | 3 ++- app/app.controller.js | 3 +++ .../controlPanel/controlPanel.directive.js | 22 +++++++++++++++ .../components/game/grid}/table.directive.js | 0 game.js => app/game.js | 0 index.html => app/index.html | 23 +++++++++++----- {table => app}/module.js | 0 bower.json | 27 +++++++++++++++++++ package.json | 5 +++- {libraries => public/css}/bootstrap.min.css | 0 game.css => public/css/game.css | 0 server.js | 20 ++++++++++++++ 12 files changed, 95 insertions(+), 8 deletions(-) create mode 100644 app/app.controller.js create mode 100644 app/components/game/controls/controlPanel/controlPanel.directive.js rename {table => app/components/game/grid}/table.directive.js (100%) rename game.js => app/game.js (100%) rename index.html => app/index.html (54%) rename {table => app}/module.js (100%) create mode 100644 bower.json rename {libraries => public/css}/bootstrap.min.css (100%) rename game.css => public/css/game.css (100%) create mode 100644 server.js 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..d8c2b86 --- /dev/null +++ b/app/app.controller.js @@ -0,0 +1,3 @@ +game.controller("app", ['$scope', function($scope){ + +}] 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..f016380 --- /dev/null +++ b/app/components/game/controls/controlPanel/controlPanel.directive.js @@ -0,0 +1,22 @@ +'use strict'; +app.directive("controlPanel", function(){ + return { + restrict: "E", + template: controlPanelTemplate(), + } +}) + + +function controlPanelTemplate(){ + return ` +
+
+ ` +} 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/game.js b/app/game.js similarity index 100% rename from game.js rename to app/game.js diff --git a/index.html b/app/index.html similarity index 54% rename from index.html rename to app/index.html index 964bbdb..4857b3e 100644 --- a/index.html +++ b/app/index.html @@ -4,14 +4,25 @@ Grains of Life - - - - - + + + + + + + + + + + + + + + + + + @@ -45,7 +47,7 @@

Game of Life

- + From f7d8ecaa0f4b0a323b9e363e9b089149006aa6f2 Mon Sep 17 00:00:00 2001 From: thejohnbackes Date: Fri, 3 Jun 2016 16:01:48 -0400 Subject: [PATCH 3/8] added spec: allowe user to choose waveform --- app/index.html | 16 +++++++++------- bower.json | 3 ++- package.json | 8 +++----- server.js | 2 +- tests/index.spec.js | 1 + 5 files changed, 16 insertions(+), 14 deletions(-) diff --git a/app/index.html b/app/index.html index d510239..04ef2bd 100644 --- a/app/index.html +++ b/app/index.html @@ -4,12 +4,6 @@ Grains of Life - - - - - - @@ -23,7 +17,15 @@ + + + + + + + + + diff --git a/bower.json b/bower.json index 1f3eead..7eb803f 100644 --- a/bower.json +++ b/bower.json @@ -22,6 +22,7 @@ "bootstrap-slider": "*", "angular": "^1.5.6", "bootstrap": "^3.3.6", - "jquery": "^2.2.4" + "jquery": "^2.2.4", + "timbre.js": "timbre#^14.11.25" } } diff --git a/package.json b/package.json index 7d35425..cf557f1 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,8 @@ "body-parser": "^1.15.1", "express": "^4.13.4", "express-session": "^1.13.0", - "nodemon": "^1.9.2" + "nodemon": "^1.9.2", + "timbre": "^14.11.25" }, "devDependencies": { "mocha": "^2.5.3", @@ -25,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/server.js b/server.js index a68cb22..ab6cd15 100644 --- a/server.js +++ b/server.js @@ -7,8 +7,8 @@ var session = require("express-session"); // PUBLIC ROUTES app.use('/app', express.static(__dirname + "/app")) app.use('/files',express.static(__dirname + "/public")) + app.use('/files', express.static(__dirname + "/libraries")) app.use('/files', express.static(__dirname + "/bower_components")) - app.use('/files/angular', express.static(__dirname + "/bower_components/angular")) app.use('/files/bootstrap', express.static(__dirname + "/bower_components/bootstrap/dist")) app.use('/files/bootstrap/slider', express.static(__dirname + "/bower_components/bootstrap-slider")) diff --git a/tests/index.spec.js b/tests/index.spec.js index 498b5e1..4419b68 100644 --- a/tests/index.spec.js +++ b/tests/index.spec.js @@ -43,6 +43,7 @@ xdescribe("Grains of Life", function(){ it("controls height") it("controls wave function") it("controls the playback speed") + it("allows user to choose waveform") }) describe("Audio Output", function () { it("uses web audio api") From fe87abd776ab2deafe5fed3d0aad548eb54b3b8b Mon Sep 17 00:00:00 2001 From: thejohnbackes Date: Fri, 3 Jun 2016 17:46:00 -0400 Subject: [PATCH 4/8] refactored to angular --- app/app.controller.js | 22 +++- .../controlPanel/controlPanel.directive.js | 18 +-- app/index.html | 25 ++-- server.js | 4 +- tests/index.spec.js | 120 +++++++++++++----- 5 files changed, 135 insertions(+), 54 deletions(-) diff --git a/app/app.controller.js b/app/app.controller.js index 0ff2b9f..45fb4bc 100644 --- a/app/app.controller.js +++ b/app/app.controller.js @@ -1,3 +1,23 @@ -game.controller("app", ['$scope', function($scope){ +game.controller("app", ['$scope', '$window', 'cell', 'gameFactory', function($scope, $window, Cell, gameOfLife){ + debugger; + $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() { + debugger; + 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/controlPanel/controlPanel.directive.js b/app/components/game/controls/controlPanel/controlPanel.directive.js index f016380..b1460b3 100644 --- a/app/components/game/controls/controlPanel/controlPanel.directive.js +++ b/app/components/game/controls/controlPanel/controlPanel.directive.js @@ -1,22 +1,22 @@ 'use strict'; -app.directive("controlPanel", function(){ +game.directive("controlPanel", function(){ return { restrict: "E", - template: controlPanelTemplate(), + replace: true, + template: controlPanelTemplate() } }) function controlPanelTemplate(){ return ` -
+
` } diff --git a/app/index.html b/app/index.html index 04ef2bd..e5d6e34 100644 --- a/app/index.html +++ b/app/index.html @@ -15,37 +15,36 @@ - - + + - + + + + +

Game of Life

-
- -
- - - - - -
+ + +


diff --git a/server.js b/server.js index ab6cd15..683cb19 100644 --- a/server.js +++ b/server.js @@ -6,8 +6,8 @@ var session = require("express-session"); // PUBLIC ROUTES app.use('/app', express.static(__dirname + "/app")) - app.use('/files',express.static(__dirname + "/public")) - app.use('/files', express.static(__dirname + "/libraries")) + 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")) diff --git a/tests/index.spec.js b/tests/index.spec.js index 4419b68..669cefd 100644 --- a/tests/index.spec.js +++ b/tests/index.spec.js @@ -1,59 +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("allows user to choose waveform") + 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'; + }) }) }) From 7f2c187b5443658b0ffa3af1bee380423a9ac4bb Mon Sep 17 00:00:00 2001 From: thejohnbackes Date: Fri, 3 Jun 2016 17:46:26 -0400 Subject: [PATCH 5/8] refactored to Angular --- .../controls/buttons/button.directives.js | 56 +++++ app/components/game/game.factory.js | 195 ++++++++++++++++++ app/components/game/grid/cell/cell.factory.js | 16 ++ app/components/game/grid/grid.directive.js | 8 + 4 files changed, 275 insertions(+) create mode 100644 app/components/game/controls/buttons/button.directives.js create mode 100644 app/components/game/game.factory.js create mode 100644 app/components/game/grid/cell/cell.factory.js create mode 100644 app/components/game/grid/grid.directive.js 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..2c67157 --- /dev/null +++ b/app/components/game/controls/buttons/button.directives.js @@ -0,0 +1,56 @@ + +game.directive("clear", function(){ + return { + restrict: "E", + replace: true, + template: "" + } +}) + +game.directive("pause", function(){ + return { + restrict: "E", + replace: true, + template: "" + } +}) + +game.directive("play", function(){ + return { + restrict: "E", + replace: true, + template: "" + } +}) + +game.directive("reset", function(){ + return { + restrict: "E", + replace: true, + template: "" + } +}) + +game.directive("save", function(){ + return { + restrict: "E", + replace: true, + template: "" + } +}) + +game.directive("step", function(){ + return { + restrict: "E", + replace: true, + template: "" + } +}) + +game.directive("submit", function(){ + return { + restrict: "E", + replace: true, + template: "" + } +}) diff --git a/app/components/game/game.factory.js b/app/components/game/game.factory.js new file mode 100644 index 0000000..d10ab76 --- /dev/null +++ b/app/components/game/game.factory.js @@ -0,0 +1,195 @@ +game.factory('gameFactory',function(){ + return { + width: 50, + height: 50, + 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"; + for (var w=0; w"; + } + 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'); + } else { + this.className = "dead"; + this.setAttribute('data-status', 'dead'); + } + }; + this.forEachCell(function (cell) { + cell.onclick = onCellClick + }) + // var cell00 = document.getElementById('0-0'); + // cell00.onclick = onCellClick; + }, + + sound: function(sines, interval) { + //sines.forEach(function(sine){ + T("perc", {r:interval-10}, sines).on("ended", function() { + this.pause(); + }).bang().play(); + //}); + }, + + makeSine: function(cell) { + var freq = 100 + (cell.x/this.height) * 4000; + var amplitude = cell.y/this.width; + return T("sin", {freq:freq, mul:amplitude}); + }, + + 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 + // (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) { + var cellObj = new Cell(cell, x, y, cell.dataset.status, 0, 0) + 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() + } + sines.push(self.makeSine(cellObj)); + } + if (cellObj.status === 'dead') { + if (cellObj.aliveCount === 3) { + cellObj.cell.click() + } + } + }) + self.sound(sines, document.getElementById('step_amount').value); + // console.log(Date.now() - start + ' ms') + }, + + 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'); + } + }) + + } + }; + } +) 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..1931c5e --- /dev/null +++ b/app/components/game/grid/cell/cell.factory.js @@ -0,0 +1,16 @@ +'use strict'; + +var Cell = class { + constructor(cell, x, y, status, deadCount, aliveCount) { + this.cell = cell + this.x = x + this.y = y + this.status = status + this.deadCount = deadCount + this.aliveCount = aliveCount + } +} + +game.factory('cell', function(){ + return Cell; +}); diff --git a/app/components/game/grid/grid.directive.js b/app/components/game/grid/grid.directive.js new file mode 100644 index 0000000..a5715cf --- /dev/null +++ b/app/components/game/grid/grid.directive.js @@ -0,0 +1,8 @@ +'use strict'; +game.directive('gameGrid',function(){ + return { + restrict: "E", + template: `
`, + replace: true + } +}) From d74d0a89146c150e2506a316619ffb9477bf4616 Mon Sep 17 00:00:00 2001 From: thejohnbackes Date: Sun, 5 Jun 2016 16:35:49 -0400 Subject: [PATCH 6/8] foreach functionality moved to cell factory. step function is still broken. --- app/app.controller.js | 2 +- .../controls/buttons/button.directives.js | 21 +- app/components/game/game.factory.js | 307 ++++++++++++------ .../game/grid/cell/cell.directive.js | 25 +- .../game/grid/cell/row.directive.js | 6 +- app/components/game/grid/grid.directive.js | 1 - app/index.html | 1 + 7 files changed, 252 insertions(+), 111 deletions(-) diff --git a/app/app.controller.js b/app/app.controller.js index 20cb05d..a97d9be 100644 --- a/app/app.controller.js +++ b/app/app.controller.js @@ -5,7 +5,7 @@ game.controller("app", ['$scope', '$window', 'cell', 'gameFactory', function($sc 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('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], diff --git a/app/components/game/controls/buttons/button.directives.js b/app/components/game/controls/buttons/button.directives.js index 2c67157..d64b16d 100644 --- a/app/components/game/controls/buttons/button.directives.js +++ b/app/components/game/controls/buttons/button.directives.js @@ -15,13 +15,28 @@ game.directive("pause", function(){ } }) -game.directive("play", function(){ +game.directive("play", ['stepFactory', function(stepFn){ return { restrict: "E", replace: true, - template: "" + template: ``, + link: function(scope, playBtn){ + scope.autoPlayOn = false; + scope.enableAutoPlay = function(){ + if (scope.autoPlayOn) { + scope.autoPlayOn = false + clearInterval(scope.setIntervalID) + playBtn[0].innerHTML = 'Play' + } else { + debugger; + scope.autoPlayOn = true + scope.setIntervalID = setInterval(stepFn.bind(scope), document.getElementById('step_amount').value || 100) + playBtn[0].innerHTML = 'Pause' + } + } + } } -}) +}]) game.directive("reset", function(){ return { diff --git a/app/components/game/game.factory.js b/app/components/game/game.factory.js index 4f5ac55..b554bf7 100644 --- a/app/components/game/game.factory.js +++ b/app/components/game/game.factory.js @@ -10,10 +10,11 @@ var synth = T("OscGen", (T("reverb", { }, T("sin", {mul:0.25})))).play(); -game.factory('gameFactory',['synthFactory', function(synth){ +game.factory('gameFactory',['synthFactory', 'stepFactory', 'forEachCell', function(synth, step, forEachCell){ return { width: 50, height: 50, + cellCount: this.width * this.height, stepInterval: null, autoPlayOn: false, setIntervalID: 0, @@ -41,24 +42,24 @@ game.factory('gameFactory',['synthFactory', function(synth){ 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) - }) - }) - - }, + // 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" @@ -90,7 +91,7 @@ game.factory('gameFactory',['synthFactory', function(synth){ this.setAttribute('style', 'background-color:#FFFFFF') } }; - this.forEachCell(function (cell) { + forEachCell(function (cell) { cell.onclick = onCellClick }) // var cell00 = document.getElementById('0-0'); @@ -122,92 +123,93 @@ game.factory('gameFactory',['synthFactory', function(synth){ // }); // }, -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 = []; +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); +// } +// }) +// }, - 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' - } - }, + // 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) { + forEachCell(function(cell) { cell.className = "dead"; cell.setAttribute('data-status', 'dead'); }) }, randomize: function () { - this.forEachCell(function (cell, x, y) { + forEachCell(function (cell, x, y) { var state = Math.floor(Math.random() * 2) if (state === 1) { cell.className = "dead" @@ -222,3 +224,114 @@ step: function () { }; }] ) + +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 state = 0; + return function () { + rootScope.$broadcast('step', state++); + 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) + }) + }) + + } +}) + +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 index 1a89f2f..20248be 100644 --- a/app/components/game/grid/cell/cell.directive.js +++ b/app/components/game/grid/cell/cell.directive.js @@ -1,12 +1,23 @@ 'use strict'; - -game.directive('cell', function(){ +game.directive('cell', ['cellClickFactory', '$log', function(cellClick, $log){ return { - restrict: 'EA', - template: ``, + restrict: 'E', + template: ``, + replace: true, + scope: { + "status": '@', + }, link: function(scope, cell, attrs){ - + scope.$on('step', function(){ + console.log('stepping'); + }) + scope.toggleStatus = function(){cellClick(scope, cell[0], attrs)} } - } -}) +}]) + + +// +// function onClick (scope, cell, attrs, cellClick){ +// return cellClick.bind(scope, cell, attrs); +// } diff --git a/app/components/game/grid/cell/row.directive.js b/app/components/game/grid/cell/row.directive.js index 8e9c07a..f574550 100644 --- a/app/components/game/grid/cell/row.directive.js +++ b/app/components/game/grid/cell/row.directive.js @@ -3,14 +3,16 @@ game.directive('row', function(){ return { restrict: 'EA', - template: ``, + template: ``, // scope: { // row: '@' // }, - transclude: false, controller: ['$scope', function($scope){ console.log($scope); // $scope.row = $scope.id; }] } }) + + + // template: ``, diff --git a/app/components/game/grid/grid.directive.js b/app/components/game/grid/grid.directive.js index 0264ca9..dddcf5d 100644 --- a/app/components/game/grid/grid.directive.js +++ b/app/components/game/grid/grid.directive.js @@ -5,7 +5,6 @@ game.directive('gameGrid',['cell', function(Cell){ template: `
`, replace: true, link: function(scope){ - debugger; scope.rows = makeRows(50, 50, Cell) } } diff --git a/app/index.html b/app/index.html index 5bc0dbd..fc0ee59 100644 --- a/app/index.html +++ b/app/index.html @@ -24,6 +24,7 @@ + From ae18e42f8505a89160630ef6a76de8b283d45aff Mon Sep 17 00:00:00 2001 From: thejohnbackes Date: Sun, 5 Jun 2016 16:36:16 -0400 Subject: [PATCH 7/8] foreach functionality moved to cell factory. step function is still broken. --- app/components/game/game.factory.js | 5 +- .../game/grid/cell/cell.directive.js | 11 ++-- app/components/game/grid/cell/cell.factory.js | 59 ++++++++++++++++--- 3 files changed, 60 insertions(+), 15 deletions(-) diff --git a/app/components/game/game.factory.js b/app/components/game/game.factory.js index b554bf7..2162b49 100644 --- a/app/components/game/game.factory.js +++ b/app/components/game/game.factory.js @@ -18,6 +18,7 @@ game.factory('gameFactory',['synthFactory', 'stepFactory', 'forEachCell', functi stepInterval: null, autoPlayOn: false, setIntervalID: 0, + currentStep: 0, createAndShowBoard: function () { // create element @@ -233,8 +234,8 @@ game.factory('cellStepFactory', [function(){ } }]) -game.factory('stepFactory', ['forEachCell', '$rootScope', function(forEachCell, rootScope){ - let state = 0; +game.factory('stepFactory', ['forEachCell', '$rootScope', 'gameFactory', function(forEachCell, rootScope, gameOfLife){ + let state = gameOfLife.currentStep; return function () { rootScope.$broadcast('step', state++); color = '#'+Math.floor(Math.random()*16777215).toString(16) diff --git a/app/components/game/grid/cell/cell.directive.js b/app/components/game/grid/cell/cell.directive.js index 20248be..7d9546d 100644 --- a/app/components/game/grid/cell/cell.directive.js +++ b/app/components/game/grid/cell/cell.directive.js @@ -1,5 +1,5 @@ 'use strict'; -game.directive('cell', ['cellClickFactory', '$log', function(cellClick, $log){ +game.directive('cell', ['cellClickFactory', '$log', 'cellFactory', 'gameFactory', function(cellClick, $log, cellFactory, gameFactory){ return { restrict: 'E', template: ``, @@ -8,10 +8,13 @@ game.directive('cell', ['cellClickFactory', '$log', function(cellClick, $log){ "status": '@', }, link: function(scope, cell, attrs){ - scope.$on('step', function(){ - console.log('stepping'); + let DOMcell = cell[0]; + scope.cellObj = new cellFactory(DOMcell, scope.$parent.$parent.index, scope.$parent.$index, gameFactory.cellCount, gameFactory.height, gameFactory.width, scope); + let cellObj = scope.cellObj; + scope.$on('step', function(previousStep, nextStep){ + cellObj.checkNeighbors(previousStep) }) - scope.toggleStatus = function(){cellClick(scope, cell[0], attrs)} + scope.toggleStatus = function(){cellClick(scope, cell[0], attrs)}; } } }]) diff --git a/app/components/game/grid/cell/cell.factory.js b/app/components/game/grid/cell/cell.factory.js index 71d3220..71f24a0 100644 --- a/app/components/game/grid/cell/cell.factory.js +++ b/app/components/game/grid/cell/cell.factory.js @@ -47,13 +47,14 @@ var pentatonicFrequencies = [ 7040*/ ]; - var Cell = class { - constructor(cell, x, y, status, deadCount, aliveCount, height, width) { - this.cell = document.getElementById(x + '-' + y); + 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]; @@ -62,17 +63,57 @@ var Cell = class { } generateFrequency(){} //generateRandomFrequency(){} - getNeighbors(){} - checkNeighbors(){} + get neighbors(){ + let x = this.x, y = this.y; + let neighbors = [ + Cell.getNeighbor(x-1, y-1, parent), + Cell.getNeighbor(x , y-1, parent), + Cell.getNeighbor(x+1, y-1, parent), + Cell.getNeighbor(x-1, y , parent), + Cell.getNeighbor(x+1, y , parent), + Cell.getNeighbor(x-1, y+1, parent), + Cell.getNeighbor(x , y+1, parent), + Cell.getNeighbor(x+1, y+1, parent) + ]; + } + static getNeighbor(x, y, parent){ + return x < 0 || x >= parent.width || y < 0 || y <= parent.height ? null : document.getElementById( x+'-'+y ).scope.cellObj; + } + checkNeighbors(step){ + return this.neighbors.reduce( (stateCount, neighbors) => stateCount + neighbor.states[step] === 'alive' ? 1 : 0, 0) + } + makeAliveOrDead(step){ + let neighborCount = this.checkNeighbors(step); + if(this.isAlive(step)){ + if(neighborCount < 2 || neighborCount > 3) { + this.makeDead(); + } + } + else{ + if(neighborCount === 3){ + this.makeAlive(); + } + } + this.registerStatus(); + } changeColor(){} toggleState(){} randomState(){} - isAlive(){} + isAlive(step = null){ + return step === null ? this.status === 'alive' : this.states[step] === 'alive'; + } toggleNote(){} - makeAlive(){} - makeDead(){} + makeAlive(){ + this.state = 'alive'; + } + makeDead(){ + this.state = 'dead'; + } + registerState(){ + this.state.push(this.status); + } } -game.factory('cell', function(){ +game.factory('cellFactory', function(){ return Cell; }); From d5fb844b789acdc77c81f67a3f89d7c13180e4c1 Mon Sep 17 00:00:00 2001 From: thejohnbackes Date: Mon, 6 Jun 2016 02:52:30 -0400 Subject: [PATCH 8/8] synth and color now moved entirely over to angular and working. bugs on the clear button. choose file is unchanged so probably broken. SIGNIFICANT performance improvements, but lots of room to get better. Timbre is creating a lot of overhead - how can we improve it? Note: the 'step' button broadcasts a 'step' event to all the cells. I think this could be optimized. Even so, the focus should be on the timbre function. look at cell.factory.js, the makeAliveOrDead function (lines 124 and 130) --- app/app.controller.js | 8 +- .../controls/buttons/button.directives.js | 45 ++++++-- app/components/game/game.factory.js | 102 +++++++++-------- .../game/grid/cell/cell.directive.js | 11 +- app/components/game/grid/cell/cell.factory.js | 105 ++++++++++++++---- .../game/grid/cell/row.directive.js | 6 +- app/components/game/grid/grid.directive.js | 31 +++--- app/components/game/synth/synth.factory.js | 8 +- 8 files changed, 210 insertions(+), 106 deletions(-) diff --git a/app/app.controller.js b/app/app.controller.js index a97d9be..a643f84 100644 --- a/app/app.controller.js +++ b/app/app.controller.js @@ -1,12 +1,12 @@ -game.controller("app", ['$scope', '$window', 'cell', 'gameFactory', function($scope, $window, Cell, gameOfLife){ +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('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('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) diff --git a/app/components/game/controls/buttons/button.directives.js b/app/components/game/controls/buttons/button.directives.js index d64b16d..8179aa6 100644 --- a/app/components/game/controls/buttons/button.directives.js +++ b/app/components/game/controls/buttons/button.directives.js @@ -1,9 +1,17 @@ -game.directive("clear", function(){ +game.directive("clear", function(forEachCell, cellFactory){ return { restrict: "E", replace: true, - template: "" + template: "", + link: function(scope){ + scope.clear = function(){ + forEachCell( function(cell){ + debugger; + let cellObj = angular.element(cell).scope() + cellObj.$$childHead.cellObj.makeDead(true)} + )} + } } }) @@ -15,13 +23,14 @@ game.directive("pause", function(){ } }) -game.directive("play", ['stepFactory', function(stepFn){ +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 @@ -30,7 +39,7 @@ game.directive("play", ['stepFactory', function(stepFn){ } else { debugger; scope.autoPlayOn = true - scope.setIntervalID = setInterval(stepFn.bind(scope), document.getElementById('step_amount').value || 100) + scope.setIntervalID = setInterval(function(){game.step()}, document.getElementById('step_amount').value || 100) playBtn[0].innerHTML = 'Pause' } } @@ -38,11 +47,26 @@ game.directive("play", ['stepFactory', function(stepFn){ } }]) -game.directive("reset", function(){ +game.directive("reset", function(forEachCell){ return { restrict: "E", replace: true, - template: "" + 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); + } + }) + + } + } } }) @@ -54,13 +78,16 @@ game.directive("save", function(){ } }) -game.directive("step", function(){ +game.directive("step", ['gameFactory', function(gameFactory){ return { restrict: "E", replace: true, - template: "" + template: "", + link: function(scope){ + scope.step = gameFactory.step; + } } -}) +}]) game.directive("submit", function(){ return { diff --git a/app/components/game/game.factory.js b/app/components/game/game.factory.js index 2162b49..32f26c9 100644 --- a/app/components/game/game.factory.js +++ b/app/components/game/game.factory.js @@ -9,12 +9,13 @@ var synth = T("OscGen", (T("reverb", { 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: this.width * this.height, + cellCount: 2500, stepInterval: null, autoPlayOn: false, setIntervalID: 0, @@ -211,13 +212,13 @@ step: step, 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) { - cell.className = "dead" - cell.setAttribute('data-status', 'dead'); + cellObj.makeDead(true); } else { - cell.className = 'alive' - cell.setAttribute('data-status', 'alive'); + cellObj.makeAlive(true); } }) @@ -234,11 +235,14 @@ game.factory('cellStepFactory', [function(){ } }]) -game.factory('stepFactory', ['forEachCell', '$rootScope', 'gameFactory', function(forEachCell, rootScope, gameOfLife){ - let state = gameOfLife.currentStep; +game.factory('stepFactory', ['forEachCell', '$rootScope', function(forEachCell, rootScope){ + let previousStep = 0; return function () { - rootScope.$broadcast('step', state++); - color = '#'+Math.floor(Math.random()*16777215).toString(16) + 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, @@ -259,45 +263,45 @@ game.factory('stepFactory', ['forEachCell', '$rootScope', 'gameFactory', functio // (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); - } - }) + // 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); + // } + // }) }; }]) @@ -322,6 +326,8 @@ game.factory('forEachCell', function(){ } }) +// 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') { diff --git a/app/components/game/grid/cell/cell.directive.js b/app/components/game/grid/cell/cell.directive.js index 7d9546d..87c1a12 100644 --- a/app/components/game/grid/cell/cell.directive.js +++ b/app/components/game/grid/cell/cell.directive.js @@ -9,12 +9,15 @@ game.directive('cell', ['cellClickFactory', '$log', 'cellFactory', 'gameFactory' }, link: function(scope, cell, attrs){ let DOMcell = cell[0]; - scope.cellObj = new cellFactory(DOMcell, scope.$parent.$parent.index, scope.$parent.$index, gameFactory.cellCount, gameFactory.height, gameFactory.width, scope); + 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(previousStep, nextStep){ - cellObj.checkNeighbors(previousStep) + scope.$on('step', function(e, previousStep, nextStep){ + cellObj.makeAliveOrDead(previousStep) }) - scope.toggleStatus = function(){cellClick(scope, cell[0], attrs)}; + scope.toggleStatus = function(){ + //cellClick(scope, cell[0], attrs) + cellObj.click(); + }; } } }]) diff --git a/app/components/game/grid/cell/cell.factory.js b/app/components/game/grid/cell/cell.factory.js index 71f24a0..cc84a38 100644 --- a/app/components/game/grid/cell/cell.factory.js +++ b/app/components/game/grid/cell/cell.factory.js @@ -47,7 +47,13 @@ var pentatonicFrequencies = [ 7040*/ ]; -var Cell = class { +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; @@ -58,7 +64,7 @@ var Cell = class { this.deadCount = deadCount this.aliveCount = aliveCount this.freq = pentatonicFrequencies[x % pentatonicFrequencies.length]; - this.velocity = 1000* (y / width); + this.velocity = 500* (y / width); this.parent = {height, width} } generateFrequency(){} @@ -66,54 +72,113 @@ var Cell = class { get neighbors(){ let x = this.x, y = this.y; let neighbors = [ - Cell.getNeighbor(x-1, y-1, parent), - Cell.getNeighbor(x , y-1, parent), - Cell.getNeighbor(x+1, y-1, parent), - Cell.getNeighbor(x-1, y , parent), - Cell.getNeighbor(x+1, y , parent), - Cell.getNeighbor(x-1, y+1, parent), - Cell.getNeighbor(x , y+1, parent), - Cell.getNeighbor(x+1, y+1, parent) + 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){ - return x < 0 || x >= parent.width || y < 0 || y <= parent.height ? null : document.getElementById( x+'-'+y ).scope.cellObj; + 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){ - return this.neighbors.reduce( (stateCount, neighbors) => stateCount + neighbor.states[step] === 'alive' ? 1 : 0, 0) + 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.registerStatus(); + this.registerState(); + } + changeColor(){ + return '#'+Math.floor(Math.random()*16777215).toString(16); } - changeColor(){} toggleState(){} randomState(){} isAlive(step = null){ return step === null ? this.status === 'alive' : this.states[step] === 'alive'; } toggleNote(){} - makeAlive(){ - this.state = 'alive'; + 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(){ - this.state = 'dead'; + 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.state.push(this.status); + 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') + } } } -game.factory('cellFactory', function(){ + return Cell; }); diff --git a/app/components/game/grid/cell/row.directive.js b/app/components/game/grid/cell/row.directive.js index f574550..84e96d0 100644 --- a/app/components/game/grid/cell/row.directive.js +++ b/app/components/game/grid/cell/row.directive.js @@ -3,12 +3,14 @@ game.directive('row', function(){ return { restrict: 'EA', - template: ``, + template: ``, // scope: { // row: '@' // }, + link: function(scope){ + scope.cells = new Array(50) + }, controller: ['$scope', function($scope){ - console.log($scope); // $scope.row = $scope.id; }] } diff --git a/app/components/game/grid/grid.directive.js b/app/components/game/grid/grid.directive.js index dddcf5d..61378a2 100644 --- a/app/components/game/grid/grid.directive.js +++ b/app/components/game/grid/grid.directive.js @@ -1,24 +1,25 @@ 'use strict'; -game.directive('gameGrid',['cell', function(Cell){ +game.directive('gameGrid',['cellFactory', function(Cell){ return { - restrict: "E", + restrict: "EA", template: `
`, replace: true, link: function(scope){ - scope.rows = makeRows(50, 50, Cell) + // 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) - }) -} +// 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/app/components/game/synth/synth.factory.js b/app/components/game/synth/synth.factory.js index ef9e625..da511a7 100644 --- a/app/components/game/synth/synth.factory.js +++ b/app/components/game/synth/synth.factory.js @@ -3,9 +3,7 @@ var Synth = class{ constructor(){ - this.locals = { - sines: [] - } + this.sines = []; return T("OscGen", (T("reverb", { room: 0.95, damp: 0.1, @@ -15,9 +13,11 @@ var Synth = class{ } function createSynth(T){ - return Synth + return new Synth() } game.factory('synthFactory',function($window){ + debugger; + var synth = createSynth($window.T); return createSynth($window.T); });