Created
September 27, 2018 02:28
-
-
Save spiterman/37abd4602005360f31114f9dde2465c0 to your computer and use it in GitHub Desktop.
Genetic Algorithm v.2.0
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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