Minecraft GenLayer

by eddyb

HTML

<span class=info></span><div style=height:100%;overflow-y:auto>
    <br>
    &nbsp;&nbsp;&nbsp;<input class=seed value=42>
    (<input class=cx value=0>, <input class=cy value=0>) <input class=w value=4096> × <input class=h value=512>
    <div class=outputs></div>
</div>

CSS

html,body {
    height: 100%;
}
body {
    font-family: monospace;
    margin: 0;
}
.info {
    position: absolute;
    z-index: 100000;
    background: white;
    padding: 0.5em;
    border-bottom-right-radius: 0.5em;
}
input {
    border: 1px solid gray;
}
.cx, .cy, .w, .h {
    width: 5ch;
}
.outputs {
    display: flex;
    flex-direction: row;
    align-items: flex-start;
}
.outputs > * {
    display: flex;
    flex-direction: column-reverse;
}
figure {
    display: flex;
    flex-direction: column;
    align-items: center;
}
figure > figcaption {
    text-align: center;
    font-size: 14px;
    white-space: nowrap;
}
figure > canvas {
    border: 1px solid black;
    image-rendering: pixelated;
}

JavaScript 1.7

$(() => {
const range = function*(start, end, step=1) {
    if(typeof end == 'undefined') {
        end = start;
        start = 0;
    }
    if(step < 0) {
      	step = - step;
      	[start, end] = [end, start];
    }
    if(start < end)
      	for(var i = start; i < end; i += step)
          	yield i;
  	else
      	for(var i = start; i > end; i -= step)
          	yield i;
};

const i64 = x => BigInt.asIntN(64, BigInt(x));
class GenLayerRNG {
    constructor(seed0, ...seeds) {
        if(!seeds.length)
            seeds = [seed0, seed0, seed0];
        this.seed = this.seed0 = i64(seed0);
        for(let seed of seeds)
            this.feed(seed);
    }
    worldSeed(seed) {
        return new GenLayerRNG(seed, this.seed, this.seed, this.seed);
    }
    pos(x, y) {
        return new GenLayerRNG(this.seed, x, y, x, y);
    }
    feed(seed) {
        this.seed = i64(i64(this.seed * i64(i64(this.seed * GenLayerRNG.multiplicand) + GenLayerRNG.addend)) + i64(seed));
    }
    next(n) {
        if(Array.isArray(n))
            return n[this.next(n.length)];
        let r = Number((this.seed >> 24n) % i64(n));
        this.feed(this.seed0);
        return r < 0 ? r + n : r;
    }
    next0(n) {
        return this.next(n) === 0;
    }
}
GenLayerRNG.multiplicand = 6364136223846793005n;
GenLayerRNG.addend = 1442695040888963407n;

const Biomes = {names: [], colors: []};
[
    ['Ocean',                 'rgb(0, 0, 112)',     'C'],
    ['Plains',                'rgb(141, 179, 96)',  'A'],
    ['Desert',                'rgb(250, 148, 24)',  'E'],
    ['Extreme Hills',         'rgb(96, 96, 96)',    'I'],
    ['Forest',                'rgb(5, 102, 33)',    'A'],
    ['Taiga',                 'rgb(11, 102, 89)',   'F'],
    ['Swampland',             'rgb(7, 249, 178)',   'M'],
    ['River',                 'rgb(0, 0, 255)',     'B'],
    ['Hell',                  'rgb(255, 0, 0)',     'A'],
    ['Sky',                   'rgb(128, 128, 255)', 'A'],
    ['Frozen Ocean',     ...