├── README.md ├── evolution.png ├── react-es6 ├── build │ ├── css │ │ └── style.css │ ├── index.html │ └── js │ │ └── bundle.js ├── gulp.config.js ├── gulpfile.js ├── package.json └── src │ ├── index.html │ ├── jsx │ ├── DNA.js │ ├── Population.js │ ├── Util.js │ ├── World.jsx │ └── index.jsx │ └── scss │ └── style.scss └── vanilla ├── DNA.js ├── Population.js ├── index.html └── sketch.js /README.md: -------------------------------------------------------------------------------- 1 | # React.js Genetic Algorithm Boilerplate 2 | 3 | ## What is a Genetic Algorithm? 4 | 5 | Genetic Algorithms apply principles from biological evolution to find solutions to problems with no human input. The solution to the problem automatically evolves from random and incorrect trials and it gets better over time until the goal is reached. 6 | 7 | ## Can I see some examples to help me understand this? 8 | Put all this in perspective and to get you excited have a look at a few inspiring examples where developers used genetic algorithms to search for unknown solutions to known problems: 9 | - https://www.youtube.com/watch?v=qv6UVOQ0F44 10 | - https://www.youtube.com/watch?v=bBt0imn77Zg 11 | - https://www.youtube.com/watch?v=Gl3EjiVlz_4 12 | - https://www.youtube.com/watch?v=uwz8JzrEwWY 13 | - https://www.youtube.com/watch?v=8vzTCC-jbwM 14 | - https://www.youtube.com/watch?v=pgaEE27nsQw 15 | 16 | ## What are the core principles this algorithm inherits from Nature and the Darwinian Evolution? 17 | In order for natural selection to work like in Nature, all three of the below has to happen: 18 | 19 | - **Heredity**: Creatures pass down their genetic information (DNA) to their offsprings to the next generation if they live long enough to be able to reproduce. 20 | 21 | - **Variation**: Variety must be present when heredity happens. If all creatures are exactly the same then with no variation the species can't evolve. Variation normally happens when the genetic information of the parents mix at reproduction and with the random mutation. 22 | 23 | - **Selection**: Successful members of the population become parents and pass down their genetic information, unsuccessful ones die without offsprings. This is what we normally refer to as “survival of the fittest.” Natural selection is what allows a species to evolve and each generation to be more successful than their parents. 24 | 25 | ![evolution](evolution.png) 26 | 27 | ## How can this be used in a computer algorithm to solve other, generic problems? 28 | 29 | You first have to start by defining your goal. **Remember that even though we use ideas from Nature, the goal of your algorithm can be ANYTHING.** The goal needs to be measurable and easily comparable to the performance and efficiency of each member of the population as members in the population represent the potential solution to the problem. 30 | 31 | In the first generation the population is filled with members (solutions) that are completely random. In this completely random pool there are always a few who, by pure luck and chance, perform ever so slightly better than the others. These are the "fittest" members. At the end of the genereation the fittest, best performing variations are selected to become parents for the new generation. 32 | 33 | Through many generations and rounds of natural selection solutions will become better and better. All this with no human input. 34 | 35 | ## What is the actual problem solved in the boilerplate? 36 | 37 | The goal of the example Genetic Algorith project found in this repository is to find the string: "Hello Web on Devices". 38 | 39 | Each member in the population of the first generation is a random string with the same length as the target string. This is essentially the DNA of the member that is going to be passed down from each parent. Remember that member = DNA, DNA = data representing the potential solution. 40 | 41 | There is also a compare function that calculates a fitness score for each member. This is essential to decide how close are they getting to the goal. In this case this just a function that counts the matching characters between the target string and the DNA string of the member. If the DNA has one matching character at the same location, then the score is 1, if it has 5 then the score is 5. This score will help us select the best performing solutions to become parents for the new generation. 42 | 43 | When two members reproduce to create an offsring they essentially combine their genetic information (DNA) which in this case means randomly combining the DNA string from each parent. Yes, this means that we may lose already matching characters, but overal, this mutation and variation help the solution evolve faster. 44 | 45 | After a couple of dozen or hundred generations members in the generation will come closer and closer to the goal and eventually one of them will actually reach it. 46 | 47 | ## How all this is implemented in React.js? 48 | 49 | There are two key classes to support the genetic algorithm: `DNA()` and `Population()`. Each member of the population is a new instance of the DNA class since that's their most defining property. The `DNA()` class provides methods like `crossOver`, `mutate` and `calcFitness` to support the reproduction mechanism. On the other hand the `Population()` class deals with higher level logic like natural selection, generating new populations and evaluating the fitness score of its members. 50 | 51 | The `` React component is the entry point of the whole algorithm. This is what you can plug in to your existing React.js application. There's only one state in this component which keeps track of the best solution so far. The constructor of the component exposes some settings for the algorithm: 52 | ``` 53 | // Simulation settings 54 | this.targetPhrase = 'Hello Web on Devices'; 55 | this.mutationRate = 0.01; 56 | this.populationSize = 300; 57 | ``` 58 | You can try tweaking this try to speed up the algorithm. 59 | 60 | This is also the place where the `Population()` class is initialised with the settings: 61 | ``` 62 | this.population = new Population(this.targetPhrase, this.mutationRate, this.populationSize); 63 | ``` 64 | 65 | When the component is mounted we call the `draw()` method which recursively calls itself using `requestAnimationFrame()`. The `draw()` method is where most of the algorithm happens: 66 | ``` 67 | draw() { 68 | 69 | // Generate weighed mating pool with the fittest members 70 | this.population.naturalSelection(); 71 | 72 | // Generate new population of children from parents in the mating pool 73 | this.population.generate(); 74 | 75 | // Calculate fitness score of the new population 76 | this.population.calcPopulationFitness(); 77 | 78 | // Find the fittest member of the population and see if target is reached 79 | this.population.evaluate(); 80 | 81 | // If target phrase is found, stop 82 | if (this.population.isFinished()) this.running = false; 83 | 84 | // Display best result so far 85 | this.setState({result: this.population.getBest()}); 86 | 87 | // Loop and start new generation 88 | if (this.running) window.requestAnimationFrame(this.draw); 89 | } 90 | ``` 91 | 92 | Finally, in the render function you can use the state to display the result. 93 | 94 | ## What else is this good for? 95 | 96 | The boilerplate is universal which means the algorithm can be used to work on any problems where you can: 97 | - Define the goal with a function that can calculate the fitness score of each member 98 | - Define the DNA of each member that drives their behavior towards the solution 99 | 100 | When you have these two defined then all you need to do is let the algorithm workout the logic. -------------------------------------------------------------------------------- /evolution.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webondevices/react-genetic-algorithm/95e966fbe1095baed2f5b5a443d5b00ab683380b/evolution.png -------------------------------------------------------------------------------- /react-es6/build/css/style.css: -------------------------------------------------------------------------------- 1 | body{font-family:sans-serif}.result{font-size:20px;width:300px;padding:25px;color:white;text-align:center;transition:background-color 300ms} 2 | -------------------------------------------------------------------------------- /react-es6/build/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | React testing 6 | 7 | 8 | 9 |
10 | 11 | 12 | -------------------------------------------------------------------------------- /react-es6/gulp.config.js: -------------------------------------------------------------------------------- 1 | module.exports = function() { 2 | var paths = { 3 | build: './build/', 4 | source: './src/' 5 | }; 6 | 7 | return paths; 8 | }; -------------------------------------------------------------------------------- /react-es6/gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require("gulp"); 2 | var browserify = require("browserify"); 3 | var source = require("vinyl-source-stream"); 4 | var sass = require("gulp-sass"); 5 | var connect = require("gulp-connect"); 6 | var config = require("./gulp.config.js")(); 7 | 8 | function copy(settings) { 9 | return gulp.src(settings.from) 10 | .pipe(gulp.dest(settings.to)) 11 | .pipe(connect.reload()); 12 | } 13 | 14 | gulp.task("browserify", function() { 15 | return browserify(config.source + "jsx/index.jsx") 16 | .transform("babelify", {presets: ['es2015', 'react']}) 17 | .bundle() 18 | .pipe(source('bundle.js')) 19 | .pipe(gulp.dest(config.build + "js/")) 20 | .pipe(connect.reload()); 21 | }); 22 | 23 | gulp.task("copy", function() { 24 | 25 | // Copy all HTML files 26 | copy({ 27 | from: config.source + "*.html", 28 | to: config.build 29 | }); 30 | 31 | // Copy all Images files 32 | copy({ 33 | from: config.source + "images/**/*", 34 | to: config.build + "images/" 35 | }); 36 | }); 37 | 38 | gulp.task("sass", function() { 39 | var options = { 40 | outputStyle: "compressed" 41 | } 42 | return gulp.src(config.source + "scss/style.scss") 43 | .pipe(sass(options)) 44 | .pipe(gulp.dest(config.build + "css/")) 45 | .pipe(connect.reload()); 46 | }); 47 | 48 | gulp.task("watch", ["sass", "copy", "browserify"], function() { 49 | gulp.watch(config.source + "scss/**/*", ["sass"]); 50 | gulp.watch([config.source + "images/**/*", ".src/*.html"], ["copy"]); 51 | gulp.watch(config.source + "jsx/**/*", ["browserify"]); 52 | }); 53 | 54 | gulp.task("connect", function() { 55 | connect.server({ 56 | root: "build", 57 | livereload: true 58 | }) 59 | }) 60 | 61 | gulp.task("default", ["connect", "watch"]); -------------------------------------------------------------------------------- /react-es6/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-start", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "Liran, Mate and Binu", 10 | "license": "ISC", 11 | "devDependencies": { 12 | "babel-preset-es2015": "^6.18.0", 13 | "babel-preset-react": "^6.16.0", 14 | "babelify": "^7.3.0", 15 | "browserify": "^13.1.1", 16 | "gulp": "^3.9.1", 17 | "gulp-connect": "^5.0.0", 18 | "gulp-sass": "^3.0.0", 19 | "react": "^15.4.1", 20 | "react-dom": "^15.4.1", 21 | "vinyl-source-stream": "^1.1.0" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /react-es6/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | React testing 6 | 7 | 8 | 9 |
10 | 11 | 12 | -------------------------------------------------------------------------------- /react-es6/src/jsx/DNA.js: -------------------------------------------------------------------------------- 1 | import util from './util.js'; 2 | 3 | class DNA { 4 | constructor(num){ 5 | 6 | // The genetic sequence 7 | this.genes = []; 8 | this.fitness = 0; 9 | 10 | // Random DNA generated from characters 11 | this.genes = Array(num).fill(null); 12 | this.genes = this.genes.map(() => util.newChar()); 13 | } 14 | 15 | // Converts character array to a String 16 | getPhrase() { 17 | return this.genes.join(''); 18 | } 19 | 20 | // Fitness function (returns floating point % of "correct" characters) 21 | calcFitness(target) { 22 | let score = 0; 23 | 24 | this.genes.forEach((gene, i) => { 25 | if (gene === target.charAt(i)) score += 1; 26 | }); 27 | 28 | this.fitness = score / target.length; 29 | } 30 | 31 | // Cross DNA with partner to produce child 32 | crossover(partner) { 33 | 34 | // Initialise new child 35 | const child = new DNA(this.genes.length); 36 | const midpoint = util.randomInt(0, this.genes.length - 1); 37 | 38 | // Cross DNA from two parents from each side of midpoint 39 | this.genes.forEach((gene, i) => { 40 | 41 | if (i > midpoint) { 42 | child.genes[i] = this.genes[i]; 43 | } else { 44 | child.genes[i] = partner.genes[i]; 45 | } 46 | }); 47 | 48 | return child; 49 | } 50 | 51 | // picks a new random character based on a mutation probability 52 | mutate(mutationRate) { 53 | this.genes.forEach((gene, i) => { 54 | 55 | if (Math.random(0, 1) < mutationRate) { 56 | this.genes[i] = util.newChar(); 57 | } 58 | }); 59 | } 60 | } 61 | 62 | export default DNA; -------------------------------------------------------------------------------- /react-es6/src/jsx/Population.js: -------------------------------------------------------------------------------- 1 | import DNA from './DNA.js'; 2 | import util from './util.js'; 3 | 4 | class Population { 5 | constructor(t, m, populationSize) { 6 | this.target = t; 7 | this.mutationRate = m; 8 | this.generations = 0; 9 | this.perfectScore = 1; 10 | this.finished = false; 11 | this.matingPool = []; 12 | this.best = ''; 13 | 14 | // Fill population with DNA instances 15 | this.population = Array(populationSize).fill(null); 16 | this.population = this.population.map(() => new DNA(this.target.length)); 17 | 18 | this.calcPopulationFitness(); 19 | } 20 | 21 | // Calculate fitness value for every member of the population 22 | calcPopulationFitness() { 23 | this.population.forEach(member => { 24 | member.calcFitness(this.target); 25 | }); 26 | } 27 | 28 | // Generate a weighed mating pool 29 | naturalSelection() { 30 | let maxFitness = 0; 31 | 32 | this.matingPool = []; 33 | 34 | // Find the highest fitness value in the population 35 | this.population.forEach(member => { 36 | maxFitness = member.fitness > maxFitness ? member.fitness : maxFitness; 37 | }); 38 | 39 | // Based on fitness, each member is added to the mating pool a weighed number of times 40 | // higher fitness = more instance in pool = more likely to be picked as a parent 41 | // lower fitness = less instance in pool = less likely to be picked as a parent 42 | this.population.forEach(member => { 43 | const fitness = util.map(member.fitness, 0, maxFitness, 0, 1); 44 | 45 | // Arbitrary multiplier 46 | let n = Math.floor(fitness * 50); 47 | for ( ; n >= 0; n--) { 48 | this.matingPool.push(member); 49 | } 50 | }); 51 | } 52 | 53 | // Create a new generation 54 | generate() { 55 | 56 | this.population.forEach((member, i) => { 57 | 58 | // Random index for the pool 59 | const a = util.randomInt(0, this.matingPool.length - 1); 60 | const b = util.randomInt(0, this.matingPool.length - 1); 61 | 62 | // Picking a random item from the pool 63 | const partnerA = this.matingPool[a]; 64 | const partnerB = this.matingPool[b]; 65 | 66 | // Generating a child with DNA crossover 67 | const child = partnerA.crossover(partnerB); 68 | 69 | // Mutate DNA for diversity 70 | child.mutate(this.mutationRate); 71 | 72 | // Add child to the population 73 | this.population[i] = child; 74 | 75 | }); 76 | 77 | this.generations += 1; 78 | } 79 | 80 | 81 | getBest() { 82 | return this.best; 83 | } 84 | 85 | evaluate() { 86 | let worldrecord = 0.0; 87 | let index = 0; 88 | 89 | // Find the fittest member of the population 90 | this.population.forEach((member, i) => { 91 | if (member.fitness > worldrecord) { 92 | index = i; 93 | worldrecord = member.fitness; 94 | } 95 | }); 96 | 97 | // Get best result so far 98 | this.best = this.population[index].getPhrase(); 99 | 100 | // Stop simulation if found result 101 | if (worldrecord === this.perfectScore) this.finished = true; 102 | } 103 | 104 | isFinished() { 105 | return this.finished; 106 | } 107 | 108 | getGenerations() { 109 | return this.generations; 110 | } 111 | 112 | // Get average fitness for the population 113 | getAverageFitness() { 114 | let total = 0; 115 | 116 | this.population.forEach(member => { 117 | total += member.fitness; 118 | }); 119 | 120 | return total / this.population.length; 121 | } 122 | } 123 | 124 | export default Population; -------------------------------------------------------------------------------- /react-es6/src/jsx/Util.js: -------------------------------------------------------------------------------- 1 | const util = { 2 | map: function(value, low1, high1, low2, high2) { 3 | return low2 + (high2 - low2) * (value - low1) / (high1 - low1); 4 | }, 5 | 6 | randomInt: function(min, max) { 7 | return Math.floor(Math.random() * (max - min + 1) + min); 8 | }, 9 | 10 | newChar: function(){ 11 | var c = this.randomInt(63, 122 - 1); 12 | 13 | if (c === 63) c = 32; 14 | if (c === 64) c = 46; 15 | 16 | return String.fromCharCode(c); 17 | } 18 | } 19 | 20 | export default util; -------------------------------------------------------------------------------- /react-es6/src/jsx/World.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Population from './Population.js'; 3 | 4 | class World extends React.Component { 5 | 6 | constructor() { 7 | super(); 8 | 9 | this.state = { 10 | result: '' 11 | }; 12 | 13 | // Simulation settings 14 | this.targetPhrase = 'Hello Web on Devices'; 15 | this.mutationRate = 0.01; 16 | this.populationSize = 300; 17 | 18 | this.running = true; 19 | 20 | // Initialise population 21 | this.population = new Population(this.targetPhrase, this.mutationRate, this.populationSize); 22 | 23 | this.draw = this.draw.bind(this); 24 | } 25 | 26 | componentDidMount(){ 27 | 28 | // Start simulation 29 | this.draw(); 30 | } 31 | 32 | draw() { 33 | 34 | // Generate weighed mating pool with the fittest members 35 | this.population.naturalSelection(); 36 | 37 | // Generate new population of children from parents in the mating pool 38 | this.population.generate(); 39 | 40 | // Calculate fitness score of the new population 41 | this.population.calcPopulationFitness(); 42 | 43 | // Find the fittest member of the population and see if target is reached 44 | this.population.evaluate(); 45 | 46 | // If target phrase is found, stop 47 | if (this.population.isFinished()) this.running = false; 48 | 49 | // Display best result so far 50 | this.setState({result: this.population.getBest()}); 51 | 52 | // Loop and start new generation 53 | if (this.running) window.requestAnimationFrame(this.draw); 54 | } 55 | 56 | render() { 57 | const myStyle = this.running ? {backgroundColor: 'red'} : {backgroundColor: 'green'}; 58 | 59 | return ( 60 |
61 | { this.state.result } 62 |
63 | ); 64 | } 65 | } 66 | 67 | export default World; -------------------------------------------------------------------------------- /react-es6/src/jsx/index.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import World from './World.jsx'; 4 | 5 | ReactDOM.render(, document.getElementById('react-app')); -------------------------------------------------------------------------------- /react-es6/src/scss/style.scss: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: sans-serif; 3 | } 4 | 5 | .result { 6 | font-size: 20px; 7 | width: 300px; 8 | padding: 25px; 9 | color: white; 10 | text-align: center; 11 | transition: background-color 300ms; 12 | } -------------------------------------------------------------------------------- /vanilla/DNA.js: -------------------------------------------------------------------------------- 1 | // DNA constructor, generates random DNA 2 | function DNA(num) { 3 | 4 | // The genetic sequence 5 | this.genes = []; 6 | this.fitness = 0; 7 | 8 | // Random DNA generated from characters 9 | for (var i = 0; i < num; i++) { 10 | this.genes[i] = newChar(); 11 | } 12 | 13 | // Converts character array to a String 14 | this.getPhrase = function() { 15 | return this.genes.join(""); 16 | } 17 | 18 | // Fitness function (returns floating point % of "correct" characters) 19 | this.calcFitness = function(target) { 20 | var score = 0; 21 | 22 | for (var i = 0; i < this.genes.length; i++) { 23 | 24 | if (this.genes[i] == target.charAt(i)) { 25 | score++; 26 | } 27 | } 28 | 29 | this.fitness = score / target.length; 30 | } 31 | 32 | // Cross to members 33 | this.crossover = function(partner) { 34 | 35 | // A new child 36 | var child = new DNA(this.genes.length); 37 | var midpoint = randomInt(0,this.genes.length - 1); 38 | 39 | // Half from one, half from the other 40 | for (var i = 0; i < this.genes.length; i++) { 41 | 42 | if (i > midpoint) { 43 | child.genes[i] = this.genes[i]; 44 | } else { 45 | child.genes[i] = partner.genes[i]; 46 | } 47 | } 48 | 49 | return child; 50 | } 51 | 52 | // picks a new random character based on a mutation probability 53 | this.mutate = function(mutationRate) { 54 | 55 | for (var i = 0; i < this.genes.length; i++) { 56 | 57 | if (Math.random(0, 1) < mutationRate) { 58 | this.genes[i] = newChar(); 59 | } 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /vanilla/Population.js: -------------------------------------------------------------------------------- 1 | function Population(p, m, num) { 2 | this.population; // Array to hold the current population 3 | this.matingPool; // ArrayList which we will use for our "mating pool" 4 | this.generations = 0; // Number of generations 5 | this.finished = false; // Are we finished evolving? 6 | this.target = p; // Target phrase 7 | this.mutationRate = m; // Mutation rate 8 | this.perfectScore = 1; 9 | this.best = ''; 10 | this.population = []; 11 | 12 | for (var i = 0; i < num; i++) { 13 | this.population[i] = new DNA(this.target.length); 14 | } 15 | 16 | this.matingPool = []; 17 | 18 | // Fill our fitness array with a value for every member of the population 19 | this.calcFitness = function() { 20 | for (var i = 0; i < this.population.length; i++) { 21 | this.population[i].calcFitness(target); 22 | } 23 | } 24 | 25 | this.calcFitness(); 26 | 27 | // Generate a mating pool 28 | this.naturalSelection = function() { 29 | var maxFitness = 0; 30 | 31 | // Clear the ArrayList 32 | this.matingPool = []; 33 | 34 | for (var i = 0; i < this.population.length; i++) { 35 | if (this.population[i].fitness > maxFitness) { 36 | maxFitness = this.population[i].fitness; 37 | } 38 | } 39 | 40 | // Based on fitness, each member will get added to the mating pool a certain number of times 41 | // a higher fitness = more entries to mating pool = more likely to be picked as a parent 42 | // a lower fitness = fewer entries to mating pool = less likely to be picked as a parent 43 | for (var i = 0; i < this.population.length; i++) { 44 | var fitness = map(this.population[i].fitness,0,maxFitness,0,1); 45 | 46 | // Arbitrary multiplier, could also use monte carlo method 47 | var n = Math.floor(fitness * 100); 48 | 49 | for (var j = 0; j < n; j++) { 50 | this.matingPool.push(this.population[i]); 51 | } 52 | } 53 | } 54 | 55 | // Create a new generation 56 | this.generate = function() { 57 | 58 | // Refill the population with children from the mating pool 59 | for (var i = 0; i < this.population.length; i++) { 60 | 61 | // Getting a random index for the pool 62 | var a = randomInt(0, this.matingPool.length - 1); 63 | var b = randomInt(0, this.matingPool.length - 1); 64 | 65 | // Picking a random item from the pool 66 | var partnerA = this.matingPool[a]; 67 | var partnerB = this.matingPool[b]; 68 | 69 | // Generating an offspring 70 | var child = partnerA.crossover(partnerB); 71 | 72 | // Mutate DNA 73 | child.mutate(this.mutationRate); 74 | 75 | // Add child to the population 76 | this.population[i] = child; 77 | } 78 | 79 | this.generations++; 80 | } 81 | 82 | 83 | this.getBest = function() { 84 | return this.best; 85 | } 86 | 87 | // Compute the current "most fit" member of the population 88 | this.evaluate = function() { 89 | var worldrecord = 0.0; 90 | var index = 0; 91 | 92 | for (var i = 0; i < this.population.length; i++) { 93 | 94 | if (this.population[i].fitness > worldrecord) { 95 | index = i; 96 | worldrecord = this.population[i].fitness; 97 | } 98 | } 99 | 100 | this.best = this.population[index].getPhrase(); 101 | 102 | if (worldrecord === this.perfectScore) { 103 | this.finished = true; 104 | } 105 | } 106 | 107 | this.isFinished = function() { 108 | return this.finished; 109 | } 110 | 111 | this.getGenerations = function() { 112 | return this.generations; 113 | } 114 | 115 | // Compute average fitness for the population 116 | this.getAverageFitness = function() { 117 | var total = 0; 118 | 119 | for (var i = 0; i < this.population.length; i++) { 120 | total += this.population[i].fitness; 121 | } 122 | 123 | return total / (this.population.length); 124 | } 125 | 126 | this.allPhrases = function() { 127 | var everything = ""; 128 | var displayLimit = Math.min(this.population.length,50); 129 | 130 | for (var i = 0; i < displayLimit; i++) { 131 | everything += this.population[i].getPhrase() + "
"; 132 | } 133 | 134 | return everything; 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /vanilla/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |

