Created
December 9, 2017 22:43
-
-
Save pcornier/8aa8e5145bcd0abc01ccb9851196b74a to your computer and use it in GitHub Desktop.
A genetic algorithm that drives a car. Require p5.js
This file contains 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
let car; | |
let target; | |
let preview = []; | |
let speed = 0.01; | |
let error = Infinity; | |
let gen = 0; | |
let parent; | |
function setup() { | |
createCanvas(320, 240); | |
car = { | |
w: 10, // half width | |
h: 20, // half height | |
oa: createVector(160, 100), | |
ob: createVector(160, 140), | |
a: createVector(0, 0), | |
b: createVector(0, 0), | |
r: 0, | |
draw: function() { | |
push(); | |
let p = p5.Vector.add(this.a, this.b).div(2); | |
translate(p.x, p.y); | |
rotate(p5.Vector.sub(this.a, this.b).heading() + HALF_PI); | |
rect(-this.w, -this.h, this.w * 2, this.h * 2); | |
pop(); | |
} | |
}; | |
target = { | |
a: createVector(40, 80), | |
b: createVector(40, 40), | |
draw: function() { | |
push(); | |
translate(this.a.x, this.a.y + car.h); | |
//rect(-car.w, -car.h, car.w * 2, car.h * 2); | |
pop(); | |
fill(255,0,0); | |
ellipse(this.a.x, this.a.y, 5); | |
fill(255); | |
ellipse(this.b.x, this.b.y, 5); | |
} | |
} | |
} | |
function generateSequence() { | |
let seq = []; | |
for (let i = 0; i < 5; i++) { | |
let frames = random(200)|0; | |
seq.push({ | |
angle: random(2*PI), | |
frames: 0, | |
total: frames | |
}); | |
} | |
return seq; | |
} | |
function generateChild(p) { | |
let cp = random(p.length)|0; | |
let child = JSON.parse(JSON.stringify(p)); | |
let mutations = 1 + random(2); | |
for (let i = 0; i < mutations; i++) { | |
let mut = random(child.length)|0; | |
child[mut].angle = random(2*PI); | |
child[mut].total = random(200)|0; | |
} | |
return child; | |
} | |
// t = 0 to 1, b = start value, c = end - start, d = duration (steps) | |
function ease(t, b, c, d) { | |
t /= d/2; | |
if (t < 1) return c/2*t*t*t+b; | |
t -= 2; | |
return c/2*(t*t*t+2)+b; | |
} | |
function evaluate(seq) { | |
let a = car.oa.copy(); | |
let b = car.ob.copy(); | |
let t = 0; | |
for (let i = 0; i < seq.length; i++) { | |
for (let f = 0; f <= seq[i].total; f++) { | |
let p = ease(f, 0, seq[i].total, seq[i].total); | |
let cv = p5.Vector.sub(b, a).normalize(); | |
let d1 = createVector(1, 0).rotate(seq[i].angle).mult(p * speed); | |
a.add(d1); | |
let l = b.dist(a) - car.h * 2; | |
let d2 = p5.Vector.sub(a, b).normalize().mult(l); | |
b.add(d2); | |
} | |
} | |
return a.dist(target.a) + b.dist(target.b); | |
} | |
function draw() { | |
if (preview.length) { | |
let c = preview[0]; | |
if (c.frames < c.total) { | |
// calculate new position | |
let p = ease(c.frames, 0, c.total, c.total); | |
let cv = p5.Vector.sub(car.b, car.a).normalize(); | |
let d1 = createVector(1, 0).rotate(c.angle).mult(p * speed); | |
car.a.add(d1); | |
let l = car.b.dist(car.a) - car.h * 2; | |
let d2 = p5.Vector.sub(car.a, car.b).normalize().mult(l); | |
car.b.add(d2); | |
c.frames++; | |
// draw | |
background(255); | |
stroke(color(0, 0, 0)); | |
target.draw(); | |
car.draw(); | |
stroke(color(0, 255, 0)); | |
line(car.a.x, car.a.y, car.a.x+d1.x*20, car.a.y+d1.y*20); | |
fill(color(255, 0, 0)); | |
noStroke(); | |
ellipse(car.a.x, car.a.y, 5); | |
ellipse(car.b.x, car.b.y, 5); | |
noFill(); | |
stroke(0); | |
rect(0, 0, 319, 239); | |
textSize(10); | |
text('g: ' + gen + ' e: ' + error, 5, 15); | |
} | |
else { | |
preview.shift(); | |
} | |
} | |
else { | |
if (!parent) { | |
parent = generateSequence(); | |
} | |
for (let g = 0; g < 10; g++) { | |
let p = parent; | |
min = error; | |
for (let i = 0; i < 50; i++) { | |
let c = generateChild(p); | |
let e = evaluate(c); | |
if (e < min) { | |
parent = c; | |
min = e; | |
} | |
} | |
error = min; | |
gen++; | |
} | |
preview = JSON.parse(JSON.stringify(parent)); | |
car.a = car.oa.copy(); | |
car.b = car.ob.copy(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment