Skip to content

Instantly share code, notes, and snippets.

@Trion129
Created July 3, 2019 18:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Trion129/d74d2c90804422dbd193f92d968eabb1 to your computer and use it in GitHub Desktop.
Save Trion129/d74d2c90804422dbd193f92d968eabb1 to your computer and use it in GitHub Desktop.
Steering behaviour - from coding train, but generation-wise growth
<html>
<head>
<meta charset="UTF-8">
<script language="javascript" type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.8.0/p5.min.js"></script>
<script language="javascript" src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.8.0/addons/p5.dom.min.js"></script>
<script language="javascript" src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.8.0/addons/p5.sound.min.js"></script>
<script language="javascript" type="text/javascript" src="vehicle.js"></script>
<script language="javascript" type="text/javascript" src="population.js"></script>
<script language="javascript" type="text/javascript" src="sketch.js"></script>
</head>
<body>
</body>
</html>
function Population() {
this.generation = 0;
this.vehicles = [];
this.init = function(width, height) {
this.width = width;
this.height = height;
this.generation = 1;
for (var i = 0; i < 50; i++) {
var x = random(width);
var y = random(height);
this.vehicles[i] = new Vehicle(x, y);
}
}
this.nextGeneration = function() {
var oldPopulation = this.vehicles;
this.vehicles = this.crossedVehicles(oldPopulation);
this.generation++;
}
this.crossedVehicles = function(oldPopulation) {
var parent1, parent2;
var vehicles = [];
var fitness = this.calculateFitness(oldPopulation);
for(var i = 0; i < oldPopulation.length; i++) {
parent1 = this.selectParent(oldPopulation, fitness);
parent2 = this.selectParent(oldPopulation, fitness);
vehicles.push(this.crossParents(parent1, parent2));
}
return vehicles;
}
this.calculateFitness = function(oldPopulation) {
var fitness = [];
for(var i = 0; i < oldPopulation.length; i++) {
fitness.push(oldPopulation[i].getFitness());
}
return fitness;
}
this.selectParent = function(oldPopulation, fitness) {
var sumOfFitness = 0, i;
for(i = 0; i < fitness.length; i++) {
sumOfFitness += fitness[i];
}
p = random(sumOfFitness);
for(i = 0; i < fitness.length; i++) {
if(i <= 0){
break;
}
p -= fitness[i];
}
return oldPopulation[i];
}
this.crossParents = function(parent1, parent2) {
var x = random(this.width), y = random(this.height);
var dnaFromParent1 = parent1.dna;
var dnaFromParent2 = parent2.dna;
var dna = [];
for (var i = 0; i < 4; i++){
dna.push((dnaFromParent1[i] + dnaFromParent2[i])/2)
}
var vehicle = new Vehicle(x, y, dna);
return vehicle;
}
this.update = function(food, poison) {
for (var i = this.vehicles.length - 1; i >= 0; i--) {
if (!this.vehicles[i].dead()) {
this.vehicles[i].boundaries();
this.vehicles[i].behaviors(food, poison);
this.vehicles[i].update();
this.vehicles[i].display();
}
}
}
}
var population = null;
var food = [];
var poison = [];
var generation = 0;
var debug;
function setup() {
createCanvas(windowWidth * 2/3, windowHeight * 2/3);
population = new Population();
population.init(width, height);
for (var i = 0; i < 40; i++) {
var x = random(width);
var y = random(height);
food.push(createVector(x, y));
}
for (var i = 0; i < 20; i++) {
var x = random(width);
var y = random(height);
poison.push(createVector(x, y));
}
setInterval(function(){
population.nextGeneration();
}, 10000);
debug = createCheckbox();
}
// function mouseDragged() {
// vehicles.push(new Vehicle(mouseX, mouseY));
// }
function draw() {
background(51);
if (random(1) < 0.5) {
var x = random(width);
var y = random(height);
food.push(createVector(x, y));
}
if (random(1) < 0.05) {
var x = random(width);
var y = random(height);
poison.push(createVector(x, y));
}
for (var i = 0; i < food.length; i++) {
fill(0, 255, 0);
noStroke();
ellipse(food[i].x, food[i].y, 4, 4);
}
for (var i = 0; i < poison.length; i++) {
fill(255, 0, 0);
noStroke();
ellipse(poison[i].x, poison[i].y, 4, 4);
}
population.update(food, poison);
}
var mr = 0.1;
function Vehicle(x, y, dna) {
this.acceleration = createVector(0, 0);
this.velocity = createVector(0, -2);
this.position = createVector(x, y);
this.r = 4;
this.maxspeed = 5;
this.maxforce = 0.5;
this.survived = 0;
this.eaten = 0;
this.health = 1;
this.dna = [];
if (dna === undefined) {
// Food weight
this.dna[0] = random(-2, 2);
// Poison weight
this.dna[1] = random(-2, 2);
// Food perception
this.dna[2] = random(0, 100);
// Poision Percepton
this.dna[3] = random(0, 100);
} else {
// Mutation
this.dna[0] = dna[0];
if (random(1) < mr) {
this.dna[0] = random(-2, 2);
}
this.dna[1] = dna[1];
if (random(1) < mr) {
this.dna[1] = random(-2, 2);
}
this.dna[2] = dna[2];
if (random(1) < mr) {
this.dna[2] = random(0, 100);
}
this.dna[3] = dna[3];
if (random(1) < mr) {
this.dna[3] = random(0, 100);
}
}
// Method to update location
this.update = function () {
this.survived += 1;
this.health -= 0.005;
// Update velocity
this.velocity.add(this.acceleration);
// Limit speed
this.velocity.limit(this.maxspeed);
this.position.add(this.velocity);
// Reset accelerationelertion to 0 each cycle
this.acceleration.mult(0);
}
this.getFitness = function() {
if (this.health < 0) {
this.health = 0;
}
this.fitness = this.health + 100 * this.eaten;
return this.fitness;
}
this.applyForce = function (force) {
// We could add mass here if we want A = F / M
this.acceleration.add(force);
}
this.behaviors = function (good, bad) {
var steerG = this.eat(good, 0.5, this.dna[2]);
var steerB = this.eat(bad, -1, this.dna[3]);
steerG.mult(this.dna[0]);
steerB.mult(this.dna[1]);
this.applyForce(steerG);
this.applyForce(steerB);
}
this.clone = function () {
if (random(1) < 0.002) {
return new Vehicle(this.position.x, this.position.y, this.dna);
} else {
return null;
}
}
this.eat = function (list, nutrition, perception) {
var record = Infinity;
var closest = null;
for (var i = list.length - 1; i >= 0; i--) {
var d = this.position.dist(list[i]);
if (d < this.maxspeed) {
list.splice(i, 1);
if(nutrition > 0){
this.eaten += 1;
}
this.health += nutrition;
} else {
if (d < record && d < perception) {
record = d;
closest = list[i];
}
}
}
// This is the moment of eating!
if (closest != null) {
return this.seek(closest);
}
return createVector(0, 0);
}
// A method that calculates a steering force towards a target
// STEER = DESIRED MINUS VELOCITY
this.seek = function (target) {
var desired = p5.Vector.sub(target, this.position); // A vector pointing from the location to the target
// Scale to maximum speed
desired.setMag(this.maxspeed);
// Steering = Desired minus velocity
var steer = p5.Vector.sub(desired, this.velocity);
steer.limit(this.maxforce); // Limit to maximum steering force
return steer;
}
this.dead = function () {
return (this.health < 0)
}
this.display = function () {
// Draw a triangle rotated in the direction of velocity
var angle = this.velocity.heading() + PI / 2;
push();
translate(this.position.x, this.position.y);
rotate(angle);
if (debug.checked()) {
strokeWeight(3);
stroke(0, 255, 0);
noFill();
line(0, 0, 0, -this.dna[0] * 25);
strokeWeight(2);
ellipse(0, 0, this.dna[2] * 2);
stroke(255, 0, 0);
line(0, 0, 0, -this.dna[1] * 25);
ellipse(0, 0, this.dna[3] * 2);
}
var gr = color(0, 255, 0);
var rd = color(255, 0, 0);
var col = lerpColor(rd, gr, this.health);
fill(col);
stroke(col);
strokeWeight(1);
beginShape();
vertex(0, -this.r * 2);
vertex(-this.r, this.r * 2);
vertex(this.r, this.r * 2);
endShape(CLOSE);
pop();
}
this.boundaries = function () {
var d = 25;
var desired = null;
if (this.position.x < d) {
desired = createVector(this.maxspeed, this.velocity.y);
} else if (this.position.x > width - d) {
desired = createVector(-this.maxspeed, this.velocity.y);
}
if (this.position.y < d) {
desired = createVector(this.velocity.x, this.maxspeed);
} else if (this.position.y > height - d) {
desired = createVector(this.velocity.x, -this.maxspeed);
}
if (desired !== null) {
desired.normalize();
desired.mult(this.maxspeed);
var steer = p5.Vector.sub(desired, this.velocity);
steer.limit(this.maxforce);
this.applyForce(steer);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment