for (let i = 0; i < 100; i++) {
boids.push(new Boid(random(width), random(height)));
for (let i = 0; i < 10; i++) {
evilBoids.push(new EvilBoid(random(width), random(height)));
let bgColor = color(0, 0, 0, 2);
rect(0, 0, width, height);
while (food.length < maxFood) {
food.push(createVector(random(width), random(height)));
for (let i = boids.length - 1; i >= 0; i--) {
boid.update(food, obstacles, evilBoids);
food.push(createVector(boid.position.x, boid.position.y));
let offspring = boid.reproduce();
if (offspring) boids.push(offspring);
for (let i = evilBoids.length - 1; i >= 0; i--) {
evil.update(boids, obstacles);
let offspring = evil.reproduce();
if (offspring) evilBoids.push(offspring);
constructor(x, y, traits) {
this.position = createVector(x, y);
this.velocity = p5.Vector.random2D();
this.acceleration = createVector();
this.traits = traits || {
awareness: random(50, 150),
lifespan: random(3000, 5000),
this.energy = this.traits.energy;
this.color = color(random(0, 50), random(100, 255), random(200, 255), 150);
let separation = this.separate(boids).mult(1.5);
let alignment = this.align(boids).mult(1.0);
let cohesion = this.cohere(boids).mult(1.0);
this.applyForce(separation);
this.applyForce(alignment);
this.applyForce(cohesion);
update(food, obstacles, predators) {
this.energy -= this.traits.speed * 0.01;
this.avoidObstacles(obstacles);
this.velocity.add(this.acceleration);
this.velocity.limit(this.traits.speed);
this.position.add(this.velocity);
this.acceleration.mult(0);
this.acceleration.add(force);
for (let i = food.length - 1; i >= 0; i--) {
let d = distSq(this.position, food[i]);
avoidObstacles(obstacles) {
for (let obs of obstacles) {
let d = distSq(this.position, obs);
let flee = p5.Vector.sub(this.position, obs).normalize().mult(this.traits.speed);
for (let predator of predators) {
let d = distSq(this.position, predator.position);
if (d < predator.traits.awareness) {
let flee = p5.Vector.sub(this.position, predator.position).normalize().mult(this.traits.speed);
if (this.position.x > width) this.position.x = 0;
if (this.position.x < 0) this.position.x = width;
if (this.position.y > height) this.position.y = 0;
if (this.position.y < 0) this.position.y = height;
return this.energy <= 0 || this.age >= this.traits.lifespan;
if (this.age > this.traits.lifespan - 4000) {
speed: constrain(this.traits.speed + random(-mutationRate, mutationRate), 2, 5),
energy: constrain(this.traits.energy + random(-mutationRate, mutationRate) * 20, 50, 150),
awareness: constrain(this.traits.awareness + random(-mutationRate, mutationRate) * 10, 50, 150),
lifespan: constrain(this.traits.lifespan + random(-mutationRate, mutationRate) * 500, 3000, 5000),
return new Boid(this.position.x, this.position.y, newTraits);
translate(this.position.x, this.position.y);
rotate(this.velocity.heading());
ellipse(-12, 0, 5, this.mouthOpen ? 10 : 5);
let desiredSeparation = 25;
let steer = createVector();
for (let other of boids) {
let d = distSq(this.position, other.position);
if (d > 0 && d < desiredSeparation * desiredSeparation) {
let diff = p5.Vector.sub(this.position, other.position);
steer.setMag(this.traits.speed);
steer.sub(this.velocity);
steer.limit(this.maxForce);
let sum = createVector();
for (let other of boids) {
let d = distSq(this.position, other.position);
if (d > 0 && d < neighborDist * neighborDist) {
sum.setMag(this.traits.speed);
let steer = p5.Vector.sub(sum, this.velocity);
steer.limit(this.maxForce);
let sum = createVector();
for (let other of boids) {
let d = distSq(this.position, other.position);
if (d > 0 && d < neighborDist * neighborDist) {
let desired = p5.Vector.sub(target, this.position);
desired.setMag(this.traits.speed);
let steer = p5.Vector.sub(desired, this.velocity);
steer.limit(this.maxForce);
this.position = createVector(x, y);
this.velocity = p5.Vector.random2D();
this.acceleration = createVector();
awareness: random(100, 200),
lifespan: random(2000, 4000),
this.energy = this.traits.energy;
this.color = color(255, 0, 0, 150);
update(boids, obstacles) {
this.energy -= this.traits.speed * 0.01;
this.avoidObstacles(obstacles);
this.velocity.add(this.acceleration);
this.velocity.limit(this.traits.speed);
this.position.add(this.velocity);
this.acceleration.mult(0);
for (let boid of boids) {
let d = distSq(this.position, boid.position);
let seek = p5.Vector.sub(closestBoid.position, this.position);
seek.setMag(this.traits.speed);
let steer = p5.Vector.sub(seek, this.velocity);
steer.limit(this.maxForce);
this.consume(closestBoid);
this.acceleration.add(force);
avoidObstacles(obstacles) {
for (let obs of obstacles) {
let d = distSq(this.position, obs);
let flee = p5.Vector.sub(this.position, obs).normalize().mult(this.traits.speed);
let d = distSq(this.position, boid.position);
this.energy += boid.energy * 0.5;
if (this.position.x > width) this.position.x = 0;
if (this.position.x < 0) this.position.x = width;
if (this.position.y > height) this.position.y = 0;
if (this.position.y < 0) this.position.y = height;
return this.energy <= 0 || this.age >= this.traits.lifespan;
if (this.age > this.traits.lifespan - 3000) {
speed: constrain(this.traits.speed + random(-mutationRate, mutationRate), 2, 5),
energy: constrain(this.traits.energy + random(-mutationRate, mutationRate) * 20, 50, 150),
awareness: constrain(this.traits.awareness + random(-mutationRate, mutationRate) * 10, 100, 200),
lifespan: constrain(this.traits.lifespan + random(-mutationRate, mutationRate) * 500, 2000, 4000),
return new EvilBoid(this.position.x, this.position.y, newTraits);
translate(this.position.x, this.position.y);
rotate(this.velocity.heading());
ellipse(-12, 0, 10, this.mouthOpen ? 12 : 8);
function distSq(v1, v2) {
return (v1.x - v2.x) * (v1.x - v2.x) + (v1.y - v2.y) * (v1.y - v2.y);
function generateFood() {
while (food.length < maxFood) {
food.push(createVector(random(width), random(height)));
for (let i = 0; i < food.length; i++) {
ellipse(food[i].x, food[i].y, 8, 8);
function generateObstacles() {
while (obstacles.length < maxObstacles) {
obstacles.push(createVector(random(width), random(height)));
function displayObstacles() {
for (let i = 0; i < obstacles.length; i++) {
ellipse(obstacles[i].x, obstacles[i].y, 30, 30);