Hexagonal Maze

First attempt at #maze generation.

Log in to post a comment.

Canvas.setpenopacity(1);
const extent = 110;
const size = 2;

class Cell {
    constructor(x, y, sideLength) {
        this.x = x;
        this.y = y;
        this.sideLength = sideLength;
        this.arity = 6;
        this.nextIndex = Math.floor(Math.random() * this.arity);
        this.angle = 360/this.arity;
        this.neighbors = [...Array(this.arity)].map(() => null);
        this.cellDistance = (sideLength-0.175) * Math.cos(this.angle);
    }
    
    setNeighbor(index, cell) {
        this.neighbors[(index)%this.arity] = cell;
    }
    
    findNeighborCoords(index) {
        const turtle = new Turtle();
        turtle.penup();
        turtle.goto(this.x, this.y);
        turtle.seth((180 + (this.angle * index + this.angle/2))%360);
        turtle.forward(2 * this.cellDistance);
        const tx = turtle.x();
        const ty = turtle.y();
        return [tx, ty];
    }
    
    attachNeighbors(cells) {
        const missing = this.neighbors.map((c, i) => ({ i, c })).filter(({c}) => c===null).map(({i}) => i);
        missing.forEach(i => {
            const [tx, ty] = this.findNeighborCoords(i);
            const newNeighbor = cells.find(c => 
                Math.round(c.x) == Math.round(tx) 
                && Math.round(c.y) == Math.round(ty)
            );
            if (newNeighbor) {
                if (Math.random() > 0.01) {
                    this.neighbors[i] = true;
                } else {
                    this.neighbors[i] = newNeighbor;
                }
            }
        });
    }

    findNext(cells) {
        let ret = false;
        for (let i = this.nextIndex; i < this.nextIndex+this.arity; i++) {
            const [tx, ty] = this.findNeighborCoords(i%this.arity);
            this.nextIndex = i%this.arity;
            if (!cells.find(c => Math.round(c.x) == Math.round(tx) && Math.round(c.y) == Math.round(ty))) {
                if (tx >= -extent && tx <= extent && ty >= -extent && ty <= extent) {
                    const newCell = new Cell(tx, ty, this.sideLength);
                    this.setNeighbor(i%this.arity, newCell);
                    newCell.setNeighbor((i%this.arity+(this.arity/2))%this.arity, this);
                    ret = newCell;
                }
                else {
                    this.ready = true;
                    break;
                }
            }
            if (this.neighbors.filter(c => !c).length === 0) {
                this.ready = true;
            }
            if (ret) break;
        }
        return ret;
    }
    
    render() {
        const turtle = new Turtle();
        turtle.penup();
        turtle.goto(this.x - this.sideLength/2, this.y + this.cellDistance);
        for (let cur, i = 0; i < this.arity; i++, cur = this.neighbors[(i+4)%this.arity]) {
            if (cur === true) {
                turtle.pendown();
            }
            turtle.forward(this.sideLength);
            turtle.right(this.angle);
            turtle.penup();
        }
        this.rendered = true;
    }
}

class Organism {
    constructor() {
        this.cells = [new Cell(0, 0, size)];
        this.nextRender = [];
    }
    
    grow() {
        this.cells.filter(c => !c.ready).forEach((c, i) => {
            const newCell = c.findNext(this.cells);
            if (newCell) {
                newCell.attachNeighbors(this.cells);
                this.cells.splice(0, 0, newCell);
            }
            c.attachNeighbors(this.cells);
            this.nextRender = this.cells.filter(c => c.ready && !c.rendered);
        });
    }
    
    render() {
        this.nextRender.forEach(c => c.render());
        return this.nextRender.length;
    }
}

const o = new Organism();
let lastProgress = 0;
let stalled = 0;

function walk(i) {
    o.grow();
    const progress = o.render();
    if (progress !== lastProgress) {
        lastProgress = progress;
        stalled = 0;
    } else {
        stalled++;
    }
    return stalled < 10;
}