Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions examples/NeuralNetwork-color-classifier/sketch.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ let label = "training";

function setup() {
createCanvas(640, 240);

// For this example to work across all browsers
// "webgl" or "cpu" needs to be set as the backend
ml5.setBackend("webgl");

rSlider = createSlider(0, 255, 255).position(10, 20);
gSlider = createSlider(0, 255, 0).position(10, 40);
bSlider = createSlider(0, 255, 0).position(10, 60);
Expand Down
2 changes: 1 addition & 1 deletion examples/NeuralNetwork-mouse-gesture/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>ml5.js Neural Network Color Classifier</title>
<title>ml5.js Neural Network Gesture Classifier</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.6.0/p5.js"></script>
<script src="../../dist/ml5.js"></script>
</head>
Expand Down
4 changes: 4 additions & 0 deletions examples/NeuralNetwork-mouse-gesture/sketch.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ let start, end;

function setup() {
createCanvas(640, 240);
// For this example to work across all browsers
// "webgl" or "cpu" needs to be set as the backend
ml5.setBackend("webgl");

// Step 2: set your neural network options
let options = {
task: "classification",
Expand Down
77 changes: 77 additions & 0 deletions examples/NeuroEvolution-flappy-bird/bird.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
class Bird {
constructor(brain) {
if (brain) {
this.brain = brain;
} else {
// A bird's brain receives 4 inputs and classifies them into one of two labels
this.brain = ml5.neuralNetwork({
inputs: 4,
outputs: ["flap", "no flap"],
task: "classification",
neuroEvolution: true,
});
}

// The bird's position (x will be constant)
this.x = 50;
this.y = 120;

// Velocity and forces are scalar since the bird only moves along the y-axis
this.velocity = 0;
this.gravity = 0.5;
this.flapForce = -10;

// Adding a fitness
this.fitness = 0;
this.alive = true;
}

think(pipes) {
let nextPipe = null;
for (let pipe of pipes) {
if (pipe.x + pipe.w > this.x) {
nextPipe = pipe;
break;
}
}

let inputs = [
this.y / height,
this.velocity / height,
nextPipe.top / height,
(nextPipe.x - this.x) / width,
];

let results = this.brain.classifySync(inputs);
if (results[0].label == "flap") {
this.flap();
}
}

// The bird flaps its wings
flap() {
this.velocity += this.flapForce;
}

update() {
// Add gravity
this.velocity += this.gravity;
this.y += this.velocity;
// Dampen velocity
this.velocity *= 0.95;

// Handle the "floor"
if (this.y > height || this.y < 0) {
this.alive = false;
}

this.fitness++;
}

show() {
strokeWeight(2);
stroke(0);
fill(127, 200);
circle(this.x, this.y, 16);
}
}
16 changes: 16 additions & 0 deletions examples/NeuroEvolution-flappy-bird/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>ml5.js NeuroEvolution Flappy Bird</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.6.0/p5.js"></script>
<script src="../../dist/ml5.js"></script>
</head>
<body>
<script src="bird.js"></script>
<script src="pipe.js"></script>
<script src="sketch.js"></script>
</body>
</html>
34 changes: 34 additions & 0 deletions examples/NeuroEvolution-flappy-bird/pipe.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
class Pipe {
constructor() {
this.spacing = 100;
this.top = random(height - this.spacing);
this.bottom = this.top + this.spacing;
this.x = width;
this.w = 20;
this.speed = 2;
}

collides(bird) {
// Is the bird within the vertical range of the top or bottom pipe?
let verticalCollision = bird.y < this.top || bird.y > this.bottom;
// Is the bird within the horizontal range of the pipes?
let horizontalCollision = bird.x > this.x && bird.x < this.x + this.w;
// If it's both a vertical and horizontal hit, it's a hit!
return verticalCollision && horizontalCollision;
}

show() {
fill(0);
noStroke();
rect(this.x, 0, this.w, this.top);
rect(this.x, this.bottom, this.w, height - this.bottom);
}

update() {
this.x -= this.speed;
}

offscreen() {
return this.x < -this.w;
}
}
97 changes: 97 additions & 0 deletions examples/NeuroEvolution-flappy-bird/sketch.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
let birds = [];
let pipes = [];

function setup() {
createCanvas(640, 240);
// cpu is higher performance for tiny neural networks like in this example
ml5.setBackend("cpu");

for (let i = 0; i < 200; i++) {
birds[i] = new Bird();
}
pipes.push(new Pipe());
}

function draw() {
background(255);

for (let i = pipes.length - 1; i >= 0; i--) {
pipes[i].update();
pipes[i].show();
if (pipes[i].offscreen()) {
pipes.splice(i, 1);
}
}

for (let bird of birds) {
if (bird.alive) {
for (let pipe of pipes) {
if (pipe.collides(bird)) {
bird.alive = false;
}
}
bird.think(pipes);
bird.update();
bird.show();
}
}

if (frameCount % 100 == 0) {
pipes.push(new Pipe());
}

if (allBirdsDead()) {
normalizeFitness();
reproduction();
}
}

function allBirdsDead() {
for (let bird of birds) {
if (bird.alive) {
return false;
}
}
return true;
}

function reproduction() {
let nextBirds = [];
for (let i = 0; i < birds.length; i++) {
let parentA = weightedSelection();
let parentB = weightedSelection();
let child = parentA.crossover(parentB);
child.mutate(0.01);
nextBirds[i] = new Bird(child);
}
birds = nextBirds;
}

// Normalize all fitness values
function normalizeFitness() {
let sum = 0;
for (let bird of birds) {
sum += bird.fitness;
}
for (let bird of birds) {
bird.fitness = bird.fitness / sum;
}
}

function weightedSelection() {
// Start with the first element
let index = 0;
// Pick a starting point
let start = random(1);
// At the finish line?
while (start > 0) {
// Move a distance according to fitness
start = start - birds[index].fitness;
// Next element
index++;
}
// Undo moving to the next element since the finish has been reached
index--;
// Instead of returning the entire Bird object, just the brain is returned
return birds[index].brain;
}
115 changes: 115 additions & 0 deletions examples/NeuroEvolution-sensors/creature.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
class Creature {
constructor(x, y, brain) {
this.position = createVector(x, y);
this.velocity = createVector(0, 0);
this.acceleration = createVector(0, 0);
this.fullSize = 12;
this.r = this.fullSize;
this.maxspeed = 2;
this.sensors = [];
this.health = 100;

let totalSensors = 15;
for (let i = 0; i < totalSensors; i++) {
let a = map(i, 0, totalSensors, 0, TWO_PI);
let v = p5.Vector.fromAngle(a);
v.mult(this.fullSize * 1.5);
this.sensors[i] = new Sensor(v);
}

if (brain) {
this.brain = brain;
} else {
this.brain = ml5.neuralNetwork({
inputs: this.sensors.length,
outputs: 2,
task: "regression",
neuroEvolution: true,
});
}
}

reproduce() {
let brain = this.brain.copy();
brain.mutate(0.1);
return new Creature(this.position.x, this.position.y, brain);
}

eat() {
for (let i = 0; i < food.length; i++) {
let d = p5.Vector.dist(this.position, food[i].position);
if (d < this.r + food[i].r) {
this.health += 0.5;
food[i].r -= 0.05;
if (food[i].r < 20) {
food[i] = new Food();
}
}
}
}

think() {
for (let i = 0; i < this.sensors.length; i++) {
this.sensors[i].value = 0;
for (let j = 0; j < food.length; j++) {
this.sensors[i].sense(this.position, food[j]);
}
}
let inputs = [];
for (let i = 0; i < this.sensors.length; i++) {
inputs[i] = this.sensors[i].value;
}

// Predicting the force to apply
const outputs = this.brain.predictSync(inputs);
let angle = outputs[0].value * TWO_PI;
let magnitude = outputs[1].value;
let force = p5.Vector.fromAngle(angle).setMag(magnitude);
this.applyForce(force);
}

// Method to update location
update() {
// Update velocity
this.velocity.add(this.acceleration);
// Limit speed
this.velocity.limit(this.maxspeed);
this.position.add(this.velocity);
// Reset acceleration to 0 each cycle
this.acceleration.mult(0);
this.health -= 0.25;
}

// Wraparound
borders() {
if (this.position.x < -this.r) this.position.x = width + this.r;
if (this.position.y < -this.r) this.position.y = height + this.r;
if (this.position.x > width + this.r) this.position.x = -this.r;
if (this.position.y > height + this.r) this.position.y = -this.r;
}

applyForce(force) {
// We could add mass here if we want A = F / M
this.acceleration.add(force);
}

show() {
push();
translate(this.position.x, this.position.y);
for (let sensor of this.sensors) {
stroke(0, this.health * 2);
line(0, 0, sensor.v.x, sensor.v.y);
if (sensor.value > 0) {
fill(255, sensor.value * 255);
stroke(0, 100);
circle(sensor.v.x, sensor.v.y, 4);
}
}
noStroke();
fill(0, this.health * 2);
this.r = map(this.health, 0, 100, 2, this.fullSize);
this.r = constrain(this.r, 2, this.fullSize);
circle(0, 0, this.r * 2);
pop();
}
}
12 changes: 12 additions & 0 deletions examples/NeuroEvolution-sensors/food.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
class Food {
constructor() {
this.position = createVector(random(width), random(height));
this.r = 50;
}

show() {
noStroke();
fill(0, 100);
circle(this.position.x, this.position.y, this.r * 2);
}
}
Loading