Skip to content

Instantly share code, notes, and snippets.

@davidsaccavino
Created April 10, 2021 22:56
Show Gist options
  • Save davidsaccavino/46f6d2e7d24c75c200e2de82fccd23b7 to your computer and use it in GitHub Desktop.
Save davidsaccavino/46f6d2e7d24c75c200e2de82fccd23b7 to your computer and use it in GitHub Desktop.
function random(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
// The maximum is exclusive and the minimum is inclusive
return Math.floor(Math.random() * (max - min)) + min;
}
function generateLetter() {
const code = random(97, 123); // ASCII char codes
return String.fromCharCode(code);
}
class Member {
constructor(goal){
this.goal = goal;
this.keys = [];
for(let i = 0; i < goal.length; i+=1){
this.keys[i] = generateLetter();
}
}
fitness(){
let match = 0;
// finds the number of matches in comparison to the goal
for(let i = 0; i < this.keys.length; i += 1){
if(this.keys[i] == this.goal[i]){
match += 1;
}
}
return match / this.goal.length;
}
crossover(partner){
const { length } = this.goal;
const child = new Member(this.goal);
const midpoint = random(0, length);
// this randomly selects phenotypes to create a child
// by randomly selecting a midpoint
for(let i = 0; i < length; i+= 1){
if(i > midpoint){
child.keys[i] = this.keys[i];
}
else
{
child.keys[i] = partner.keys[i];
}
}
return child;
}
mutate(mutationRate){
for(let i = 0; i < this.keys.length; i += 1) {
// if below prefedine mutation rate
// generate a new random letter on this position
if(Math.random() < mutationRate){
this.keys[i] = generateLetter();
}
}
}
}
class Population {
constructor(size, goal, mutationRate){
size = size || 1;
this.members = [];
this.mutationRate = mutationRate
for(let i=0; i < size; i+=1){
this.members.push(new Member(goal));
}
}
_naturalSelection() {
let matingPool = [];
this.members.forEach( (m) => {
// The more fit the mate, the more times it shows up in the mating pool
// making more fit mates more likely to pass on their genetics
// if fitness == 0, add just one member
const f = Math.floor(m.fitness() * 100 || 1);
// adds the mate once to the matingPool for each fitness
for(let i = 0; i < f; i +=1){
matingPool.push(m);
}
});
return matingPool;
}
_reproduce(matingPool){
for(let i = 0; i < this.members.length; i+= 1){
// pick 2 random members/parents from the mating pool
const parentA = matingPool[random(0, matingPool.length)];
const parentB = matingPool[random(0, matingPool.length)];
// perform crossover
const child = parentA.crossover(parentB);
// perform mutation
child.mutate(this.mutationRate);
this.members[i] = child;
}
}
evolve(generations){
for(let i = 0; i < generations; i += 1){
const pool = this._naturalSelection();
this._reproduce(pool);
}
}
}
function generate(populationSize, goal, mutationRate, generations) {
// Create a population and evlove for N generations
const population = new Population(populationSize, goal, mutationRate);
population.evolve(generations);
// Get the typed words from all members, and find if someone was able to type the target
const membersKeys = population.members.map((m) => m.keys.join(''));
const perfectCandidatesNum = membersKeys.filter((w) => w === goal);
// Print the results
console.log(membersKeys);
console.log(`${perfectCandidatesNum ? perfectCandidatesNum.length : 0} member(s) typed "${goal}`);
}
generate(250, 'helloworld', 0.025, 50);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment