Skip to content

Commit aa48916

Browse files
committed
neuro evollution steering 2
1 parent 023b9e5 commit aa48916

File tree

5 files changed

+435
-0
lines changed

5 files changed

+435
-0
lines changed
+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6+
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
7+
<title>NeuroEvolution Steering</title>
8+
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.8.0/p5.min.js"></script>
9+
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.8.0/addons/p5.dom.min.js"></script>
10+
<script src="https://cdn.jsdelivr.net/npm/@tensorflow/[email protected]/dist/tf.min.js"></script>
11+
</head>
12+
<body>
13+
<script src="nn.js"></script>
14+
<script src="sketch.js"></script>
15+
<script src="vehicle.js"></script>
16+
<script src="obstacle.js"></script>
17+
<div id="canvascontainer"></div>
18+
<div id="interface">
19+
<p>
20+
speed: <input id="speedSlider" type="range" min="1" max="10" value="1"> <span id="speed">1</span>
21+
</p>
22+
<p>
23+
debug: <input id="debug" type="checkbox"></input>
24+
</p>
25+
</div>
26+
27+
</body>
28+
</html>
29+

05_neuro_evolution_steering_2/nn.js

+96
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
// Daniel Shiffman
2+
// Nature of Code
3+
// https://github.com/nature-of-code/noc-syllabus-S19
4+
5+
class NeuralNetwork {
6+
constructor(a, b, c, d) {
7+
if (a instanceof tf.Sequential) {
8+
this.model = a;
9+
this.num_inputs = b;
10+
this.num_hidden = c;
11+
this.num_outputs = d;
12+
} else {
13+
this.num_inputs = a;
14+
this.num_hidden = b;
15+
this.num_outputs = c;
16+
this.model = this.createModel();
17+
}
18+
}
19+
20+
createModel() {
21+
const model = tf.sequential();
22+
let hidden = tf.layers.dense({
23+
inputShape: [this.num_inputs],
24+
units: this.num_hidden,
25+
activation: 'tanh'
26+
});
27+
let output = tf.layers.dense({
28+
units: this.num_outputs,
29+
activation: 'tanh'
30+
});
31+
model.add(hidden);
32+
model.add(output);
33+
return model;
34+
}
35+
36+
dispose() {
37+
this.model.dispose();
38+
}
39+
40+
save() {
41+
this.model.save('downloads://vehicle`-brain');
42+
}
43+
44+
// Synchronous for now
45+
predict(input_array) {
46+
// console.log(input_array);
47+
return tf.tidy(() => {
48+
let xs = tf.tensor([input_array]);
49+
let ys = this.model.predict(xs);
50+
let y_values = ys.dataSync();
51+
return y_values;
52+
});
53+
}
54+
55+
// Adding function for neuro-evolution
56+
copy() {
57+
return tf.tidy(() => {
58+
const modelCopy = this.createModel();
59+
const w = this.model.getWeights();
60+
for (let i = 0; i < w.length; i++) {
61+
w[i] = w[i].clone();
62+
}
63+
modelCopy.setWeights(w);
64+
const nn = new NeuralNetwork(modelCopy, this.num_inputs, this.num_hidden, this.num_outputs);
65+
return nn;
66+
});
67+
}
68+
69+
// Accept an arbitrary function for mutation
70+
mutate(rate) {
71+
tf.tidy(() => {
72+
const w = this.model.getWeights();
73+
for (let i = 0; i < w.length; i++) {
74+
let shape = w[i].shape;
75+
let arr = w[i].dataSync().slice();
76+
for (let j = 0; j < arr.length; j++) {
77+
arr[j] = mutateWeight(arr[j], rate);
78+
}
79+
let newW = tf.tensor(arr, shape);
80+
w[i] = newW;
81+
}
82+
this.model.setWeights(w);
83+
});
84+
}
85+
}
86+
87+
// Mutation function to be passed into bird.brain
88+
function mutateWeight(x, rate) {
89+
if (random(1) < rate) {
90+
let offset = randomGaussian() * 0.5;
91+
let newx = x + offset;
92+
return newx;
93+
} else {
94+
return x;
95+
}
96+
}
+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
class Obstacle {
2+
constructor(x, y) {
3+
this.pos = createVector(x, y);
4+
this.r = 16;
5+
}
6+
7+
update() {
8+
this.pos.y += 4;
9+
}
10+
11+
show() {
12+
stroke(255);
13+
noFill();
14+
ellipse(this.pos.x, this.pos.y, this.r * 2);
15+
}
16+
}
+107
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
// Daniel Shiffman
2+
// Nature of Code
3+
// https://github.com/nature-of-code/noc-syllabus-S19
4+
5+
// Evolutionary "Steering Behavior" Simulation
6+
7+
// An array of vehicles
8+
let population = [];
9+
10+
// An array of "obstacles"
11+
let obstacles = [];
12+
13+
// Checkbox to show additional info
14+
let debug;
15+
16+
// Slider to speed up simulation
17+
let speedSlider;
18+
let speedSpan;
19+
20+
let totalVehicles = 30;
21+
22+
let best = null;
23+
24+
function setup() {
25+
tf.setBackend('cpu');
26+
27+
// Add canvas and grab checkbox and slider
28+
let canvas = createCanvas(640, 480);
29+
canvas.parent('canvascontainer');
30+
debug = select('#debug');
31+
speedSlider = select('#speedSlider');
32+
speedSpan = select('#speed');
33+
34+
// Create initial population
35+
for (let i = 0; i < totalVehicles; i++) {
36+
population[i] = new Vehicle();
37+
}
38+
}
39+
40+
let counter = 0;
41+
42+
function draw() {
43+
background(0);
44+
45+
// How fast should we speed up
46+
let cycles = speedSlider.value();
47+
speedSpan.html(cycles);
48+
49+
// Variable to keep track of highest scoring vehicle
50+
51+
// Run the simulation "cycles" amount of time
52+
for (let n = 0; n < cycles; n++) {
53+
// Always keep a minimum amount of food
54+
if ((frameCount * cycles + n) % 15 == 0) {
55+
obstacles.push(new Obstacle(random(-50, width), 0));
56+
}
57+
58+
let record = -1;
59+
// Go through all vehicles and find the best!
60+
for (let i = population.length - 1; i >= 0; i--) {
61+
let v = population[i];
62+
v.update();
63+
const dead = v.check(obstacles);
64+
// If the vehicle has died, remove
65+
if (dead) {
66+
population.splice(i, 1);
67+
} else {
68+
// Is it the vehicles that has lived the longest?
69+
if (v.score > record) {
70+
record = v.score;
71+
best = v;
72+
}
73+
}
74+
}
75+
76+
// If there is less than 20 apply reproduction
77+
while (population.length < 20) {
78+
for (let v of population) {
79+
// Every vehicle has a chance of cloning itself according to score
80+
// Argument to "clone" is probability
81+
let newVehicle = v.clone((0.1 * v.score) / record);
82+
// If there is a child
83+
if (newVehicle != null) {
84+
population.push(newVehicle);
85+
}
86+
}
87+
}
88+
89+
for (let i = 0; i < obstacles.length; i++) {
90+
obstacles[i].update();
91+
}
92+
}
93+
94+
// Draw all the food
95+
for (let i = 0; i < obstacles.length; i++) {
96+
obstacles[i].show();
97+
}
98+
99+
best.highlight();
100+
101+
// Draw all the vehicles
102+
if (debug.checked()) {
103+
for (let v of population) {
104+
v.debug();
105+
}
106+
}
107+
}

0 commit comments

Comments
 (0)