Skip to content

Commit 4d3976e

Browse files
committed
one more neuroevolution example
1 parent 28be083 commit 4d3976e

File tree

5 files changed

+203
-0
lines changed

5 files changed

+203
-0
lines changed
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
class Creature {
2+
constructor(x, y, brain) {
3+
this.position = createVector(x, y);
4+
this.velocity = createVector(0, 0);
5+
this.acceleration = createVector(0, 0);
6+
this.fullSize = 12;
7+
this.r = this.fullSize;
8+
this.maxspeed = 2;
9+
this.sensors = [];
10+
this.health = 100;
11+
12+
let totalSensors = 15;
13+
for (let i = 0; i < totalSensors; i++) {
14+
let a = map(i, 0, totalSensors, 0, TWO_PI);
15+
let v = p5.Vector.fromAngle(a);
16+
v.mult(this.fullSize * 1.5);
17+
this.sensors[i] = new Sensor(v);
18+
}
19+
20+
if (brain) {
21+
this.brain = brain;
22+
} else {
23+
this.brain = ml5.neuralNetwork({
24+
inputs: this.sensors.length,
25+
outputs: 2,
26+
task: "regression",
27+
neuroEvolution: true,
28+
});
29+
}
30+
}
31+
32+
reproduce() {
33+
let brain = this.brain.copy();
34+
brain.mutate(0.1);
35+
return new Creature(this.position.x, this.position.y, brain);
36+
}
37+
38+
eat() {
39+
for (let i = 0; i < food.length; i++) {
40+
let d = p5.Vector.dist(this.position, food[i].position);
41+
if (d < this.r + food[i].r) {
42+
this.health += 0.5;
43+
food[i].r -= 0.05;
44+
if (food[i].r < 20) {
45+
food[i] = new Food();
46+
}
47+
}
48+
}
49+
}
50+
51+
think() {
52+
for (let i = 0; i < this.sensors.length; i++) {
53+
this.sensors[i].value = 0;
54+
for (let j = 0; j < food.length; j++) {
55+
this.sensors[i].sense(this.position, food[j]);
56+
}
57+
}
58+
let inputs = [];
59+
for (let i = 0; i < this.sensors.length; i++) {
60+
inputs[i] = this.sensors[i].value;
61+
}
62+
63+
// Predicting the force to apply
64+
const outputs = this.brain.predictSync(inputs);
65+
let angle = outputs[0].value * TWO_PI;
66+
let magnitude = outputs[1].value;
67+
let force = p5.Vector.fromAngle(angle).setMag(magnitude);
68+
this.applyForce(force);
69+
}
70+
71+
// Method to update location
72+
update() {
73+
// Update velocity
74+
this.velocity.add(this.acceleration);
75+
// Limit speed
76+
this.velocity.limit(this.maxspeed);
77+
this.position.add(this.velocity);
78+
// Reset acceleration to 0 each cycle
79+
this.acceleration.mult(0);
80+
this.health -= 0.25;
81+
}
82+
83+
// Wraparound
84+
borders() {
85+
if (this.position.x < -this.r) this.position.x = width + this.r;
86+
if (this.position.y < -this.r) this.position.y = height + this.r;
87+
if (this.position.x > width + this.r) this.position.x = -this.r;
88+
if (this.position.y > height + this.r) this.position.y = -this.r;
89+
}
90+
91+
applyForce(force) {
92+
// We could add mass here if we want A = F / M
93+
this.acceleration.add(force);
94+
}
95+
96+
show() {
97+
push();
98+
translate(this.position.x, this.position.y);
99+
for (let sensor of this.sensors) {
100+
stroke(0, this.health * 2);
101+
line(0, 0, sensor.v.x, sensor.v.y);
102+
if (sensor.value > 0) {
103+
fill(255, sensor.value * 255);
104+
stroke(0, 100);
105+
circle(sensor.v.x, sensor.v.y, 4);
106+
}
107+
}
108+
noStroke();
109+
fill(0, this.health * 2);
110+
this.r = map(this.health, 0, 100, 2, this.fullSize);
111+
this.r = constrain(this.r, 2, this.fullSize);
112+
circle(0, 0, this.r * 2);
113+
pop();
114+
}
115+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
class Food {
2+
constructor() {
3+
this.position = createVector(random(width), random(height));
4+
this.r = 50;
5+
}
6+
7+
show() {
8+
noStroke();
9+
fill(0, 100);
10+
circle(this.position.x, this.position.y, this.r * 2);
11+
}
12+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
7+
<title>ml5.js NeuroEvolution Steering</title>
8+
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.6.0/p5.js"></script>
9+
<script src="../../dist/ml5.js"></script>
10+
</head>
11+
<body>
12+
<script src="sensor.js"></script>
13+
<script src="food.js"></script>
14+
<script src="vehicle.js"></script>
15+
<script src="sketch.js"></script>
16+
</body>
17+
</html>
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
class Sensor {
2+
constructor(v) {
3+
this.v = v.copy();
4+
this.value = 0;
5+
}
6+
7+
sense(position, food) {
8+
//{!1} Find the "tip" (or endpoint) of the sensor by adding position
9+
let end = p5.Vector.add(position, this.v);
10+
//{!1} How far is it from the food center
11+
let d = end.dist(food.position);
12+
//{!1} If it is within the radius light up the sensor
13+
if (d < food.r) {
14+
// The further into the center the food, the more the sensor activates
15+
this.value = 1;
16+
} else {
17+
// this.value = 0;
18+
}
19+
}
20+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
let bloops = [];
2+
let timeSlider;
3+
let food = [];
4+
5+
function setup() {
6+
createCanvas(640, 240);
7+
ml5.tf.setBackend("cpu");
8+
for (let i = 0; i < 20; i++) {
9+
bloops[i] = new Creature(random(width), random(height));
10+
}
11+
for (let i = 0; i < 8; i++) {
12+
food[i] = new Food();
13+
}
14+
timeSlider = createSlider(1, 20, 1);
15+
}
16+
17+
function draw() {
18+
background(255);
19+
for (let i = 0; i < timeSlider.value(); i++) {
20+
for (let i = bloops.length - 1; i >= 0; i--) {
21+
bloops[i].think();
22+
bloops[i].eat();
23+
bloops[i].update();
24+
bloops[i].borders();
25+
if (bloops[i].health < 0) {
26+
bloops.splice(i, 1);
27+
} else if (random(1) < 0.001) {
28+
let child = bloops[i].reproduce();
29+
bloops.push(child);
30+
}
31+
}
32+
}
33+
for (let treat of food) {
34+
treat.show();
35+
}
36+
for (let bloop of bloops) {
37+
bloop.show();
38+
}
39+
}

0 commit comments

Comments
 (0)