-
Notifications
You must be signed in to change notification settings - Fork 0
/
app.js
286 lines (253 loc) · 8.2 KB
/
app.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
var app = angular.module('conway', []);
app.controller('mainCtrl', ['$scope', function ($scope) {
// Initialize spped in milliseconds
$scope.speed = 200;
// Initialize size of each cell at 15 pixels
$scope.cellSize = 13;
// Initialize number of alive cells and generations
$scope.aliveCells = 0;
$scope.alivePercentage = 0;
$scope.generations = 0;
// Start the board in paused state
$scope.status = 'paused';
/*
Revealing module containing all of the logic
Public methods are exposed in return
*/
$scope.conway = (function () {
// Set colors for dead and alive cells
var deadColor = '#9899A6';
var aliveColor = '#5465E5';
// Declare module state variables
var loop,
grid,
canvas,
ctx,
canvasLeft,
canvasTop,
gridWidth,
cellsPerRow,
numberOfCells,
cellInnerDimension,
aliveCells = 0;
// Initialize canvas
canvas = document.getElementById('canvas');
canvasLeft = canvas.offsetLeft;
canvasTop = canvas.offsetTop;
if (canvas.getContext) {
ctx = canvas.getContext('2d');
}
function initCanvas() {
grid = [];
// Set the dimensions of the grid and cells
gridWidth = 80 * $scope.cellSize;
cellsPerRow = gridWidth / $scope.cellSize;
numberOfCells = Math.pow(cellsPerRow, 2);
cellInnerDimension = $scope.cellSize-1;
}
// Constructor function that creates new cells
function Cell(left, top, width, height, index) {
// Set the placement and dimensions of the cell
this.left = left;
this.top = top;
this.width = width;
this.height = height;
// Set the index of the cell in the grid
this.index = index;
// Initialize the cell as dead
this.alive = false;
this.fillColor = deadColor;
// Initialize the number of alive neighbors to 0
this.aliveNeighbors = 0;
}
// Takes a cell as a parameter and draws a rect in the canvas
function Draw(cell) {
ctx.fillStyle = cell.fillColor;
ctx.fillRect(cell.left, cell.top, cell.width, cell.height);
}
// If the board is paused, run() sets window interval and redraws the board
function run() {
$scope.status === 'paused' ? loop = window.setInterval(redrawBoard, $scope.speed) : null;
$scope.status = 'running';
}
// Clear window interval and set state to paused
function pause() {
clearInterval(loop);
$scope.status = 'paused';
}
// Reset the board to initial state
function reset() {
$scope.generations = 0;
$scope.aliveCells = 0;
$scope.alivePercentage = 0;
pause();
initBoard();
}
// Initialize a new board with the randomize flag set to true
function randomize() {
initBoard(true);
}
// Pause the board and redraw only 1 generation
function nextStep(){
pause();
window.setTimeout(redrawBoard, 300);
}
// Lower the window interval period, speeding up the board redraw
function speedUp() {
if ($scope.speed > 50) {
$scope.speed -= 50;
if ($scope.status === 'running') {
pause();
run();
}
}
}
// Increase the window interval period, slowing down the board redraw
function slowDown() {
if ($scope.speed < 800) {
$scope.speed += 50;
if ($scope.status === 'running') {
pause();
run();
}
}
}
// Initialize the game board, used on initial load and reset
function initBoard(randomize) {
initCanvas();
// Fill up the canvas with new Cell objects
for (var xPoint = 0, yPoint = 0, index = 0; index < numberOfCells; index++) {
// Create new cell
var thisShape = new Cell(xPoint, yPoint, cellInnerDimension, cellInnerDimension, index);
// If randomize flag is true, randomly assign cells to alive state
if (randomize) {
$scope.aliveCells = 0;
$scope.alivePercentage = 0;
var randNum = Math.floor(Math.random()*17);
if (randNum <= 2) {
thisShape.alive = true;
$scope.aliveCells += 1;
$scope.alivePercentage = ($scope.aliveCells/6400)*100;
thisShape.fillColor = aliveColor;
}
}
// Add the new cell to the grid
grid.push(thisShape);
// Increment x-axis by the width of a standard cell
xPoint += $scope.cellSize;
// If x-axis has reached max width of the grid,
// move x-axis to beginning of the line and increment
// the y-axis by the height of a standard cell
if (xPoint % gridWidth === 0) {
xPoint = 0;
yPoint += $scope.cellSize;
}
// Draw the cell we just created to the canvas
Draw(grid[index]);
}
}
// Calculates the number of neighboring cells that are alive
function calcNeighbors() {
// Set o to the number of cells in each row
var o = cellsPerRow;
// neighbors is an array containing the locations of all of the cells
// that are directly adjacent to the current cell
var neighbors = [-(o+1),-o,-(o-1),-1,1,o-1,o,o+1];
var aliveNeighbors = 0;
// Loop through the grid and find alive neighbors for each cell
grid.forEach(function(cell) {
cell.aliveNeighbors = 0;
// Loop through the neighbors array and for each neighbor of the
// current cell, check if it is alive
// if so, increment aliveNeighbors by 1
neighbors.forEach(function(x) {
var index = cell.index + x;
if (index > -1){
if (grid[index] && grid[index].alive === true){
cell.aliveNeighbors += 1;
}
}
});
})
}
// Set a cell to alive
function birth(cell) {
cell.alive = true;
cell.fillColor = aliveColor;
}
// Set a cell to dead
function death(cell) {
cell.alive = false;
cell.fillColor = deadColor;
}
// redrawBoard is called on each interval or setTimeout
function redrawBoard(board) {
calcNeighbors();
aliveCells = 0;
$scope.generations += 1;
for (var k = 0; k < numberOfCells; k++) {
var element = grid[k];
var aliveNeighbors = element.aliveNeighbors;
if (element && element.alive){
if (element.aliveNeighbors < 2) {
death(element);
} else if (element.aliveNeighbors > 3) {
death(element);
} else if (element.aliveNeighbors == 2 || element.aliveNeighbors == 3) {
birth(element);
}
} else {
if (element.aliveNeighbors == 3) {
birth(element);
}
}
}
grid.forEach(function(cell) {
if (cell.alive) {
aliveCells += 1;
}
Draw(cell);
});
$scope.aliveCells = aliveCells;
$scope.alivePercentage = ($scope.aliveCells/6400)*100;
$scope.$apply();
}
// Click handler to turn cells dead/alive
canvas.addEventListener('click', function(event) {
var x = event.pageX - canvasLeft,
y = event.pageY - canvasTop;
grid.forEach(function(element) {
if (y > element.top && y < element.top + element.height
&& x > element.left && x < element.left + element.width) {
if (element.fillColor === deadColor){
element.fillColor = aliveColor;
element.alive = true;
$scope.aliveCells += 1;
$scope.$apply();
Draw(element);
} else {
element.fillColor = deadColor;
element.alive = false;
$scope.aliveCells -= 1;
$scope.$apply();
Draw(element);
}
}
$scope.alivePercentage = ($scope.aliveCells/6400)*100;
});
}, false);
// Return public methods, accessible through $scope.conway
return {
run: run,
pause: pause,
nextStep: nextStep,
reset: reset,
randomize: randomize,
initBoard: initBoard,
speedUp: speedUp,
slowDown: slowDown
};
})();
// On page load, initialize the board
$scope.conway.initBoard();
}])