├── 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 |
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 |
--------------------------------------------------------------------------------