テレビの中の砂嵐

シードあり乱数が欲しかっただけなのですが、アルゴリズムを幾つか実装する序でに砂嵐画面にしてみた。砂嵐は表示領域4x4倍分の一部を切り出す、なんちゃって方式です。毎回生成している訳ではありません。画像のクリックで更新の切り替えです。


<p class="codepen" data-height="605" data-default-tab="result" data-slug-hash="XJJEpPx" data-pen-title="テレビの中の砂嵐" data-user="ikiuo" style="height: 605px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/ikiuo/pen/XJJEpPx">
  テレビの中の砂嵐</a> by Ikiuo (<a href="https://codepen.io/ikiuo">@ikiuo</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://public.codepenassets.com/embed/index.js"></script>


```html:sample.html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="utf-8">
    <title>テレビの中の砂嵐</title>
  </head>
  <body>
    <table>
      <tr><th>MT19937</th><td> <canvas id="tagMT19937"></canvas></td></tr>
      <tr><th>XorShift<br>32Bit</th><td><canvas id="tagXorShift32"></canvas></td></tr>
      <tr><th>LCG<br>64Bit</th><td><canvas id="tagLCG64"></canvas></td></tr>
      <tr><th>LCG<br>32Bit</th><td><canvas id="tagLCG32"></canvas></td></tr>
    </table>

    <script>

     function seed32(seed) {
         while (!seed)
             seed = (Math.random() * 4294967296) >>> 0;
         return seed;
     }

     /*
      * メルセンヌツイスタ
      */

     class MT19937 {
         constructor(seed = 5489) {
             seed = seed >>> 0;

             const state = new Uint32Array(624);
             state[0] = seed;
             for (let i = 1; i < 624; i++) {
                 const x = (seed ^ (seed >>> 30)) >>> 0;
                 const y = (BigInt(x) * 0x6c078965n) & 0xffffffffn;
                 state[i] = seed = (Number(y) + i) >>> 0;
             }
             this.state = state;
             this.index = 0;
         }

         generate() {
             const state = this.state;

             const cidx = this.index;
             const nidx = (cidx < 623) ? cidx + 1 : 0;
             const xidx = (cidx < 227) ? cidx + 397 : cidx - 227;
             this.index = nidx;

             const sc = state[cidx];
             const sn = state[nidx];
             const sx = state[xidx];
             const sy = ((sc & 0x80000000) | (sn & 0x7fffffff)) >>> 1;
             const sz = (sn & 1) ? 0x9908b0df : 0;
             const ss = (sx ^ sy ^ sz) >>> 0;
             state[cidx] = ss;

             const t1 = ss ^ (ss >>> 11);
             const t2 = t1 ^ ((t1 << 7) & 0x9d2c5680);
             const t3 = t2 ^ ((t2 << 15) & 0xefc60000);
             const tr = t3 ^ (t3 >>> 18);
             return tr >>> 0;
         }

         genreal() {
             return this.generate() / 4294967296;
         }
     }

     /*
      * Xorshift (32ビット)
      */

     class XorShift32 {
         constructor(seed) {
             this.seed = seed32(seed);
         }

         generate() {
             let x = this.seed;
             x ^= x << 13;
             x ^= x >>> 17;
             x ^= x << 5;
             return (this.seed = x >>> 0);
         }

         genreal() {
             return this.generate() / 4294967296;
         }
     }

     /*
      * 線形合同法(64ビット)
      */

     class LCG64 {
         constructor(seed) {
             this.seed = BigInt(seed32(seed));
             this.generate64()
         }

         generate64() {
             return this.seed = (this.seed * 0x5851f42d4c957f2dn + 0x14057b7ef767814fn) & 0xffff_ffff_ffff_ffffn;
         }

         generate() {
             return Number(this.generate64() >> 32n) >>> 0;
         }

         genreal() {
             return Number(this.generate64() >> 11n) / 9007199254740992;
         }
     }

     /*
      * 線形合同法(32ビット)
      */

     class LCG32 {
         constructor(seed) {
             this.seed = seed32(seed);
         }

         generate() {
             return this.seed = (this.seed * 0x0019660d + 0x3c6ef35f) >>> 0;
         }

         genreal() {
             return this.generate() / 4294967296;
         }
     }

     /*
      * 以下、砂嵐画面処理
      */

     function makeImage(ctx, width, height, rand) {
         const total = width * height;

         const image = ctx.createImageData(width, height);
         const pixel = image.data;

         const brightness = 0.70;
         const table = [...Array(256)].map(
             (_, n) => 255.4 * Math.pow(n / 255, 1.0 - brightness) >>> 0);

         let c = 0;
         for (let i = 0; i < pixel.length; i += 4, c >>= 8) {
             if (!(i & 0x0c))
                 c = rand.generate();
             const g = table[c & 255];
             pixel[i + 0] = g;
             pixel[i + 1] = g;
             pixel[i + 2] = g;
             pixel[i + 3] = 255;
         }

         return image;
     }

     class SandStorm
     {
         constructor(tag, rand) {
             const width = 720;
             const height = 480;
             tag.width = width;
             tag.height = height;

             this.tag = tag;
             this.rand = rand;
             this.ctx = this.tag.getContext('2d');
             this.width = this.ctx.canvas.width;
             this.height = this.ctx.canvas.height;
             this.image = makeImage(this.ctx, this.width * 4, this.height * 4, this.rand);
             this.count = 0;

             this.enable = true;
             this.tag.onclick = (() => this.onclick ());
         }
         update() {
             if (!this.enable)
                 return;

             const ox = this.count & 1 ? this.width * 2 : 0;
             const oy = this.count & 2 ? this.height * 2 : 0;
             const dx = ox + this.rand.genreal () * this.width;
             const dy = oy + this.rand.genreal () * this.height;
             this.ctx.putImageData(this.image, -dx, -dy);
             ++this.count;
         }
         onclick() {
             this.enable = !this.enable;
         }
     }

     window.onload = function() {
         const mt = new SandStorm(tagMT19937, new MT19937());
         const xs32 = new SandStorm(tagXorShift32, new XorShift32());
         const lcg64 = new SandStorm(tagLCG64, new LCG64());
         const lcg32 = new SandStorm(tagLCG32, new LCG32());

         const render = (() => {
             window.requestAnimationFrame(render);
             mt.update();
             xs32.update();
             lcg64.update();
             lcg32.update();
         });
         window.requestAnimationFrame(render);
     }

    </script>
  </body>
</html>
```