xxxxxxxxxx
210
// Rocket class
class Rocket {
constructor(dna) {
this.position = createVector(width / 2, height);
this.velocity = createVector();
this.acceleration = createVector();
this.completed = false;
this.crashed = false;
this.fitness = 0;
if (dna) {
this.dna = dna;
} else {
this.dna = new DNA();
}
}
applyForce(force) {
this.acceleration.add(force);
}
update() {
let d = dist(this.position.x, this.position.y, target.x, target.y);
if (d < 10) {
this.completed = true;
this.position = target.copy();
}
if (
this.position.x > obstacle.x &&
this.position.x < obstacle.x + obstacle.w &&
this.position.y > obstacle.y &&
this.position.y < obstacle.y + obstacle.h
) {
this.crashed = true;
}
if (
this.position.x > width ||
this.position.x < 0 ||
this.position.y > height ||
this.position.y < 0
) {
this.crashed = true;
}
this.applyForce(this.dna.genes[count]);
if (!this.completed && !this.crashed) {
this.velocity.add(this.acceleration);
this.position.add(this.velocity);
this.acceleration.mult(0);
this.velocity.limit(4);
}
}
show() {
push();
noStroke();
fill(255, 150);
translate(this.position.x, this.position.y);
rotate(this.velocity.heading());
rectMode(CENTER);
rect(0, 0, 25, 5);
pop();
}
calcFitness() {
let d = dist(this.position.x, this.position.y, target.x, target.y);
this.fitness = map(d, 0, width, width, 0);
if (this.completed) {
this.fitness *= 10;
}
if (this.crashed) {
this.fitness /= 10;
}
}
}
// DNA class
class DNA {
constructor(genes) {
if (genes) {
this.genes = genes;
} else {
this.genes = [];
for (let i = 0; i < lifespan; i++) {
this.genes[i] = p5.Vector.random2D();
this.genes[i].setMag(maxForce);
}
}
}
crossover(partner) {
let newGenes = [];
let mid = floor(random(this.genes.length));
for (let i = 0; i < this.genes.length; i++) {
if (i > mid) {
newGenes[i] = this.genes[i];
} else {
newGenes[i] = partner.genes[i];
}
}
return new DNA(newGenes);
}
mutate() {
for (let i = 0; i < this.genes.length; i++) {
if (random(1) < mutationRate) {
this.genes[i] = p5.Vector.random2D();
this.genes[i].setMag(maxForce);
}
}
}
}
// GA variables
let population;
let lifespan = 400;
let count = 0;
let target;
let maxForce = 0.2;
let mutationRate = 0.01;
let obstacle;
function setup() {
createCanvas(400, 300);
population = new Population();
target = createVector(width / 2, 50);
obstacle = {
x: width / 2 - 50,
y: height / 2 - 25,
w: 100,
h: 50
};
}
function draw() {
background(0);
population.run();
count++;
if (count === lifespan) {
population.evaluate();
population.selection();
count = 0;
}
fill(255);
ellipse(target.x, target.y, 16, 16);
fill(175);
rect(obstacle.x, obstacle.y, obstacle.w, obstacle.h);
}
// Population class
class Population {
constructor() {
this.rockets = [];
this.popsize = 100;
this.matingPool = [];
for (let i = 0; i < this.popsize; i++) {
this.rockets[i] = new Rocket();
}
}
evaluate() {
let maxFitness = 0;
for (let i = 0; i < this.popsize; i++) {
this.rockets[i].calcFitness();
if (this.rockets[i].fitness > maxFitness) {
maxFitness = this.rockets[i].fitness;
}
}
for (let i = 0; i < this.popsize; i++) {
this.rockets[i].fitness /= maxFitness;
}
this.matingPool = [];
for (let i = 0; i < this.popsize; i++) {
let n = this.rockets[i].fitness * 100;
for (let j = 0; j < n; j++) {
this.matingPool.push(this.rockets[i]);
}
}
}
selection() {
let newRockets = [];
for (let i = 0; i < this.rockets.length; i++) {
let parentA = random(this.matingPool).dna;
let parentB = random(this.matingPool).dna;
let child = parentA.crossover(parentB);
child.mutate();
newRockets[i] = new Rocket(child);
}
this.rockets = newRockets;
}
run() {
for (let i = 0; i < this.popsize; i++) {
this.rockets[i].update();
this.rockets[i].show();
}
}
}