シードあり乱数が欲しかっただけなのですが、アルゴリズムを幾つか実装する序でに砂嵐画面にしてみた。砂嵐は表示領域4x4倍分の一部を切り出す、なんちゃって方式です。毎回生成している訳ではありません。画像のクリックで更新の切り替えです。
See the Pen テレビの中の砂嵐 by Ikiuo (@ikiuo) on CodePen.
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>