1
Fork 0

Add javascript/clever-dots

This commit is contained in:
prescientmoon 2024-03-04 16:51:26 +01:00
commit 144e1a6907
Signed by: prescientmoon
SSH key fingerprint: SHA256:UUF9JT2s8Xfyv76b8ZuVL7XrmimH4o49p4b+iexbVH4
7 changed files with 3147 additions and 0 deletions

View file

@ -9,6 +9,7 @@ The experiments are currently organized based on the language they use:
- [Elm](./elm/)
- [F#](./fsharp/)
- [Idris](./idris/)
- [Javascript](./javascript/)
- [Lean](./lean/)
- [Lua](./lua/)
- [Purescript](./purescript/)

5
javascript/README.md Normal file
View file

@ -0,0 +1,5 @@
# Javascript
| Name | Description |
| ----------------------------- | -------------------------------------------------- |
| [clever-dots](./clever-dots/) | Half broken genetic algorithm implementation in js |

View 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

View 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();

View 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>

View 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

File diff suppressed because it is too large Load diff