11 | 12 | -------------------------------------------------------------------------------- /vanilla/sketch.js: -------------------------------------------------------------------------------- 1 | var target; 2 | var popmax; 3 | var mutationRate; 4 | var population; 5 | 6 | var running = true; 7 | 8 | function setup() { 9 | target = 'Hello Web on Devices'; 10 | mutationRate = 0.01; 11 | popmax = 300; 12 | 13 | // Create a population with a target phrase, mutation rate, and population max 14 | population = new Population(target, mutationRate, popmax); 15 | 16 | // Initialise first generation 17 | draw(); 18 | } 19 | 20 | function draw() { 21 | 22 | // Generate weighed mating pool with the fittest members 23 | population.naturalSelection(); 24 | 25 | // Generate new population of offsprings from parents in the mating pool 26 | population.generate(); 27 | 28 | // Calculate fitness of the new population 29 | population.calcFitness(); 30 | 31 | // Find the fittest member of the population and see if target is reached 32 | population.evaluate(); 33 | 34 | // If target phrase is found, stop 35 | if (population.isFinished()) running = false; 36 | 37 | // Display best result so far 38 | displayInfo(); 39 | 40 | // Loop and start new generation 41 | if (running) window.requestAnimationFrame(draw); 42 | } 43 | 44 | function displayInfo() { 45 | var answer = population.getBest(); 46 | var element = document.getElementById('result'); 47 | 48 | element.innerHTML = answer; 49 | } 50 | 51 | function map(value, low1, high1, low2, high2) { 52 | return low2 + (high2 - low2) * (value - low1) / (high1 - low1); 53 | } 54 | 55 | function randomInt(min, max) { 56 | return Math.floor( Math.random() * (max - min + 1) + min ); 57 | } 58 | 59 | function newChar() { 60 | var c = randomInt(63, 122 - 1); 61 | 62 | if (c === 63) c = 32; 63 | if (c === 64) c = 46; 64 | 65 | return String.fromCharCode(c); 66 | } 67 | 68 | window.onload = setup; 69 | --------------------------------------------------------------------------------