Skip to content

Commit

Permalink
Merge pull request #336 from nature-of-code/notion-update-docs
Browse files Browse the repository at this point in the history
[Notion] Chapter 9 rewrite completed!
  • Loading branch information
shiffman authored Jun 28, 2023
2 parents ae8160e + 83b697d commit 8c91394
Show file tree
Hide file tree
Showing 12 changed files with 318 additions and 402 deletions.
2 changes: 1 addition & 1 deletion content/06_libraries.html
Original file line number Diff line number Diff line change
Expand Up @@ -1336,7 +1336,7 @@ <h2 id="connected-systems-part-i-string">Connected Systems, Part I: String</h2>
<img src="images/06_libraries/06_libraries_15.png" alt="Figure 6.11: Soft body simulation designs">
<figcaption>Figure 6.11: Soft body simulation designs</figcaption>
</figure>
<p>Let's begin by simulating a "soft pendulum"—a bob hanging from a string, instead of a rigid arm—and use the design from Figure 6.14 as the basis. Toxiclibs.js does offer a convenient <code><strong>ParticleString2D</strong></code> class that creates a string of connected particles in a single constructor call. However, for demonstration purposes, I will create my own array using a <code><strong>for</strong></code> loop with the goal of giving you a deeper understanding of the system and enabling you to create your own custom designs beyond a single string in the future.</p>
<p>Let's begin by simulating a "soft pendulum"—a bob hanging from a string, instead of a rigid arm—and use the design from Figure 6.14 as the basis. Toxiclibs.js does offer a convenient <code>ParticleString2D</code> class that creates a string of connected particles in a single constructor call. However, for demonstration purposes, I will create my own array using a <code><strong>for</strong></code> loop with the goal of giving you a deeper understanding of the system and enabling you to create your own custom designs beyond a single string in the future.</p>
<p>First, I’l need an array of particles (let’s use the same <code>Particle</code> class built in the previous example).</p>
<pre class="codesplit" data-code-language="javascript">let particles = [];</pre>
<p>Now, let’s say I want to have 20 particles, all spaced 10 pixels apart.</p>
Expand Down
450 changes: 201 additions & 249 deletions content/09_ga.html

Large diffs are not rendered by default.

