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;
}