/* built with Studio Sketchpad:
* https://sketchpad.cc
*
* observe the evolution of this sketch:
* https://studio.sketchpad.cc/sp/pad/view/ro.7U2vPnjVHOS/rev.2587
*
* authors:
* ManaT
*
* license (unless otherwise specified):
* creative commons attribution-share alike 3.0 license.
* https://creativecommons.org/licenses/by-sa/3.0/
*/
/***************************************************
***************************************************
* *
* Inspired by chapters in Valentino Braitenberg's *
* "Vehicles: Experiments in Synthetic Psychology" *
* *
* Created by: Christopher Field *
* *
***************************************************
***************************************************/
int numVehicles = 100;
ArrayList vehicles;
int numAlive;
int frameCount;
void setup() {
size(500, 500);
frameRate(30);
strokeWeight(3);
rectMode(CENTER);
vehicles = new ArrayList();
for (int i = 0; i < numVehicles; i++)
vehicles.add(new Vehicle());
numAlive = numVehicles;
frameCount = 0;
}
void draw() {
background(0);
createGradient(250, 250, 200, color(250), color(0));
for (int i = 0; i < vehicles.size(); i++) {
Vehicle vehicle = (Vehicle)vehicles.get(i);
vehicle.update();
vehicle.draw();
}
//Draw death circle
noFill();
ellipse(250, 250, 100, 100);
if (numAlive == 0) return;
frameCount++;
if (frameCount >= numAlive && random(numVehicles) > numAlive) {
frameCount %= numAlive;
Vehicle parent = null;
while (true) {
int i = int(random(numVehicles));
Vehicle vehicle = (Vehicle)vehicles.get(i);
if (vehicle.alive) {
parent = vehicle;
break;
}
}
for (int i = 0; i < vehicles.size(); i++) {
Vehicle vehicle = (Vehicle)vehicles.get(i);
if (!vehicle.alive) {
vehicle.respawn(parent);
break;
}
}
}
}
void createGradient (float x, float y, float radius, color c1, color c2) {
if (radius < 1) return;
// calculate differences between color components
float deltaR = red(c2)-red(c1);
float deltaG = green(c2)-green(c1);
float deltaB = blue(c2)-blue(c1);
noFill();
for (float i = 1; i < radius; i++) {
float tempRed = red(c1) + deltaR * (i / radius);
float tempGreen = green(c1) + deltaG * (i / radius);
float tempBlue = blue(c1) + deltaB * (i / radius);
stroke(tempRed, tempGreen, tempBlue);
ellipse(x, y, i*2, i*2);
}
}
class Vehicle {
float x, y;
float rotation;
color skin;
boolean alive;
ArrayList eyes;
ArrayList wheels;
Vehicle() {
x = random(500);
y = random(500);
skin = color(random(256), random(256), random(256));
rotation = random(2)*PI;
alive = true;
wheels = new ArrayList();
wheels.add(new Wheel(-10,-5));
wheels.add(new Wheel(-10,5));
eyes = new ArrayList();
if (random(2) < 1) {
eyes.add(new Eye(10,-5, (Wheel)wheels.get(0)));
eyes.add(new Eye(10,5, (Wheel)wheels.get(1)));
} else {
eyes.add(new Eye(10,-5, (Wheel)wheels.get(1)));
eyes.add(new Eye(10,5, (Wheel)wheels.get(0)));
}
}
void update() {
if (!alive) return;
//Brownian motion
rotation += random(-0.1, 0.1)*PI;
x += random(-2, 2);
y += random(-2, 2);
//Check for death
float lumines = dist(x, y, 250, 250);
if (x >= 500 || x < 0 ||
y >= 500 || y < 0 ||
lumines < 50) {
die();
return;
}
//See
Eye eye1 = (Eye)eyes.get(0);
Eye eye2 = (Eye)eyes.get(1);
eye1.see(250-dist(x+cos(rotation)*10,y+sin(rotation)*-5, 250, 250));
eye2.see(250-dist(x+cos(rotation)*10,y+sin(rotation)*5, 250, 250));
//Move
PVector force = new PVector(0,0);
Wheel wheel1 = (Wheel)wheels.get(0);
Wheel wheel2 = (Wheel)wheels.get(1);
force.add(new PVector(wheel1.strength*cos(rotation),wheel1.strength*sin(rotation)));
force.add(new PVector(wheel2.strength*cos(rotation),wheel2.strength*sin(rotation)));
rotation += (wheel1.strength-wheel2.strength) * 0.01*PI;
x += force.x;
y += force.y;
}
void draw() {
if (!alive) return;
pushMatrix();
translate(x, y);
rotate(rotation);
fill(skin);
rect(0, 0, 20, 10);
popMatrix();
for (int i = 0; i < eyes.size(); i++) {
Eye eye = (Eye)eyes.get(i);
eye.draw(x,y,rotation);
}
for (int i = 0; i < wheels.size(); i++) {
Wheel wheel = (Wheel)wheels.get(i);
wheel.draw(x,y,rotation);
}
}
void die() {
alive = false;
numAlive--;
}
void respawn(Vehicle parent) {
x = parent.x;
y = parent.y;
skin = parent.skin;
rotation = parent.rotation-0.1*PI;
parent.rotation += 0.1*PI;
eyes = parent.eyes;
alive = true;
numAlive++;
if (random(10) < 1) mutate();
}
void mutate() {
switch(int(random(3))) {
case 0:
skin = color((red(skin)+random(-50,50))%256, green(skin), blue(skin));
break;
case 1:
skin = color(red(skin), (green(skin)+random(-50,50))%256, blue(skin));
break;
case 2:
skin = color(red(skin), green(skin), (blue(skin)+random(-50,50))%256);
break;
default:
break;
}
skin = color(random(256), random(256), random(256));
Eye eye = (Eye)eyes.get(int(random(2)));
switch(int(random(5))) {
case 0:
eye.brightThreshold = random(250);
eye.darkThreshold = random(eye.brightThreshold);
break;
case 1:
eye.brightStrength = random(2);
break;
case 2:
eye.darkStrength = random(2);
break;
case 3:
eye.normalStrength = random(2);
break;
case 4:
Eye eye0 = (Eye)eyes.get(0);
Eye eye1 = (Eye)eyes.get(1);
Wheel tempWheel = eye0.wheel;
eye0.wheel = eye1.wheel;
eye1.wheel = tempWheel;
break;
default:
break;
}
if (random(10) < 1) xmutate();
}
void xmutate() {
skin = color(random(256), random(256), random(256));
eyes = new ArrayList();
if (random(2) < 1) {
eyes.add(new Eye(10,-5, (Wheel)wheels.get(0)));
eyes.add(new Eye(10,5, (Wheel)wheels.get(1)));
} else {
eyes.add(new Eye(10,-5, (Wheel)wheels.get(1)));
eyes.add(new Eye(10,5, (Wheel)wheels.get(0)));
}
}
}
class Eye {
float x, y;
Wheel wheel;
float brightThreshold;
float darkThreshold;
float brightStrength;
float darkStrength;
float normalStrength;
Eye(float x, float y, Wheel wheel) {
this.x = x;
this.y = y;
this.wheel = wheel;
brightThreshold = random(250);
darkThreshold = random(brightThreshold);
brightStrength = random(2);
darkStrength = random(2);
normalStrength = random(2);
}
void draw(float x, float y, float rotation) {
fill(255);
pushMatrix();
translate(x,y);
rotate(rotation);
ellipse(this.x,this.y,5,5);
popMatrix();
}
void see(float lumines) {
if (lumines > brightThreshold) {
wheel.setStrength(brightStrength);
return;
}
if (lumines < darkThreshold) {
wheel.setStrength(darkStrength);
return;
}
wheel.setStrength(normalStrength);
}
}
class Wheel {
float x, y;
float strength;
Wheel(float x, float y) {
this.x = x;
this.y = y;
strength = 1;
}
void draw(float x, float y, float rotation) {
fill(255);
pushMatrix();
translate(x,y);
rotate(rotation);
rect(this.x,this.y,8,4);
popMatrix();
}
void setStrength(float strength) {
this.strength = strength;
}
PVector getForce(float rotation) {
return new PVector(strength*cos(rotation),strength*sin(rotation));
}
}