dumb by default


dir: Home / Applets / Conway's game of life (64x64)
published-date: 11 Jun 2025 09:25 +0700

Conway's game of life (64x64)


  1window.onload = function()
  2{
  3
  4const canvas = document.getElementById("viewport");
  5const ctx = canvas.getContext("2d", {"alpha": false});
  6const ftoi = (v)=>~~v;
  7const iwrap = (a, b) => a<0?b+(a%b):a%b;
  8const isNumeric = (v) => !isNaN(parseFloat(v)) && isFinite(v);
  9var sim = null;
 10var inputProxy = null;
 11var timer = null;
 12var recorder = null;
 13const states = {};
 14states.running = 1;
 15states.recording = 0;
 16
 17
 18function cTimer () {
 19  this.f = 0;
 20  this.fps = 0;
 21  this.t = 0;
 22  this.tt = 0;
 23  this.pt = 0;
 24  this.dt = 0;
 25  this.update = () => {
 26    this.f += 1;
 27    this.t = Date.now();
 28    this.dt = this.t - this.pt;
 29    this.tt += this.dt;
 30    this.pt = this.t;
 31  }
 32  this.ontimed = (t, cb) => {
 33    if (this.tt > t) {
 34      this.tt = 0;
 35      cb();
 36    }
 37  }
 38
 39};
 40
 41
 42function cInputProxy () {
 43  this.values = {};
 44  this.target = {};
 45  this.display = {};
 46  this.defaults = {};
 47  this.register = (name, _default=null, cast=(v)=>v) => {
 48    this.target[name] = document.getElementById(name);
 49    this.display[name] = this.target[name].parentElement.querySelector("#" + name + "_value");
 50    this.values[name] = cast(this.target[name].value);
 51    this.defaults[name] = _default;
 52    this.target[name].addEventListener("input", (event) => {
 53      this.values[name] = cast(event.target.value);
 54      if (this.display[name] !== null) {
 55        this.display[name].innerHTML = "" + event.target.value;
 56      };
 57    });
 58    if (_default !== null) {
 59      this.target[name].value = "" + _default;
 60      this.values[name] = cast(_default);
 61      if (this.display[name] !== null) {
 62        this.display[name].innerHTML = "" + _default;
 63      };
 64    };
 65    return this.target[name];
 66  };
 67};
 68
 69
 70class ac2DMatrix {
 71  itox(i) {return i % this.w};
 72  itoy(i) {return ftoi(i / this.w)};
 73  ctoi(x, y) {return x + (y * this.w)};
 74  get(x, y) {return this.buf[this.ctoi(x, y)]};
 75  set(x, y, v) {this.buf[this.ctoi(x, y)] = v;};
 76  map(cb) {for (let i=0; i<this.l; i++){this.buf[i] = cb(this.buf[i])}};
 77  zeros() {for (let i=0; i<this.l; i++) {this.buf[i] = 0}};
 78  sum() {let s=0; for (let i=0; i<this.l; i++) {s+=this.buf[i]}; return s};
 79  count(o) {let s=0; for (let i=0; i<this.l; i++) {if(this.buf[i]===o){s+=1}}; return s};
 80  load2DArray(arr) {
 81    for (let x=0; x<this.w; x++) {
 82      for (let y=0; y<this.h; y++) {
 83        this.set(x, y, arr[y][x]);
 84      };
 85    };
 86  };
 87  to2DArray() {
 88    let r = []
 89    for (let y=0; y<this.h; y++) {
 90      r.push([]);
 91      for (let x=0; x<this.w; x++) {
 92        r[y].push(this.get(x, y));
 93      };
 94    };
 95    return r;
 96  };
 97  queryKernel(mk, x, y, wrap) {
 98    const lx = ftoi(mk.w/2);
 99    const ly = ftoi(mk.h/2);
100    for (let kx=0; kx<mk.w; kx++) {
101      for (let ky=0; ky<mk.h; ky++) {
102        let dx = x+kx-lx;
103        let dy = y+ky-ly;
104        if (wrap||false) {
105          dx = iwrap(dx, this.w)
106          dy = iwrap(dy, this.h)
107        };
108        mk.set(kx, ky, this.get(dx, dy) || null)
109      };
110    };
111  };
112};
113
114
115class cUint8Matrix extends ac2DMatrix {
116  constructor (w, h) {
117    super();
118    this.w = w; this.h = h; this.l = w*h;
119    this.buf = new Uint8Array(this.l);
120  };
121};
122
123
124class CanvasRecorder {
125  constructor (canvas) {
126    this.canvas = canvas;
127    this.media = null;
128    this.data = [];
129    this.timeout = null;
130  };
131  start(onstop, fps) {
132    if (this.media !== null) return;
133    const stream = this.canvas.captureStream();
134    this.media  = new MediaRecorder(stream);
135    this.media.ondataavailable = (chunk)=>this.data.push(chunk.data);
136    this.media.onstop = ()=>{this.save(); onstop()};
137    this.timeout = setTimeout(()=>{this.stop()}, 20000);
138    this.media.start();
139    console.log('recording started')
140  };
141  stop() {
142    console.log('recording stopped')
143    if (this.timeout !== null) {
144      clearTimeout(this.timeout)
145    }
146    this.timeout = null;
147    if (this.media !== null && this.media.state === 'recording') {
148      this.media.stop();
149    }
150  };
151  save() {
152    if (this.media === null) throw "media is null";
153    if (this.data.length === 0) return;
154    const link = document.createElement('a');
155    link.href = URL.createObjectURL(new Blob(this.data, {type:'video/webm'}))
156    link.download = 'gol.webm';
157    link.click();
158    this.destroy();
159  };
160  destroy() {
161    this.data.length = 0;
162    this.media = null;
163    console.log('recorder destroyed')
164  };
165};
166
167class CellRenderer {
168  constructor (w, h, cw, ch) {
169    this.w = w; this.cw = cw;
170    this.h = h; this.ch = ch;
171    this.rx = w / cw;
172    this.ry = h / ch;
173  };
174  draw(ctx, x, y) {
175    ctx.rect(x, y, this.rx, this.ry)
176  };
177};
178
179class ParticleSim {
180  constructor (w, h) {
181    this.w = w; this.h = h, this.l=this.w*this.h;
182    this.zb = [new cUint8Matrix(this.w, this.h), new cUint8Matrix(this.w, this.h)];
183    this.z = this.zb[0];
184    this.k = null;
185    this.rules = {};
186    this.colors = ["white"];
187    this.renderQueue = Array.from({length:this.colors.length+1}, ()=>[])
188  };
189  swpz() {
190    this.z = this.zb[1];
191    this.zb[1] = this.zb[0];
192    this.zb[0] = this.z;
193  };
194  clearRenderQueue() {
195    for (let n=0; n<this.renderQueue.length; n++) {
196      if (this.renderQueue[n].length){
197        this.renderQueue[n].length = 0;
198      };
199    };
200  };
201  update() {
202    if (this.k === null) {throw "kernel is null"};
203    let a = this.zb[0];
204    let b = this.zb[1];
205    let v = null;
206    b.zeros();
207    this.clearRenderQueue()
208    for (let i=0; i<a.l; i++) {
209      let x = a.itox(i);
210      let y = a.itoy(i);
211      a.queryKernel(this.k, x, y, true);
212      v = a.get(x,y);
213      v = this.rules[v](this.k);
214      if (v > 0) {
215        this.renderQueue[v].push(i);
216      };
217      b.set(b.itox(i), b.itoy(i), v);
218    };
219    this.swpz();
220  };
221  setCell(x, y, v) {
222    this.z.set(iwrap(x, this.w), iwrap(y, this.h), v);
223    this.renderQueue[v].push(this.z.ctoi(iwrap(x, this.w), iwrap(y, this.h)))
224  };
225  render(ctx, w, h) {
226    const rx = (w-1) / this.w;
227    const ry = (h-1) / this.h;
228    const px = 2;
229    var i;
230    ctx.strokeStyle = "rgba(255,255,255,.1)";
231    ctx.beginPath();
232    for (let x=0; x<=this.w; x++) {
233      ctx.moveTo(x*rx+1, 0);
234      ctx.lineTo(x*rx+1, h);
235    };
236    for (let y=0; y<=this.h; y++) {
237      ctx.moveTo(0, y*ry+1);
238      ctx.lineTo(w, y*ry+1);
239    }
240    ctx.stroke();
241    ctx.strokeStyle = "rgba(255,255,255,.5)";
242    ctx.beginPath();
243    for (let x=0; x<=this.w; x+=10) {
244      ctx.moveTo(x*rx+1, 0);
245      ctx.lineTo(x*rx+1, 10);
246    };
247    for (let y=0; y<=this.h; y+=10) {
248      ctx.moveTo(0, y*ry+1);
249      ctx.lineTo(10, y*ry+1);
250    }
251    ctx.stroke();
252    for (let n=0; n<this.renderQueue.length; n++) {
253      if (this.renderQueue[n].length > 0) {
254        ctx.beginPath();
255        ctx.fillStyle = this.colors[(n+1)%this.colors.length]
256        while (this.renderQueue[n].length > 0) {
257          i = this.renderQueue[n].pop();
258          ctx.rect(rx*this.z.itox(i)+px, ry*this.z.itoy(i)+px, rx-px, ry-px)
259        };
260        ctx.fill();
261      };
262    };
263  };
264  reset() {
265    this.zb[0].zeros();
266    this.zb[1].zeros();
267  };
268};
269
270
271const rulesSet = {};
272rulesSet['B3/S23'] = (sim) => {
273  sim.k = new cUint8Matrix(3, 3);
274  sim.rules[0] = (k) => {
275    let c = k.count(1);
276    return c === 3 ? 1 : 0;
277  };
278  sim.rules[1] = (k) => {
279    let c = k.count(1) - 1;
280    return (c === 2 || c === 3) ? 1 : 0;
281  };  
282};
283
284
285function validateRLE(s) {
286  s = s.replace(/\s/g, "");
287  let ret = true;
288  let chr = "";
289  for (let i=0; i<s.length; i++) {
290    chr = s.charAt(i);
291    ret &&= (chr === "$") || (chr === "b") || (chr === "o") || isNumeric(chr) || (i === s.length - 1 &&  chr === "!");
292  }
293  return ret
294};
295
296function parseRLE(s) {
297  s = s.replace(/\s/g, "");
298  let chr = "";
299  let num = "";
300  let tmp = 0;
301  let row = 0;
302  let off = 0;
303  let ret = [];
304  const flushNum = () => {
305    if (num === "") {
306      return 1;
307    };
308    tmp = parseInt(num);
309    num = "";
310    return tmp
311  };
312  for (let i=0; i<s.length; i++) {
313    chr = s.charAt(i);
314    if (chr === "!") {
315      break
316    } else if (isNumeric(chr)) {
317      num += chr;
318    } else if (chr === "$") {
319      row += flushNum();
320      off = 0;
321    } else if (chr === "b") {
322      off += flushNum();
323    } else if (chr === "o") {
324      tmp = flushNum();
325      for (let j=0; j<tmp; j++) {
326        ret.push([off, row]);
327        off += 1;
328      };
329    } else {
330      throw "rule undefined for token " + chr;
331    };
332  };
333  return ret
334};
335
336const presets = {};
337const e1ul = [[0,0],[1,0],[0,1],[1,2],[2,2],[3,2],[3,3]]
338const e1dr = [[0,0],[0,1],[1,1],[2,1],[3,2],[2,3],[3,3]]
339
340presets['placeholder'] = [[[0,0]]];
341presets['glider1'] = [];
342const g1p1dr = [[1,0],[2,2],[2,1],[1,2],[0,2]];
343const g1p2dr = [[0,0],[1,1],[2,1],[0,2],[1,2]];
344const g1p1ur = [[0,0],[1,0],[1,1],[2,1],[0,2]];
345const g1p2ur = [[0,0],[1,0],[2,0],[2,1],[1,2]];
346const g1p1dl = [[1,0],[0,1],[0,2],[1,2],[2,2]];
347const g1p2dl = [[2,0],[0,1],[1,1],[1,2],[2,2]];
348const g1p1ul = [[0,0],[1,0],[2,0],[0,1],[1,2]];
349const g1p2ul = [[0,1],[1,0],[1,1],[2,0],[2,2]];
350for (let i=0; i<4; i++) {
351  presets['glider1'].push([[ i*16, i*16], ...(Math.random() > .5 ? g1p1dr: g1p2dr)])
352  presets['glider1'].push([[ i*16+32-8, i*16-8], ...(Math.random() > .5 ? g1p1ul: g1p2ul)])
353  presets['glider1'].push([[-i*16+32-8, i*16], ...(Math.random() > .5 ? g1p1ur: g1p2ur)])
354  presets['glider1'].push([[-i*16+8-8 , i*16-8], ...(Math.random() > .5 ? g1p1dl: g1p2dl)])
355};
356
357presets['glider2'] = [];
358const g2p1 = [[1,1],[4,1],[5,2],[1,3],[5,3],[2,4],[3,4],[4,4],[5,4]];
359const g2p2 = [[4,1],[2,2],[6,2],[7,3],[2,4],[7,4],[3,5],[4,5],[5,5],[6,5],[7,5]];
360const g2p3 = [[4,1],[5,1],[2,2],[7,2],[8,3],[2,4],[8,4],[3,5],[4,5],[5,5],[6,5],[7,5],[8,5]];
361for (let i=0; i<8; i++) {presets['glider2'].push([[i*8, 20+0], ...g2p1])};
362for (let i=0; i<8; i++) {presets['glider2'].push([[i*8, 20+8], ...g2p2])};
363for (let i=0; i<4; i++) {presets['glider2'].push([[i*16,20+16], ...g2p3])};
364
365presets['glider3'] = [
366  [[0,3], ...parseRLE("4o$o3bo$o$bo2bo2$5b4o$4bo2bo$4bo2bo$4bo2bo$5b4o2$bo2bo$o$o3bo$4o!")],
367  [[15,2], ...parseRLE("2bo6b$2o7b$o2b3o2bo$o4b3ob$b3o2b2ob2$b3o2b2ob$o4b3ob$o2b3o2bo$2o7b$2bo")],
368  [[50,10], ...parseRLE("3bo$bo3bo$o$o4bo$5o2$5b2o$5b2o2$6b2o$4bo4bo$3bo$3bo5bo$3b6o!")],
369  [[32,4], ...parseRLE("2$11bo$5b3o2bobo$5bo6bo$5bobo3bo$6b2o$5b2o$3b4o$2b2o3bo$b2ob2ob2o$2bo" +
370                        "4bo$3b2o2$3b2o$2bo4bo$b2ob2ob2o$2b2o3bo$3b4o$5b2o$6b2o$5bobo3bo$5bo6b" +
371                        "o$5b3o2bobo$11bo!")],
372  [[10,43], ...parseRLE("21bo$18b4o$13bo2bob2o$13bo$4o8bo3bob2o$o3bo5b2ob2obobob5o$o9b2obobobo" +
373                        "2b5o$bo2bo2b2o2bo3b3o2bob2o$6bo2bob2o$6bo4b2o$6bo2bob2o$bo2bo2b2o2bo3b" +
374                        "3o2bob2o$o9b2obobobo2b5o$o3bo5b2ob2obobob5o$4o8bo3bob2o$13bo$13bo2bob" +
375                        "2o$18b4o$21bo!")],
376  [[43,40], ...parseRLE("2b2o$b4o$2ob2o$b2o4$4b2o$3bobo4bobo$2b2obo4b2o$3b2o6bo$4bo3$2b2o$b4o$2ob2o$b2o!")],
377  [[18,18], ...parseRLE("bo2bo$o$o3bo$4o2$5b2o$6bobo$2b2o4bo$bo$2o2b2o$b2o2bo$2b3o2$2b3o$b2o2b" +
378                       "o$2o2b2o$bo$2b2o4bo$6bobo$5b2o2$4o$o3bo$o$bo2bo!")],
379  [[56,32], ...parseRLE("bo2bo$o$o3bo$4o9b2o$6b3o5b2o$6b2ob2o6b3o$6b3o5b2o$4o9b2o$o3bo$o$bo2bo!")],
380];
381
382presets['gosper-gun'] = [[[1,1],[24,0],[22,1],[24,1],[12,2],[13,2],[20,2],[21,2],[34,2],[35,2],[11,3],[15,3],[20,3],[21,3],
383  [21,3],[34,3],[35,3],[0,4],[1,4],[10,4],[16,4],[20,4],[21,4],[0,5],[1,5],[1,5],[10,5],[14,5],[16,5],[17,5],[22,5],[24,5],
384  [10,6],[16,6],[24,6],[11,7],[15,7],[12,8],[13,8]],[[8,59],...e1ul]];
385
386presets['simkin-gun'] = [[[15,24],[0,0],[1,0],[7,0],[8,0],[0,1],[1,1],[7,1],[8,1],[4,3],[5,3],[4,4],[5,4],[22,9],[23,9],
387  [25,9],[26,9],[21,10],[27,10],[21,11],[28,11],[31,11],[32,11],[21,12],[22,12],[23,12],[27,12],[31,12],[32,12],[26,13]],
388  [[55+20,61+20],...e1ul],[[8-24,0-24],...e1dr]]
389
390presets['r-pentomino'] = [[[0,0],[1,1],[2,2],[3,2],[2,1],[2,0]]];
391presets['diehard'] = [[[28,28],[7, 1],[1, 2],[2, 2],[2, 3],[6, 3],[7, 3],[8, 3]]];
392presets['acorn'] = [[[28,28],[1,3],[2,3],[2,1],[4,2],[5,3],[6,3],[7,3]]];
393presets['inf1'] = [[[28,28],[7,1],[5,2],[7,2],[8,2],[5,3],[7,3],[5,4],[3,5],[3,6],[1,6]]];
394presets['inf2'] = [[[28,28],[1,1],[2,1],[3,1],[5,1],[1,2],[4,3],[5,3],[2,4],[3,4],[5,4],[1,5],[3,5],[5,5]]];
395presets['86P6H3V0'] = [[[16,53],[5,0],[6,0],[7,0],[25,0],[26,0],[27,0],[4,1],[8,1],[12,1],[20,1],[24,1],[28,1],[3,2],[4,2],
396  [9,2],[11,2],[12,2],[13,2],[19,2],[20,2],[21,2],[23,2],[28,2],[29,2],[2,3],[4,3],[6,3],[7,3],[9,3],[13,3],[14,3],[18,3],
397  [19,3],[23,3],[25,3],[26,3],[28,3],[30,3],[1,4],[2,4],[4,4],[9,4],[11,4],[14,4],[15,4],[17,4],[18,4],[21,4],[23,4],
398  [28,4],[30,4],[31,4],[0,5],[5,5],[9,5],[14,5],[18,5],[23,5],[27,5],[32,5],[14,6],[15,6],[16,6],[17,6],[18,6],[0,7],[1,7],
399  [11,7],[12,7],[14,7],[18,7],[20,7],[21,7],[31,7],[32,7],[11,8],[14,8],[18,8],[21,8],[10,9],[14,9],[16,9],[18,9],[22,9],
400  [11,10],[21,10]]];
401presets['74P8H2V0'] = [[[24,43],[5,0],[7,0],[9,0],[4,1],[5,1],[6,1],[7,1],[8,1],[9,1],[10,1],[4,2],[10,2],[5,3],[9,3],[5,4],
402  [9,4],[3,5],[4,5],[10,5],[11,5],[5,6],[9,6],[2,7],[4,7],[6,7],[8,7],[10,7],[12,7],[4,8],[6,8],[8,8],[10,8],[1,9],[2,9],
403  [4,9],[6,9],[8,9],[10,9],[12,9],[13,9],[1,10],[5,10],[6,10],[8,10],[9,10],[13,10],[0,12],[1,12],[5,12],[9,12],[13,12],
404  [14,12],[5,13],[9,13],[6,14],[7,14],[8,14],[4,16],[9,16],[10,16],[3,17],[5,17],[8,17],[9,17],[6,18],[9,18],[11,18],[3,19],
405  [6,19],[9,19],[11,19],[4,20],[6,20],[10,20]]];
406
407// https://conwaylife.com/wiki/58P5H1V1
408presets['58P5H1V1'] = [[[40,40], ...parseRLE("20b2ob$20b2ob$19bo2bo$16b2obo2bo$22bo$14b2o3bo2bo$14b2o5bob$15bob5ob$" +
409                                             "16bo6b3$13b3o7b$13bo9b$11b2o10b$5b2o4bo11b$5b3o3bo11b$3bo4bo14b$3bo3bo" +
410                                             "15b$7bo15b$2b2obobo15b$2o5bo15b$2o4b2o15b$2b4o!")]];
411
412presets['295P5H1V1'] = [[[10,10], ...parseRLE("13b2o37b$5b2o4b2obobo35b$4b3o4b4o37b$3b2o6b2o5bo33b$2b2o2b2o3bo2bo2bo" +
413                                              "34b$b2o5bo7bo2b2o31b$b2obo3b4o40b$4bo3b2o2b2obo36b$5b3o4bobo37b$6b2o3b" +
414                                              "2o2bo36b$6bo5bo39b$b4obo2bo2bo3bo35b$b3o3b5o2b7obo29b$obo4bo10bo2b2o" +
415                                              "29b$3obo3bo3bo5b3o31b$7bobo2bo7b2o30b$bo3bo5b2o8b2o2bobo24b$4bo7bo8b3o" +
416                                              "bob3o22b$3bo8b3o6bo4bo25b$5bo6bobo5bobo29b$5bo6bob2o3bo4bo27b$13bob4o" +
417                                              "3bo5bo2bo20b$12b2o2b2obobo3bob3o22b$17bo6bo2b3o3b3o16b$20bo2bo6b2o20b$" +
418                                              "16b2o4bo2bo10b2o14b$18bo13bo3bo15b$16b2o4b2o8bo19b$17bo3b3o8bobobobo" +
419                                              "13b$17bo4b2o8bo5b2o12b$24bo8bo2b3o13b$21bo2bo8bo8bo9b$26b4o8b2o3bo8b$" +
420                                              "23bo6b2o6b2o3bo8b$23bo4bo12bo10b$23bo15bo12b$25b2obobo7bo2bo10b$25bo4b" +
421                                              "o9b3o9b$28b3ob2o2bo3bo3bob2o4b$29bo2b2obo5bo3bo2bo3b$37b2o2bo3bo6b$34b" +
422                                              "ob2ob2obo2b2o3bob$31bo5bo3bo7bobo$32b2o12b2o3bo$38bo7b2o4b$39b3o3b2o2b" +
423                                              "o2b$38bo2bob3o6b$38bo4b2o7b$39bo12b$42bo2bo6b$41bo10b$42b2o!")]]; 
424
425presets['71P16H8V0'] = [[[0, 23], ...parseRLE("22b5o$21bo4bo$26bo$16b2o3bo3bo$15bo2bo4bo$bo6bo3b2o2b2o$ob2o5bo13b5o$o" +
426                                              "4bo3bo4bo6bo5bo$bo3bo4bo16bo$3bo12b2o3bo4bo$4b3o7bo7bo$4bo11bo2bo3bo2b" +
427                                              "o$5b2o10bobo7bo$9b2o12bo3bo$7bo4bo11b4o$13bo$7bo5bo$8b6o!")]];
428
429// https://conwaylife.com/forums/viewtopic.php?&p=156408#p156408 p89
430presets['hassler-unamed (>:3)'] = [[[12,12], ...parseRLE("14bo2$14b3o$14bo2bo$14bo2bo12bo$28b3o$17b2o8bo$18bo8b2o5b2o$34bo$" +
431                                                         "25bo6bobo$26bo5b2o$24bo$23b2ob2o$14b3o3b2ob3o2bo$ob3o8bobo6b3o3bo" +
432                                                         "$2bo10b2o8bob3o$2bo10bo10b3o$3b2obo$6b2o2$13bo$13bo11b2o$14bo11bo" +
433                                                         "$12b4o7b3o$11b4obo6bo$9bo3bob2o4bobo$10bobo2b2o4b2o$6b2o4bo2bo$5b" +
434                                                         "obo5b2o$5bo$4b2o2$9b2o$10bo$7b3o$7bo!")]];
435
436presets['p149 oscillator'] = [[[3,5], ...parseRLE("35bo$14b2o17b3o$15bo16bo$15bobo3bo10b2o$16b2o2bobo$6b2o13bo$6bobo2b2o" +
437                                                  "$3bo4bo3bo16bo$3b5ob3o16bobo$7bobo6bo12b2o$3b3obobo5bobo$2bo2bob2o6b2o" +
438                                                  "$2b2o31b2o$35bo$15b3o18b3o$3b2o10bo22bo$3bo12bo$2obo29b2o$o2bob2o26b2o" +
439                                                  "$2b2obo$3bobo$2bo2b2o$3b2o2bo$4bob2o$4bobo$3b2obobo30b2o$7b2o30b2o2$16b" +
440                                                  "2o30b2o$16b2o30bobob2o$50bobo$49b2obo$49bo2b2o$50b2o2bo$51bobo$51bob2o" +
441                                                  "$22b2o26b2obo2bo$22b2o29bob2o$40bo12bo$18bo22bo10b2o$18b3o18b3o$21bo$" +
442                                                  "20b2o31b2o$40b2o6b2obo2bo$39bobo5bobob3o$26b2o12bo6bobo$26bobo16b3ob5o" +
443                                                  "$27bo16bo3bo4bo$44b2o2bobo$35bo13b2o$34bobo2b2o$23b2o10bo3bobo$24bo16b" +
444                                                  "o$21b3o17b2o$21bo!")]];
445
446
447function loadCoos(sim, arr) {
448  let o = arr[0];
449  for (let i=1; i<arr.length; i++) {
450    sim.setCell(arr[i][0] + o[0], arr[i][1] + o[1], 1);
451  };
452};
453
454
455function loadPreset(sim, kind) {
456  sim.reset();
457  for (let j=0; j<presets[kind].length; j++) {
458    loadCoos(sim, presets[kind][j]);
459  };
460  if (states.running === 0) {
461    simRender();
462  };
463};
464
465
466function appInit() {
467  inputProxy = new cInputProxy();
468  sim = new ParticleSim(64, 64);
469  timer = new cTimer();
470  recorder = new CanvasRecorder(canvas);
471
472  rulesSet['B3/S23'](sim);
473
474  inputProxy.register('clearSim').addEventListener('click', (e)=>{
475    sim.reset()
476  });
477  inputProxy.register('stepSim').addEventListener('click', (e)=>{
478    simStep()
479    if (states.running === 1) {
480      states.running = 0;
481      inputProxy.target.pauseSim.value = 'resume';
482    };
483  });
484  inputProxy.register('pauseSim').addEventListener('click', (e)=>{
485    states.running ^= 1;
486    if (states.running === 1) {
487      e.target.value = 'pause';
488      loop();
489    } else {
490      e.target.value = 'resume';
491    };
492  });
493  inputProxy.register('speedSim', 100, parseFloat);
494  inputProxy.register('presetSelect');
495  while (inputProxy.target.presetSelect.firstChild) {
496    inputProxy.target.presetSelect.removeChild(inputProxy.target.presetSelect.lastChild);
497  };
498  for (let k in presets) {
499    if (k === 'placeholder') continue;
500    let opt = document.createElement('option');
501    opt.value = k;
502    opt.innerHTML = k;
503    inputProxy.target.presetSelect.append(opt);
504  };
505  inputProxy.register('simRecord').addEventListener('click', (e)=>{
506    if (states.recording === 1) {
507      states.recording = 0;
508      e.target.value = 'record'
509      recorder.stop();
510    } else {
511      recorder.start(()=>{
512        states.recording = 0;
513        e.target.value = 'record'
514      }, 25)
515      states.recording = 1;
516      e.target.value = 'stop'
517    }
518  });
519  inputProxy.register('rleOffsetX', 0, parseInt);
520  inputProxy.register('rleOffsetY', 0, parseInt);
521  inputProxy.register('rleInput');
522  inputProxy.register('rleLoad').addEventListener('click', (e)=>{
523    if (validateRLE(inputProxy.target.rleInput.value)) {
524      sim.reset();
525      loadCoos(sim, [[inputProxy.values.rleOffsetX,inputProxy.values.rleOffsetY],
526                     ...parseRLE(inputProxy.target.rleInput.value)])
527      simRender();
528    }
529  });
530  inputProxy.register('presetLoad').addEventListener('click', (e)=>{
531    loadPreset(sim, inputProxy.values.presetSelect);
532  });
533  inputProxy.target.presetSelect.value = inputProxy.target.presetSelect.firstChild.value;
534  inputProxy.values.presetSelect = inputProxy.target.presetSelect.firstChild.value;
535  inputProxy.target.rleInput.value = "15bo$13b2o$13b2o$16bo$13b2obo$14bo$14bobo$15b2o$15bo$19bo$9b3o6b2o$10b2o5b3o$" +
536                                     "11bo$3b2ob2o17bob2o$o6b2o14b3ob2o$b2ob3o14b2o6bo$b2obo17b2ob2o$18bo$10b3o5b2o" +
537                                     "$10b2o6b3o$10bo$14bo$13b2o$13bobo$15bo$13bob2o$13bo$15b2o$15b2o$14bo!"
538
539};
540
541
542function simRender() {
543  ctx.clearRect(0, 0, canvas.width, canvas.height);
544  sim.render(ctx, canvas.width, canvas.height);
545};
546
547
548function simStep() {
549  sim.update();
550  simRender();
551};
552
553
554function loop() {
555  timer.update();
556  timer.ontimed(20 / (inputProxy.values.speedSim / 200), simStep);
557  if (states.running === 1) {
558    window.requestAnimationFrame(loop);
559  };
560};
561
562appInit()
563loadPreset(sim, "glider1")
564loop()
565
566};




Built with Hugo | previoip (c) 2025