Random Connected Area - Fat

https://xcoder.in/2018/04/02/random-connected-area/#生成「胖胖的」区域

by XadillaX

HTML

<script src="https://cdnjs.cloudflare.com/ajax/libs/fbbk-json/1.1.1/fjson.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.5/lodash.min.js"></script>
<input id="length" placeholder="宽高" />
<input id="need-area" placeholder="填充面积" />
<button id="gen">生成</button>
<br />
<canvas id="cvs"></canvas><br /><br />

<img style="display: none;" id="tile" src="http://xcoder-7niu.nbut.ac.cn/basictiles.png" />

CSS

body {
    background: #20262E;
    padding: 20px;
    font-family: Helvetica;
}

#banner-message {
    background: #fff;
    border-radius: 4px;
    padding: 20px;
    font-size: 25px;
    text-align: center;
    transition: all 0.2s;
    margin: 0 auto;
    width: 300px;
}

button {
    background: #0084ff;
    border: none;
    border-radius: 5px;
    padding: 8px 14px;
    font-size: 15px;
    color: #fff;
}

#banner-message.alt {
    background: #0084ff;
    color: #fff;
    margin-top: 40px;
    width: 200px;
}

#banner-message.alt button {
    background: #fff;
    color: #000;
}

JavaScript

function random(max) {
    return Math.round(Math.random() * max);
}

class Filler2 {
    constructor(length, needArea) {
        this.length = length;
        this.needArea = needArea;
    }

    _getContiguous(frontier) {
        return Filler2.DIRS.map(dir => ({
            x: frontier.x + dir[0],
            y: frontier.y + dir[1]
        }));
    }

    fill() {
        const mat = [];
        for (let i = 0; i < this.length; i++) {
            let row = [];
            for (let j = 0; j < this.length; j++) row.push(".");
            mat.push(row);
        }

        const start = {
            x: random(this.length - 1),
            y: random(this.length - 1)
        };
        mat[start.x][start.y] = "x";

        let frontierCount = 1;
        const frontiers = {
            [`${start.x}:${start.y}`]: true
        };

        for (let i = 1; i < this.needArea; i++) {
            // 取出一个边界
            const randIdx = random(frontierCount - 1);
            const frontier = Object.keys(frontiers)[randIdx].split(":").map(n => parseInt(n));

            // _getContiguous 算出接壤坐标,filter 去除无用坐标
            const newCoors = this._getContiguous({
                x: frontier[0],
                y: frontier[1]
            }).filter(coor => {
                if (coor.x < 0 || coor.y < 0 || coor.x >= this.length || coor.y >= this.length) return false;
                if (mat[coor.x][coor.y] === "x") return false;
                return true;
            });

            // 随机取一个坐标
            const newCoor = newCoors[random(0, newCoors.length - 1)];

            // 填充进去
            mat[newCoor.x][newCoor.y] = "x";

            // 获取接壤坐标
            const contiguousOfNewCoor = this._getContiguous(newCoor).filter(coor => {
                if (coor.x < 0 || coor.y < 0 || coor.x >= this.length || coor.y >= this.length) return false;
                return true;
            });

            // 若有一个接壤点为空,就认为当前坐标是边界,若是边界则把当前坐标加入对象
            if...