Created
July 3, 2019 18:59
-
-
Save Trion129/d74d2c90804422dbd193f92d968eabb1 to your computer and use it in GitHub Desktop.
Steering behaviour - from coding train, but generation-wise growth
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<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> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | |
} | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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