├── README.md ├── index.html ├── canvashelper.js └── hardlife.js /README.md: -------------------------------------------------------------------------------- 1 | HardLifeJs 2 | ========== 3 | 4 | Hard Life problem demo for CodeAbbey 5 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Game of Life in JavaScript 6 | 7 | 8 | 9 | 10 |
11 |

Game of life simulator

12 |
For CodeAbbey problem Hard Life


13 | 14 |
15 | 16 | 17 | 18 |
19 | 20 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /canvashelper.js: -------------------------------------------------------------------------------- 1 | function CanvasHelper(canvas, context) { 2 | this.canvas = canvas; 3 | this.ctx = context; 4 | this.rndval = 0; 5 | } 6 | 7 | CanvasHelper.prototype.lineRel = function(x1, y1, dx, dy) { 8 | this.line(x1, y1, x1 + dx, y1 + dy); 9 | } 10 | 11 | CanvasHelper.prototype.line = function(x1, y1, x2, y2) { 12 | var ctx = this.ctx; 13 | ctx.beginPath(); 14 | ctx.moveTo(x1, y1); 15 | ctx.lineTo(x2, y2); 16 | ctx.closePath(); 17 | ctx.stroke(); 18 | } 19 | 20 | CanvasHelper.prototype.circle = function(x, y, r) { 21 | var ctx = this.ctx; 22 | ctx.beginPath(); 23 | ctx.arc(x, y, r, 0, 2 * Math.PI, false); 24 | ctx.closePath(); 25 | ctx.fill(); 26 | ctx.stroke(); 27 | } 28 | 29 | CanvasHelper.prototype.posFromEvent = function(e) { 30 | var e = e || window.event; 31 | var cnv = this.canvas; 32 | var offsetX = e.pageX - cnv.clientLeft - cnv.offsetLeft; 33 | var offsetY = e.pageY - cnv.clientTop - cnv.offsetTop; 34 | return {x: offsetX, y: offsetY}; 35 | } 36 | 37 | CanvasHelper.prototype.rand = function(seed) { 38 | if (typeof(seed) != 'undefined') { 39 | this.rndval = seed; 40 | } 41 | this.rndval = parseFloat('0.' + Math.sin(this.rndval + 0.31415926).toString().substr(6)); 42 | return this.rndval; 43 | } 44 | -------------------------------------------------------------------------------- /hardlife.js: -------------------------------------------------------------------------------- 1 | function HardLife(opts) { 2 | this.presets(); 3 | this.size = 2; 4 | this.w = 251; 5 | this.h = 151; 6 | if (typeof(opts) == 'object') { 7 | for (var key in opts) { 8 | this[key] = opts[key]; 9 | } 10 | } 11 | this.init(); 12 | } 13 | 14 | HardLife.prototype.presets = function() { 15 | this.neighs = []; 16 | for (var i = 0; i < 9; i++) { 17 | var c = {x: i % 3 - 1, y: Math.floor(i / 3) - 1}; 18 | if (c.x != 0 || c.y != 0) { 19 | this.neighs.push(c); 20 | } 21 | } 22 | } 23 | 24 | HardLife.prototype.init = function() { 25 | var canvas = document.getElementById('demo'); 26 | this.setupGeometry(canvas); 27 | this.ctx = canvas.getContext('2d'); 28 | this.ch = new CanvasHelper(canvas, this.ctx); 29 | this.initialSetup(); 30 | this.draw(); 31 | var self = this; 32 | canvas.onmousedown = function(e) {self.click(e)}; 33 | } 34 | 35 | HardLife.prototype.setupGeometry = function(canvas) { 36 | canvas.width = this.size * this.w; 37 | canvas.height = this.size * this.h; 38 | } 39 | 40 | HardLife.prototype.initialSetup = function() { 41 | this.cells = []; 42 | this.count = 0; 43 | if (Math.random() > 0.5) { 44 | this.pattern(0, 0, [0, 0, -1, 0, 0, -1, 0, 1, 1, 1]); 45 | } else { 46 | this.pattern(0, 0, [0, 0, 1, 0, 1, 2, 3, 1, 4, 0, 5, 0, 6, 0]); 47 | } 48 | var delta = Math.floor(Math.random() * 20) + 50; 49 | var offs = Math.floor(Math.random() * 20) - 10; 50 | this.pattern(offs - delta, delta, [-2, 0, -1, 0, 0, 0, 0, 1, -1, 2]); 51 | this.moves = 0; 52 | } 53 | 54 | HardLife.prototype.pattern = function(x, y, p) { 55 | for (var i = 0; i < p.length; i += 2) { 56 | var px = p[i] + x; 57 | var py = p[i + 1] + y; 58 | var cell = px + ' ' + py; 59 | if (!this.cells[cell]) { 60 | this.cells[cell] = 1; 61 | this.count += 1; 62 | } 63 | } 64 | } 65 | 66 | HardLife.prototype.draw = function() { 67 | var ctx = this.ctx; 68 | ctx.font = "12pt Arial"; 69 | ctx.textBaseLine = "bottom"; 70 | ctx.textAlign = "left"; 71 | ctx.fillStyle = '#000088'; 72 | ctx.fillRect(0, 0, this.w * this.size, this.h * this.size); 73 | ctx.fillStyle = '#00C000'; 74 | ctx.fillText(this.count, 0, this.h * this.size - 1); 75 | ctx.textAlign = "right"; 76 | ctx.fillText(this.moves, this.w * this.size - 1, this.h * this.size - 1); 77 | var cx = Math.floor(this.w / 2) * this.size; 78 | var cy = Math.floor(this.h / 2) * this.size; 79 | ctx.fillStyle = '#FFFF00'; 80 | for (var c in this.cells) { 81 | var xy = c.split(' '); 82 | var x = cx + (xy[0] * this.size); 83 | var y = cy - (xy[1] * this.size); 84 | ctx.fillRect(x, y, this.size, this.size); 85 | } 86 | } 87 | 88 | HardLife.prototype.step = function() { 89 | var dead = this.generateDead(); 90 | var born = this.generateBorn(); 91 | for (var dcell in dead) { 92 | delete this.cells[dcell]; 93 | this.count -= 1; 94 | } 95 | for (var bcell in born) { 96 | this.cells[bcell] = 1; 97 | this.count += 1; 98 | } 99 | this.moves += 1; 100 | this.draw(); 101 | } 102 | 103 | HardLife.prototype.generateDead = function() { 104 | var dead = []; 105 | for (var cell in this.cells) { 106 | var ns = this.countNeighs(cell); 107 | if (ns < 2 || ns > 3) { 108 | dead[cell] = 1; 109 | } 110 | } 111 | return dead; 112 | } 113 | 114 | HardLife.prototype.generateBorn = function() { 115 | var emneis = this.generateEmptyNeighs(); 116 | var res = []; 117 | for (var nei in emneis) { 118 | if (this.countNeighs(nei) == 3) { 119 | res[nei] = 1; 120 | } 121 | } 122 | return res; 123 | } 124 | 125 | HardLife.prototype.generateEmptyNeighs = function() { 126 | var res = []; 127 | var self = this; 128 | for (var cell in this.cells) { 129 | this.enumNeighs(cell, function(x, y) { 130 | var xy = x + ' ' + y; 131 | if (!self.cells[xy]) { 132 | res[x + ' ' + y] = 1; 133 | } 134 | }); 135 | } 136 | return res; 137 | } 138 | 139 | HardLife.prototype.countNeighs = function(cell) { 140 | var res = 0; 141 | var self = this; 142 | this.enumNeighs(cell, function(x, y) { 143 | if (self.cells[x + ' ' + y]) { 144 | res++; 145 | } 146 | }); 147 | return res; 148 | } 149 | 150 | HardLife.prototype.enumNeighs = function(cell, f) { 151 | var xy = cell.split(' '); 152 | var x = xy[0] * 1; 153 | var y = xy[1] * 1; 154 | var res = 0; 155 | for (var i in this.neighs) { 156 | var nr = this.neighs[i]; 157 | f(nr.x + x, nr.y + y); 158 | } 159 | } 160 | 161 | HardLife.prototype.run = function() { 162 | var self = this; 163 | var runner = function() { 164 | if (!self.running) { 165 | return; 166 | } 167 | self.step(); 168 | setTimeout(runner, 50); 169 | } 170 | 171 | if (this.running) { 172 | delete this.running; 173 | } else { 174 | this.running = true; 175 | runner(); 176 | } 177 | } 178 | 179 | HardLife.prototype.oneStep = function() { 180 | if (this.running) { 181 | return; 182 | } 183 | step(); 184 | } 185 | 186 | HardLife.prototype.reset = function() { 187 | if (this.running) { 188 | this.run(); 189 | } 190 | this.initialSetup(); 191 | this.draw(); 192 | } 193 | 194 | HardLife.prototype.click = function(event) { 195 | if (this.running) { 196 | return; 197 | } 198 | event.preventDefault(); 199 | var pos = this.ch.posFromEvent(event); 200 | pos.x -= Math.floor(this.w / 2) * this.size; 201 | pos.y -= Math.floor(this.h / 2) * this.size; 202 | pos.x = Math.floor(pos.x / this.size); 203 | pos.y = - Math.floor(pos.y / this.size) + 1; 204 | var cell = pos.x + ' ' + pos.y; 205 | if (this.cells[cell]) { 206 | delete this.cells[cell]; 207 | this.count -= 1; 208 | } else { 209 | this.cells[cell] = 1; 210 | this.count += 1; 211 | } 212 | this.draw(); 213 | } 214 | 215 | --------------------------------------------------------------------------------