6 changes: 2 additions & 4 deletions content/examples/09_ga/9_4_interactive_selection/dna.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,21 @@ class DNA {
constructor(newgenes) {
// DNA is random floating point values between 0 and 1 (!!)
// The genetic sequence
let length = 20; // Arbitrary length
if (newgenes) {
this.genes = newgenes;
} else {
this.genes = new Array(length);
this.genes = new Array(14);
for (let i = 0; i < this.genes.length; i++) {
this.genes[i] = random(0, 1);
}
}
}

crossover(partner) {
let child = new DNA(this.genes.length);
let child = new DNA();

//{!1} Picking a random “midpoint” in the genes array
let midpoint = floor(random(this.genes.length));

for (let i = 0; i < this.genes.length; i++) {
// Before the midpoint genes from this DNA
if (i < midpoint) {
Expand Down
60 changes: 28 additions & 32 deletions content/examples/09_ga/9_4_interactive_selection/flower.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ class Flower {
this.dna = dna; // Flower's DNA
this.x = x; // Position on screen
this.y = y;
let w = 70; // Size of square enclosing flower
let h = 140; // Size of square enclosing flower
this.w = 70; // Size of square enclosing flower
this.h = 140; // Size of square enclosing flower
this.fitness = 1; // How good is this flower?
this.boundingBox = new Rectangle(
this.x - w / 2,
this.y - h / 2,
this.x - this.w / 2,
this.y - this.h / 2,
this.w,
this.h
);
Expand All @@ -29,18 +29,32 @@ class Flower {
// Display the flower
show() {
let genes = this.dna.genes;
let c = color(genes[0], genes[1], genes[2]); // petal color
let size = map(genes[3], 0, 1, 0, this.wh / 4); // petal size
let count = floor(map(genes[4], 0, 1, 0, 10)); // petal count
let centerColor = color(genes[5], genes[6], genes[7]); // center color
let centerSize = map(genes[8], 0, 1, 0, this.wh / 8); // center size
let stemColor = color(genes[9], genes[10], genes[11]); // stem color
let stemLength = map(genes[12], 0, 1, 0, (this.wh * 3) / 4); // stem length
let c = color(genes[0], genes[1], genes[2], genes[3]); // petal color
let size = map(genes[4], 0, 1, 4, 24); // petal size
let count = floor(map(genes[5], 0, 1, 2, 16)); // petal count
let centerColor = color(genes[6], genes[7], genes[8]); // center color
let centerSize = map(genes[9], 0, 1, 24, 48); // center size
let stemColor = color(genes[10], genes[11], genes[12]); // stem color
let stemLength = map(genes[13], 0, 1, 50, 100); // stem length

push();
translate(this.x, this.y);
noStroke();
// Draw the bounding box
if (this.rolloverOn) fill(0, 0.25);
else noFill();
stroke(0);
strokeWeight(0.5);
rectMode(CENTER);
rect(0, 0, this.w, this.h);

translate(0, this.h / 2 - stemLength);

// Draw the stem
stroke(stemColor);
strokeWeight(4);
line(0, 0, 0, stemLength);

noStroke();
// Draw the petals
fill(c);
for (let i = 0; i < count; i++) {
Expand All @@ -54,36 +68,18 @@ class Flower {
fill(centerColor);
ellipse(0, 0, centerSize, centerSize);

// Draw the stem
fill(stemColor);
rect(0, centerSize / 2 + stemLength / 2, 5, stemLength);

// Draw the bounding box
stroke(0.25);
if (this.rolloverOn) fill(0, 0.25);
else noFill();
rectMode(CENTER);
rect(0, 0, this.wh, this.wh);
pop();

// Display fitness value
textAlign(CENTER);
if (this.rolloverOn) fill(0);
else fill(0.25);
text("" + floor(this.fitness), this.x, this.y + 55);
}

getFitness() {
return this.fitness;
}

getDNA() {
return this.dna;
text("" + floor(this.fitness), this.x, this.y + 90);
}

// Increment fitness if mouse is rolling over flower
rollover(mx, my) {
if (this.r.contains(mx, my)) {
if (this.boundingBox.contains(mx, my)) {
this.rolloverOn = true;
this.fitness += 0.25;
} else {
Expand Down
59 changes: 21 additions & 38 deletions content/examples/09_ga/9_4_interactive_selection/population.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,37 +12,25 @@
class Population {
constructor(mutationRate, size) {
this.mutationRate = mutationRate; // Mutation rate
this.population = []; // array to hold the current population
this.flowers = []; // array to hold the current population
this.matingPool = [];
this.generations = 0; // Number of generations
for (let i = 0; i < size; i++) {
this.population[i] = new Flower(new DNA(), 40 + i * 80, 60);
this.flowers[i] = new Flower(new DNA(), 40 + i * 80, 80);
}
}

// Display all faces
show() {
for (let i = 0; i < this.population.length; i++) {
this.population[i].show();
for (let i = 0; i < this.flowers.length; i++) {
this.flowers[i].show();
}
}

// Are we rolling over any of the faces?
rollover(mx, my) {
for (let i = 0; i < this.population.length; i++) {
this.population[i].rollover(mx, my);
}
}

selection() {
// Sum all of the fitness values
let totalFitness = 0;
for (let i = 0; i < this.population.length; i++) {
totalFitness += this.population[i].fitness;
}
// Divide by the total to normalize the fitness values
for (let i = 0; i < this.population.length; i++) {
this.population[i].fitness /= totalFitness;
for (let i = 0; i < this.flowers.length; i++) {
this.flowers[i].rollover(mx, my);
}
}

Expand All @@ -54,47 +42,42 @@ class Population {
// At the finish line?
while (start > 0) {
// Move a distance according to fitness
start = start - population[index].fitness;
start = start - this.flowers[index].fitness;
// Next element
index++;
}
// Undo moving to the next element since the finish has been reached
index--;
return this.population[index];
return this.flowers[index];
}

selection() {
// Sum all of the fitness values
let totalFitness = 0;
for (let i = 0; i < this.population.length; i++) {
totalFitness += this.population[i].fitness;
for (let i = 0; i < this.flowers.length; i++) {
totalFitness += this.flowers[i].fitness;
}
// Divide by the total to normalize the fitness values
for (let i = 0; i < this.population.length; i++) {
this.population[i].fitness /= totalFitness;
for (let i = 0; i < this.flowers.length; i++) {
this.flowers[i].fitness /= totalFitness;
}
}

// Making the next generation
reproduction() {
// Refill the population with children from the mating pool
for (let i = 0; i < this.population.length; i++) {
let nextFlowers = [];
// Create the next population with children from the mating pool
for (let i = 0; i < this.flowers.length; i++) {
// Sping the wheel of fortune to pick two parents
let m = floor(random(this.matingPool.length));
let d = floor(random(this.matingPool.length));
// Pick two parents
let mom = this.matingPool[m];
let dad = this.matingPool[d];
// Get their genes
let momgenes = mom.getDNA();
let dadgenes = dad.getDNA();
// Mate their genes
let child = momgenes.crossover(dadgenes);
let parentA = this.weightedSelection();
let parentB = this.weightedSelection();
let child = parentA.dna.crossover(parentB.dna);
// Mutate their genes
child.mutate(this.mutationRate);
// Fill the new population with the new child
this.population[i] = new Flower(child, 40 + i * 80, 60);
nextFlowers[i] = new Flower(child, 40 + i * 80, 80);
}
// Replace the old population
this.flowers = nextFlowers;
this.generations++;
}
}
20 changes: 11 additions & 9 deletions content/examples/09_ga/9_4_interactive_selection/sketch.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,28 +9,30 @@ let population;

function setup() {
createCanvas(640, 240);
colorMode(RGB, 1.0, 1.0, 1.0, 1.0);
colorMode(RGB, 1);
// This is a very small population!
let populationSize = 8;
let mutationRate = 0.05; // A pretty high mutation rate here, our population is rather small we need to enforce variety
// Create a population with a target phrase, mutation rate, and population max
// A pretty high mutation rate here, our population is rather small we need to enforce variety
let mutationRate = 0.05;
// Create the population
population = new Population(mutationRate, populationSize);
// A simple button class
// A p5.js button
button = createButton("evolve new generation");
button.mousePressed(nextGeneration);
button.position(10, 200);
button.position(10, 210);
}

function draw() {
background(1);
// Display the faces
// Draw the flowers
population.show();
// Check for increasing fitness
population.rollover(mouseX, mouseY);
textFont("Courier");
textAlign(LEFT);
text("Generation " + population.generations, 12, height - 48);
text("Generation " + population.generations, 12, height - 40);
}

// If the button is clicked, evolve next generation
// If the button is pressed, evolve next generation
function nextGeneration() {
population.selection();
population.reproduction();
Expand Down
27 changes: 12 additions & 15 deletions content/examples/09_ga/9_5_evolving_bloops/bloop.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,17 @@ class Bloop {
}

// A bloop can find food and eat it
eat(f) {
let food = f.getFood();
// Are we touching any food objects?
for (let i = food.length - 1; i >= 0; i--) {
let foodLocation = food[i];
let d = p5.Vector.dist(this.position, foodLocation);
// If we are, juice up our strength!
if (d < this.r / 2) {
eat(food) {
// Check all the food vectors
let positions = food.foodPositions;
for (let i = positions.length - 1; i >= 0; i--) {
// How far away is the bloop?
let distance = p5.Vector.dist(this.position, positions[i]);
// If the food is nearby
if (distance < this.r * 2) {
// Increase health and remove the food!
this.health += 100;
food.splice(i, 1);
positions.splice(i, 1);
}
}
}
Expand All @@ -49,7 +50,7 @@ class Bloop {
let childDNA = this.dna.copy();
// Child DNA can mutate
childDNA.mutate(0.01);
return new Bloop(this.position, childDNA);
return new Bloop(this.position.copy(), childDNA);
} else {
return null;
}
Expand Down Expand Up @@ -86,10 +87,6 @@ class Bloop {

// Death
dead() {
if (this.health < 0.0) {
return true;
} else {
return false;
}
return this.health < 0.0;
}
}
16 changes: 5 additions & 11 deletions content/examples/09_ga/9_5_evolving_bloops/dna.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
// Class to describe DNA
// Has more features for two parent mating (not used in this example)


// Constructor (makes a random DNA)
class DNA {
constructor(newgenes) {
Expand All @@ -21,21 +20,16 @@ class DNA {
}

copy() {
// should switch to fancy JS array copy
let newgenes = [];
for (let i = 0; i < this.genes.length; i++) {
newgenes[i] = this.genes[i];
}

let newgenes = this.genes.slice();
return new DNA(newgenes);
}

// Based on a mutation probability, picks a new random character in array spots
mutate(m) {
mutate(mutationRate) {
for (let i = 0; i < this.genes.length; i++) {
if (random(1) < m) {
this.genes[i] = random(0, 1);
if (random(1) < mutationRate) {
this.genes[i] = random(1);
}
}
}
}
}
Loading

0 comments on commit 8c91394

Please sign in to comment.