Skip to content

Instantly share code, notes, and snippets.

@spiterman
Created September 27, 2018 02:28
Show Gist options
  • Select an option

  • Save spiterman/37abd4602005360f31114f9dde2465c0 to your computer and use it in GitHub Desktop.

Select an option

Save spiterman/37abd4602005360f31114f9dde2465c0 to your computer and use it in GitHub Desktop.
Genetic Algorithm v.2.0
class Organism {
constructor(r, g, b) {
this.r = r;
this.g = g;
this.b = b;
this.mutationSize = 20; //Must be greater than 2
}
reproduce() {
// Prevents negative numbers from appearing
let newR = Math.max(this.r + this.mutateTrait(), 0);
let newG = Math.max(this.g + this.mutateTrait(), 0);
let newB = Math.max(this.b + this.mutateTrait(), 0);
return new Organism(newR, newG, newB);
}
mutateTrait() {
let multiplier = Math.random() - 0.5;
if(multiplier < 0) {
return randomValue(this.mutationSize) * -1;
} else {
return randomValue(this.mutationSize);
}
}
}
function randomValue(range) {
return Math.floor(Math.random() * range);
}
class Environment {
constructor(r, g, b, startingOrganisms) {
this.r = r;
this.g = g;
this.b = b;
this.organisms = [];
this.weaknesses = [];
this.fitnesses = [];
this.startingOrganisms = startingOrganisms;
this.addOrganisms(this.startingOrganisms);
this.generationTime = 100
}
// Begins Running the simulation
run() {
let e = this;
let generation = 1;
setInterval(() => {
e.replaceOrganism();
console.log(`Generation: ${generation}`);
console.log(`Average Weaknesses is now: ${e.computeAverageWeakness()}`);
console.log(`Average Fitness is now: ${e.computeAverageFitness()}`);
generation++;
}, e.generationTime);
}
// Adds a list of organisms with random fitnesses
addOrganisms(num) {
while(num > 0) {
let newOrganism = new Organism(randomValue(256), randomValue(256), randomValue(256));
this.organisms.push(newOrganism);
this.weaknesses.push(this.calculateWeakness(newOrganism));
this.fitnesses.push(this.calculateFitness(newOrganism));
num -= 1;
}
}
calculateWeakness(organism) {
return (Math.abs(this.r - organism.r) + Math.abs(this.g - organism.g) + Math.abs(this.b - organism.b));
}
calculateFitness(organism) {
return Math.floor(765/(this.calculateWeakness(organism) + 1)); // Add 1 to avoid dividing by 0
// return 765 - this.calculateWeakness(organism); //Alternate measure of fitness
}
replaceOrganism() {
// 1) Use fitnesses to select individual to breed
let fitnessIntervals = createIntervals(this.fitnesses);
let fitnessRange = fitnessIntervals[fitnessIntervals.length - 1][1];
let randomFitnessValue = randomValue(fitnessRange);
let fitnessIndex = searchIntervals(fitnessIntervals, randomFitnessValue);
let organismToBreed = this.organisms[fitnessIndex];
let newOrganism = organismToBreed.reproduce();
// 2) Use weaknesses to select individual to replace
let weaknessIntervals = createIntervals(this.weaknesses);
let weaknessRange = weaknessIntervals[weaknessIntervals.length - 1][1]; //Sum of all weaknesses
let randomWeaknessValue= randomValue(weaknessRange);
let weaknessIndex = searchIntervals(weaknessIntervals, randomWeaknessValue);
// 3) Clear memory of old organism
delete this.organisms[weaknessIndex];
delete this.weaknesses[weaknessIndex];
delete this.fitnesses[weaknessIndex];
// 4) Replace old organism with new organism
this.organisms[weaknessIndex] = newOrganism
this.weaknesses[weaknessIndex] = this.calculateWeakness(newOrganism);
this.fitnesses[weaknessIndex] = this.calculateFitness(newOrganism);
}
// Compute the average weakness of the population
computeAverageWeakness() {
return this.weaknesses.reduce((sum, current) => sum + current)/ (this.weaknesses.length);
}
computeAverageFitness() {
return this.fitnesses.reduce((sum, current) => sum + current)/ (this.fitnesses.length);
}
}
// Binary Search Through Intervals
function searchIntervals(intervals, targetValue) {
let start = 0;
let end = intervals.length;
let mid = Math.floor((start + end)/2);
while(start < end) {
if(isWithinInterval(intervals[mid], targetValue)) return mid;
if(targetValue < intervals[mid][0]) {
end = mid;
} else {
start = mid + 1;
}
mid = Math.floor((start + end)/2)
}
return -1; //We want to guarantee target is within the interval range, so hopefully this won't be hit
}
function isWithinInterval(interval, num) {
return interval[0] <= num && num < interval[1];
}
function createIntervals(arr) {
let runningTotal = 0;
let result = arr.map(int => {
let newTotal = runningTotal + int;
let result = [runningTotal, newTotal]
runningTotal = newTotal;
return result;
});
return result;
}
let environment = new Environment(200, 14, 39, 50);
environment.run()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment