Add javascript/clever-dots
This commit is contained in:
commit
144e1a6907
|
@ -9,6 +9,7 @@ The experiments are currently organized based on the language they use:
|
||||||
- [Elm](./elm/)
|
- [Elm](./elm/)
|
||||||
- [F#](./fsharp/)
|
- [F#](./fsharp/)
|
||||||
- [Idris](./idris/)
|
- [Idris](./idris/)
|
||||||
|
- [Javascript](./javascript/)
|
||||||
- [Lean](./lean/)
|
- [Lean](./lean/)
|
||||||
- [Lua](./lua/)
|
- [Lua](./lua/)
|
||||||
- [Purescript](./purescript/)
|
- [Purescript](./purescript/)
|
||||||
|
|
5
javascript/README.md
Normal file
5
javascript/README.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
# Javascript
|
||||||
|
|
||||||
|
| Name | Description |
|
||||||
|
| ----------------------------- | -------------------------------------------------- |
|
||||||
|
| [clever-dots](./clever-dots/) | Half broken genetic algorithm implementation in js |
|
13
javascript/clever-dots/README.md
Normal file
13
javascript/clever-dots/README.md
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
# Clever-dots---genetic-algorithm-in-js
|
||||||
|
I first watched Coding Bullet's tutorial, and it was AWESOME. Then i tried this like every day. This is my first succesfully attemp, and my 4th attemp overall.
|
||||||
|
|
||||||
|
How-it-works:
|
||||||
|
Basically its just a simple genetic algorithm using tools as selection , cross over and mutation
|
||||||
|
You must wait a loooooooong time for it to train, so dont tell me that it doesnt work, until you dont actually see.
|
||||||
|
To know its progress, wait until you got 20 "thinking" mesages in the console.
|
||||||
|
I might add a progress bar another time
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Note:
|
||||||
|
I sctually understood how it works from a flappy bird AI project on Github, so you would probably see some similarities
|
61
javascript/clever-dots/game.js
Normal file
61
javascript/clever-dots/game.js
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
function game() {
|
||||||
|
this.size = [500, 500];
|
||||||
|
|
||||||
|
this.clear = function () {
|
||||||
|
var c = document.getElementById("can");
|
||||||
|
var ctx = c.getContext("2d");
|
||||||
|
ctx.fillStyle = "#000000";
|
||||||
|
ctx.fillRect(0, 0, 1000, 1000);
|
||||||
|
//console.log("clearing");
|
||||||
|
};
|
||||||
|
|
||||||
|
this.draw = function (x, y) {
|
||||||
|
var c = document.getElementById("can");
|
||||||
|
var ctx = c.getContext("2d");
|
||||||
|
ctx.fillStyle = "#FFFFFF";
|
||||||
|
ctx.fillRect(x, y, 1, 1);
|
||||||
|
//console.log(y+"drawing"+x);
|
||||||
|
//console.log("finished drawing");
|
||||||
|
};
|
||||||
|
}
|
||||||
|
var a = new game();
|
||||||
|
a.clear();
|
||||||
|
var b = new population(a);
|
||||||
|
b.reset();
|
||||||
|
b.create_population();
|
||||||
|
|
||||||
|
function redraw() {
|
||||||
|
let done = false;
|
||||||
|
let callback = null;
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
done = true;
|
||||||
|
if (callback !== null) callback();
|
||||||
|
});
|
||||||
|
|
||||||
|
return new Promise((res) => {
|
||||||
|
if (done) res();
|
||||||
|
else callback = () => res();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const iterationsPerFrame = 10;
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
for (let k = 0; k < 1000; k++) {
|
||||||
|
await redraw();
|
||||||
|
a.clear();
|
||||||
|
console.log(`thinking: ${k}`);
|
||||||
|
for (let i = 0; i < 500; i++) {
|
||||||
|
if (i % iterationsPerFrame === 0) await redraw();
|
||||||
|
for (let j = 0; j < b.Population.length; j++) {
|
||||||
|
b.think(b.Population[j]);
|
||||||
|
a.draw(b.Population[j].x, b.Population[j].y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
b.evolve();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
||||||
|
|
22
javascript/clever-dots/index.html
Normal file
22
javascript/clever-dots/index.html
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>
|
||||||
|
Genetic algorithm
|
||||||
|
</title>
|
||||||
|
|
||||||
|
<!-- jquery -->
|
||||||
|
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
|
||||||
|
|
||||||
|
<script src = "synaptic.min.js"></script>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<canvas id = "can" width = "1010" height = "1010" >
|
||||||
|
Canvas is not supported by your browser
|
||||||
|
</canvas>
|
||||||
|
|
||||||
|
<script src = "population.js"></script>
|
||||||
|
<script src = "game.js"></script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
197
javascript/clever-dots/population.js
Normal file
197
javascript/clever-dots/population.js
Normal file
|
@ -0,0 +1,197 @@
|
||||||
|
function population(game){
|
||||||
|
this.max = 50;
|
||||||
|
this.top = 4;
|
||||||
|
this.game = game;
|
||||||
|
|
||||||
|
if (this.max < this.top){
|
||||||
|
this.top = this.max;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.Population = [];
|
||||||
|
this.scale = 200;
|
||||||
|
|
||||||
|
this.reset = function(){
|
||||||
|
this.iteration = 1;
|
||||||
|
this.mutateRate = 1;
|
||||||
|
|
||||||
|
this.best_population = 0;
|
||||||
|
this.best_fitness = 0;
|
||||||
|
this.best_score = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.create_population = function(){
|
||||||
|
this.Population = [];
|
||||||
|
|
||||||
|
for (var i=0; i<this.max; i++){
|
||||||
|
var newUnit = new synaptic.Architect.Perceptron(5, 32, 4);
|
||||||
|
|
||||||
|
newUnit.index = i;
|
||||||
|
newUnit.fitness = 0;
|
||||||
|
newUnit.score = 0;
|
||||||
|
newUnit.isWinner = false;
|
||||||
|
newUnit.x = 250;
|
||||||
|
newUnit.y = 100;
|
||||||
|
//newUnit.round = 0;
|
||||||
|
|
||||||
|
this.Population.push(newUnit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.think = function(unit){
|
||||||
|
var inputs = [500,500,unit.x,unit.y,dist(unit.x,unit.y,500,500)];
|
||||||
|
var outputs = this.Population[unit.index].activate(inputs);
|
||||||
|
|
||||||
|
var max = outputs.indexOf((Math.max.apply( Math,outputs)));
|
||||||
|
//console.log(Math.max.apply( Math,outputs));
|
||||||
|
if (max == 0 && unit.y >= 0){
|
||||||
|
unit.y -= 1;
|
||||||
|
}
|
||||||
|
else if (max == 2 && unit.y <= 500){
|
||||||
|
unit.y += 1;
|
||||||
|
}
|
||||||
|
else if (max == 1 && unit.x <= 500){
|
||||||
|
unit.x += 1;
|
||||||
|
}
|
||||||
|
else if (max == 3 && unit.x >= 0){
|
||||||
|
unit.x -= 1;
|
||||||
|
}
|
||||||
|
//unit.round++;
|
||||||
|
//this.game.draw(unit.x,unit.y);
|
||||||
|
unit.fitness = dist(unit.x,unit.y,0,0);
|
||||||
|
//console.log(max+"evolving"+outputs);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.evolve = function(){
|
||||||
|
var Winners = this.select();
|
||||||
|
|
||||||
|
if (this.mutateRate == 1 && Winners[0].fitness < 0){
|
||||||
|
this.create_population();
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
this.mutateRate = 0.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
for (var i=this.top; i<this.max; i++){
|
||||||
|
var parentA, parentB, offspring;
|
||||||
|
|
||||||
|
if (i == this.top){
|
||||||
|
parentA = Winners[0].toJSON();
|
||||||
|
parentB = Winners[1].toJSON();
|
||||||
|
offspring = this.crossOver(parentA, parentB);
|
||||||
|
|
||||||
|
} else if (i < this.max-2){
|
||||||
|
parentA = this.getRandomUnit(Winners).toJSON();
|
||||||
|
parentB = this.getRandomUnit(Winners).toJSON();
|
||||||
|
offspring = this.crossOver(parentA, parentB);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
offspring = this.getRandomUnit(Winners).toJSON();
|
||||||
|
}
|
||||||
|
|
||||||
|
// mutate the offspring
|
||||||
|
offspring = this.mutation(offspring);
|
||||||
|
|
||||||
|
// create a new unit using the neural network from the offspring
|
||||||
|
var newUnit = synaptic.Network.fromJSON(offspring);
|
||||||
|
newUnit.index = this.Population[i].index;
|
||||||
|
newUnit.fitness = 0;
|
||||||
|
newUnit.score = 0;
|
||||||
|
newUnit.isWinner = false;
|
||||||
|
newUnit.x = 250;
|
||||||
|
newUnit.y = 100;
|
||||||
|
//newUnit.round = 0;
|
||||||
|
|
||||||
|
// update population by changing the old unit with the new one
|
||||||
|
this.Population[i] = newUnit;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.Population.sort(function(unitA, unitB){
|
||||||
|
return unitA.index - unitB.index;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.select = function(){
|
||||||
|
var sortedPopulation = this.Population.sort(
|
||||||
|
function(unitA, unitB){
|
||||||
|
return unitB.fitness - unitA.fitness;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
for (var i=0; i<this.top; i++){
|
||||||
|
this.Population[i].isWinner = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sortedPopulation.slice(0, this.top);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.crossOver = function(parentA, parentB){
|
||||||
|
var cutPoint = this.random(0, parentA.neurons.length-1);
|
||||||
|
|
||||||
|
for (var i = cutPoint; i < parentA.neurons.length; i++){
|
||||||
|
var biasFromParentA = parentA.neurons[i]['bias'];
|
||||||
|
parentA.neurons[i]['bias'] = parentB.neurons[i]['bias'];
|
||||||
|
parentB.neurons[i]['bias'] = biasFromParentA;
|
||||||
|
}
|
||||||
|
|
||||||
|
cutPoint = this.random(0, parentA.connections.length-1);
|
||||||
|
|
||||||
|
for (var i = cutPoint; i < parentA.connections.length; i++){
|
||||||
|
var weightFromParentA = parentA.connections[i]['weight'];
|
||||||
|
parentA.connections[i]['weight'] = parentB.connections[i]['weight'];
|
||||||
|
parentB.connections[i]['weight'] = weightFromParentA;
|
||||||
|
}
|
||||||
|
return this.random(0, 1) == 1 ? parentA : parentB;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.mutation = function(offspring){
|
||||||
|
for (var i = 0; i < offspring.neurons.length; i++){
|
||||||
|
offspring.neurons[i]['bias'] = this.mutate(offspring.neurons[i]['bias']);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < offspring.connections.length; i++){
|
||||||
|
offspring.connections[i]['weight'] = this.mutate(offspring.connections[i]['weight']);
|
||||||
|
}
|
||||||
|
|
||||||
|
return offspring;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.mutate = function(gene){
|
||||||
|
if (Math.random() < this.mutateRate) {
|
||||||
|
var mutateFactor = 1 + ((Math.random() - 0.5) * 3 + (Math.random() - 0.5));
|
||||||
|
gene *= mutateFactor;
|
||||||
|
}
|
||||||
|
|
||||||
|
return gene;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.random = function(min, max){
|
||||||
|
return Math.floor(Math.random()*(max-min+1) + min);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.getRandomUnit = function(array){
|
||||||
|
return array[this.random(0, array.length-1)];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function dist(x1,y1,x2,y2){
|
||||||
|
var x = x2 - x1;
|
||||||
|
var y = y2 - y1;
|
||||||
|
return Math.sqrt(Math.pow(x,2) + Math.pow(y,2));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
2848
javascript/clever-dots/synaptic.min.js
vendored
Normal file
2848
javascript/clever-dots/synaptic.min.js
vendored
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue