Swarm experiments
https://en.wikipedia.org/wiki/Swarm_intelligence
by Anton
HTML
<div id="world"></div>
<div id="stats">Stats</div>
<div id="heading"><div id="arrow"></div></div>
CSS
body {
background: #20262E;
font-family: Helvetica;
padding: 20px;
}
.boid {
position: absolute;
display: block;
width: 5px;
height: 5px;
background-color: #EEE;
border-radius: 50%;
}
#world {
position: absolute;
height: calc(95% - 40px);
width: calc(95% - 40px);
border: solid 1px #333;
}
#stats {
position: absolute;
top: 5px;
right: 5px;
color: #555;
font-size: 14px;
text-align: right;
}
#heading {
position: absolute;
bottom: 30px;
right: 30px;
width: 60px;
height: 60px;
border: solid 1px #555;
border-radius: 50%;
}
#arrow {
position: absolute;
top: 0;
right: 30px;
width: 1px;
height: 30px;
transform-origin: bottom center;
background-color: #555;
}
JavaScript 1.7
/**
* Simulate swarm movements using Boids approach, three rules:
* 1. Steer to avoid crowding flockmates
* 2. Steer towards average heading of local flockmates
* 3. Steer towards average position of local flockmates
*/
const count = 50;
const duration = 60; // seconds
const fps = 25;
const headingAdj = 0.5; // heading adjustment rate
const attractionAdj = 0.5; // attraction adjustment rate
const pushAdj = 0.5; // push away from too close neighbours
const bounceAdj = 0.5;
const maxDelta = 5;
const localZone = 100; // Closer boids are "local"
const comfortZone = 20;
const world = $('#world');
const stats = $('#stats');
const maxX = world.width();
const maxY = world.height();
const swarm = [];
class Boid {
constructor(id) {
this.id = id;
this.x = Math.random() * maxX;
this.y = Math.random() * maxY;
this.dx = maxDelta * (2 * Math.random() - 1);
this.dy = maxDelta * (2 * Math.random() - 1);
this.calcSpeed();
this.calcHeading();
this.element = $(`<div class="boid"></div>`);
world.append(this.element);
this.draw();
}
calcSpeed() {
this.speed = Math.sqrt(this.dx * this.dx + this.dy * this.dy);
}
calcHeading() {
this.heading = this.speed === 0 ? 0 : Math.asin(this.dx / this.speed);
if(this.dy < 0) this.heading = Math.PI - this.heading;
}
draw() {
this.element.css({ top: maxY - this.y, left: this.x });
}
move(avgX, avgY, avgHeading) {
this.update(avgX, avgY, avgHeading);
this.x += this.dx;
this.y += this.dy;
// Reflect off sides
if(this.x < 0) { this.x = 0; this.dx *= -bounceAdj; }
if(this.x > maxX) { this.x = maxX; this.dx *= -bounceAdj; }
if(this.y < 0) { this.y = 0; this.dy = -bounceAdj; }
if(this.y > maxY) { this.y = maxY; this.dy = -bounceAdj; }
this.draw();
}
update() {
this.calcSpeed();
...