Advertisement
Guest User

Untitled

a guest
Mar 30th, 2020
119
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. var canvas = document.getElementById("myCanvas");
  2. var ctx = canvas.getContext("2d");
  3.  
  4. const gravity = 21;
  5. const delta = 0.016;
  6. const opening = 100;
  7. const minOpen = 25;
  8. const divisions = 10;
  9. const nColums = divisions / 2 - 1;
  10. const offset = 300;
  11. const tubesVelocity = 1.5;
  12. const minOpenSosia = 50;
  13. const populationSize = 200;
  14. var score = 0;
  15. var visualScore = 0;
  16. var generation = 0;
  17. var bestScore = 0;
  18.  
  19. canvas.width = window.innerWidth -50;
  20. canvas.height = 300;
  21.  
  22. class Matrix{
  23.  
  24.     constructor(rows, columns){
  25.         this.rows = rows;
  26.         this.columns = columns;
  27.  
  28.         this.data = [];
  29.  
  30.         for (let row = 0; row < this.rows; row++){
  31.             this.data.push([]);
  32.         }
  33.  
  34.         //this.randomize();
  35.     }
  36.  
  37.     randomize(){
  38.         for (let row = 0; row < this.rows; row++){
  39.             for (let column = 0; column < this.columns; column++){
  40.                 this.data[row][column] = Math.random() * 2 - 1;
  41.             }
  42.         }
  43.     }
  44.  
  45.     static multiply(a, b){
  46.         if(a.columns != b.rows)
  47.             return;
  48.         let prodotto = new Matrix(a.rows, b.columns);
  49.         for (let i = 0; i < prodotto.rows; i++){
  50.             for (let y = 0; y < prodotto.columns; y++){
  51.                 let somma = 0;
  52.                 for (let z = 0; z < b.rows; z++){
  53.                     somma += a.data[i][z] * b.data[z][y];
  54.                 }
  55.                 prodotto.data[i][y] = somma;
  56.             }
  57.         }
  58.         return prodotto;
  59.     }
  60.  
  61.     static convertArray(array){
  62.         let risultato = new Matrix(array.length, 1);
  63.         for (let row = 0; row < risultato.rows; row++){
  64.             risultato.data[row][0] = array[row];
  65.         }
  66.         return risultato;
  67.     }
  68.  
  69.  
  70.     map(funzione){
  71.         for (let row = 0; row < this.rows; row++){
  72.             for (let column = 0; column < this.columns; column++){
  73.                 this.data[row][column] = funzione(this.data[row][column]);
  74.             }
  75.         }
  76.     }
  77.  
  78.     add(n){
  79.         for (let row = 0; row < this.rows; row++){
  80.             for (let column = 0; column < this.columns; column++){
  81.                 this.data[row][column] += n.data[row][column];
  82.             }
  83.         }  
  84.     }
  85.  
  86.     mutate(rate){
  87.         for (let row = 0; row < this.rows; row++){
  88.             for (let column = 0; column < this.columns; column++){
  89.                 if(Math.random() < rate){
  90.                     this.data[row][column] = (Math.random() * 2) - 1;
  91.                 }
  92.             }
  93.         }
  94.     }
  95.  
  96.     stampa(){
  97.         console.log(this);
  98.         console.table(this.data);
  99.     }
  100. }
  101. function sigmoid(n){
  102.     return 1 / (1 + Math.exp(-n));
  103. }
  104.  
  105. class NeuralNetwork{
  106.     //TODO: I BIAS
  107.     constructor(numeroNodiInput, numeroNodiHidden, numeroNodiOutput){
  108.        
  109.         this.numeroNodiInput = numeroNodiInput;
  110.         this.numeroNodiHidden = numeroNodiHidden;
  111.         this.numeroNodiOutput = numeroNodiOutput;
  112.  
  113.         this.pesiInputHidden = new Matrix(numeroNodiHidden, numeroNodiInput);
  114.         this.pesiInputHidden.randomize();
  115.         this.weightBiasIH = new Matrix(numeroNodiHidden, 1);
  116.         this.weightBiasIH.randomize();
  117.  
  118.         this.pesiHiddenOutput = new Matrix(numeroNodiOutput, numeroNodiHidden);
  119.         this.pesiHiddenOutput.randomize();
  120.         this.weightBiasHO = new Matrix(numeroNodiOutput, 1);
  121.         this.weightBiasHO.randomize();
  122.    
  123.     }
  124.  
  125.  
  126.     //feed forward
  127.     predict(inputs){
  128.         inputs = Matrix.convertArray(inputs);
  129.  
  130.         let nodiHidden = Matrix.multiply(this.pesiInputHidden, inputs);
  131.         nodiHidden.add(this.weightBiasIH);
  132.         nodiHidden.map(sigmoid);
  133.  
  134.         let nodiOutput = Matrix.multiply(this.pesiHiddenOutput, nodiHidden);
  135.         nodiOutput.add(this.weightBiasHO);
  136.         nodiOutput.map(sigmoid);
  137.    
  138.         return nodiOutput;
  139.     }
  140.  
  141.     mutate(rate){
  142.         this.pesiInputHidden.mutate(rate);
  143.         this.pesiHiddenOutput.mutate(rate);
  144.     }
  145.  
  146. }
  147.  
  148.  
  149. class Tube{
  150.     constructor(x, y, width, height){
  151.         this.x = x;
  152.         this.y = y;
  153.         this.width = width / 1.5;
  154.         this.height = height;
  155.     }
  156.  
  157.     update(){
  158.         this.x -= tubesVelocity;
  159.         if(this.x < - canvas.width / divisions){
  160.             this.x = canvas.width + (canvas.width / divisions) * (divisions / 2);
  161.             this.height = Math.floor(Math.random() * (canvas.height - opening - minOpen - minOpenSosia)) + minOpen;
  162.         }
  163.     }      
  164.  
  165.     render(){
  166.         ctx.fillStyle = "lightgreen";
  167.         ctx.fillRect(this.x, this.y, this.width, this.height);
  168.         ctx.fillRect(this.x, this.height + opening, this.width, canvas.height);
  169.         ctx.lineWidth = 3;
  170.         ctx.strokeStyle = "black";
  171.         ctx.strokeRect(this.x, this.y, this.width, this.height);
  172.         ctx.strokeRect(this.x, this.height + opening, this.width, canvas.height);
  173.     }
  174.  
  175. }
  176. class Tubes{
  177.     constructor(){
  178.         this.init();
  179.     }
  180.  
  181.     init(){
  182.         this.tubes = [];
  183.         for(let i = 0; i < nColums; i++){
  184.             this.tubes.push(new Tube(canvas.width / divisions * (i * (divisions / 2 - 1)) + offset, 0, canvas.width / divisions, Math.floor(Math.random() * (canvas.height - opening - minOpen - minOpenSosia)) + minOpen));
  185.         }
  186.     }
  187.  
  188.     update(){
  189.         for(let i = 0; i < this.tubes.length; i++){
  190.             this.tubes[i].update();
  191.         }
  192.     }
  193.  
  194.     render(){
  195.         for(let i = 0; i < this.tubes.length; i++){
  196.             this.tubes[i].render();
  197.         }
  198.     }
  199. }
  200.  
  201. class Bird{
  202.     constructor(brain = undefined){
  203.         this.fitness = 0;
  204.         this.x = canvas.width / 5;
  205.         this.y = canvas.height / 2;
  206.         this.radius = 7;
  207.         this.isJumping = false;
  208.         this.velocity = 0;
  209.         this.nearestTube = 0;
  210.         this.isDead = false;
  211.         this.score = 0;
  212.         this.gravity = gravity;
  213.         if(brain){
  214.             this.brain = brain;
  215.         }else{
  216.             this.brain = new NeuralNetwork(4, 8, 2);
  217.         }
  218.     }
  219.  
  220.     update(tubes){
  221.         if(this.isDead)
  222.             return;
  223.         //al massimo togli se rallenta//
  224.         let tube = tubes.tubes[this.findFirstTube(tubes)];
  225.         let output = this.brain.predict([tube.x / canvas.width, tube.height / canvas.height, (tube.height + opening) / canvas.height, this.y / canvas.height]);
  226.         if(output.data[0][0] > 0.5){
  227.             this.velocity -= 11;
  228.         }
  229.         this.gravity += output.data[1][0];
  230.         this.velocity += this.gravity * delta;
  231.  
  232.         //clamp value of velocity  
  233.         if(this.velocity > 5){
  234.             this.velocity = 5;
  235.         }else if(this.velocity < -8){
  236.             this.velocity = -8;
  237.         }
  238.  
  239.         this.y += this.velocity;
  240.  
  241.         //collisioni
  242.         for(let i = 0; i < tubes.tubes.length; i++){
  243.             let tube = tubes.tubes[i];
  244.             if((this.x + this.radius > tube.x && this.x - this.radius < tube.x + tube.width) && !(this.y - this.radius > tube.height && this.y + this.radius < tube.height + opening) || (this.y + this.radius > canvas.height) || (this.y - this.radius < 0)){
  245.                 //this.y = canvas.height / 2;
  246.                 this.nearestTube = 0;
  247.                 this.isDead = true;
  248.                 this.score = score;
  249.             }
  250.         }  
  251.     }
  252.  
  253.     findFirstTube(tubes){
  254.         if(this.x - this.radius > tubes.tubes[this.nearestTube].x + tubes.tubes[this.nearestTube].width){
  255.             this.nearestTube++;
  256.             visualScore++;
  257.         }
  258.         this.nearestTube %= tubes.tubes.length;
  259.         return this.nearestTube;
  260.     }
  261.  
  262.     render(){
  263.         if(this.isDead)
  264.             return;
  265.         ctx.beginPath();
  266.         ctx.lineWidth = 1;
  267.         ctx.fillStyle = "rgba(255, 0, 0, 0.5)";
  268.         ctx.strokeStyle = "black";
  269.         ctx.arc(this.x, this.y, this.radius, 0, 2 * Math.PI);
  270.         ctx.stroke();
  271.         ctx.fill();
  272.         ctx.closePath();
  273.     }
  274.  
  275.     calculateFitness(totalScore){
  276.         this.fitness = this.score / totalScore;
  277.         return this.score / totalScore;
  278.     }
  279.  
  280.     mutate(rate){
  281.         this.brain.mutate(rate);
  282.     }
  283. }
  284. function random(min, max){
  285.     return (Math.random() * (max - min)) + min;
  286. }
  287.  
  288.  
  289. class Population{
  290.     constructor(size){
  291.         this.size = size;
  292.         this.mutationRate = 0.01;
  293.         this.birds = [];
  294.         for(let i = 0; i < this.size; i++){
  295.             this.birds.push(new Bird());
  296.         }
  297.     }
  298.  
  299.     update(tubes){
  300.         for(let i = 0; i < this.birds.length; i++){
  301.             this.birds[i].update(tubes);
  302.         }
  303.  
  304.         if(this.isAllDead()){
  305.             if(visualScore > bestScore)
  306.                 bestScore = visualScore;
  307.  
  308.             let totalFitness = 0;
  309.             let totalScore = this.calculateTotalScore();
  310.             //calculate fitness
  311.             for(let i = 0; i < this.birds.length; i++){
  312.                 totalFitness += this.birds[i].calculateFitness(totalScore);
  313.             }
  314.            
  315.             let newBirds = [];
  316.             for(let i = 0; i < this.size; i++){
  317.                 let mother = this.pickOne(this.birds);
  318.                 let father = this.pickOne(this.birds);
  319.                 let child = this.crossover(mother.brain, father.brain);
  320.                 //slider.value = 0;
  321.                 child.mutate(this.mutationRate);
  322.                 newBirds.push(child);
  323.             }
  324.             this.birds = newBirds;
  325.             tubes.init();
  326.             score = 0;
  327.             visualScore = 0;
  328.             generation++;
  329.             generationCounter.innerHTML = "Generation: " + generation;
  330.             bestScoreCounter.innerHTML = "Best Score : " + bestScore;
  331.         }else{
  332.             score += 1;
  333.         }
  334.  
  335.     }
  336.  
  337.     render(){
  338.         for(let i = 0; i < this.birds.length; i++){
  339.             this.birds[i].render();
  340.         }
  341.     }
  342.  
  343.     isAllDead(){
  344.         let counter = 0;
  345.         for(let i = 0; i < this.birds.length; i++){
  346.             if(this.birds[i].isDead){
  347.                 counter++;
  348.             }
  349.         }
  350.         if(counter === this.birds.length)
  351.             return true;
  352.         return false;
  353.     }
  354.  
  355.     crossover(motherBrain, fatherBrain){
  356.         let childBrain = new NeuralNetwork(4, 8, 2);
  357.  
  358.         for(let i = 0; i < motherBrain.pesiInputHidden.rows; i++){
  359.             for(let j = 0; j < motherBrain.pesiInputHidden.columns; j++){
  360.                 if(random(0, 100) > 50){
  361.                     childBrain.pesiInputHidden.data[i][j] = fatherBrain.pesiInputHidden.data[i][j];
  362.                 }else{
  363.                     childBrain.pesiInputHidden.data[i][j] = motherBrain.pesiInputHidden.data[i][j];
  364.                 }
  365.             }
  366.         }
  367.  
  368.         for(let i = 0; i < motherBrain.pesiHiddenOutput.rows; i++){
  369.             for(let j = 0; j < motherBrain.pesiHiddenOutput.columns; j++){
  370.                 if(random(0, 100) > 50){
  371.                     childBrain.pesiHiddenOutput.data[i][j] = fatherBrain.pesiHiddenOutput.data[i][j];
  372.                 }else{
  373.                     childBrain.pesiHiddenOutput.data[i][j] = motherBrain.pesiHiddenOutput.data[i][j];
  374.                 }
  375.             }
  376.         }
  377.  
  378.         let child = new Bird(childBrain);
  379.  
  380.         return child;
  381.     }
  382.  
  383.     calculateTotalScore(){
  384.         let score = 0;
  385.         for(let i = 0; i < this.birds.length; i++){
  386.             score += this.birds[i].score;
  387.         }
  388.         return score;
  389.     }
  390.  
  391.     pickOne(birds){
  392.         let index = 0;
  393.         let r = Math.random();
  394.  
  395.         while(r > 0){
  396.             r = r - birds[index].fitness;
  397.             index++;
  398.         }
  399.         index--;
  400.         return birds[index];
  401.     }
  402. }
  403.  
  404. var tubes;
  405. var population;
  406.  
  407. var slider = document.getElementById("myRange");
  408. var generationCounter = document.getElementById("generation");
  409. var bestScoreCounter = document.getElementById("bestScore");
  410.  
  411.  
  412. function init(){
  413.     tubes = new Tubes();
  414.     population = new Population(populationSize);
  415. }
  416.  
  417. function update(){
  418.     population.update(tubes);
  419.     tubes.update();
  420. }
  421.  
  422. function render(){
  423.     ctx.beginPath();
  424.     ctx.fillStyle = "lightblue";
  425.     ctx.fillRect(0, 0, canvas.width, canvas.height);
  426.  
  427.     tubes.render();
  428.     population.render();
  429.  
  430.     ctx.font = "25px Arial";
  431.     ctx.fillStyle = "black";
  432.     ctx.fillText("Score: " + visualScore , 10, 25);
  433.     ctx.closePath();
  434. }
  435.  
  436. function gameLoop(){
  437.     for(let i = 0; i < slider.value; i++){
  438.         update();
  439.     }
  440.     render();
  441.     window.requestAnimationFrame(gameLoop);
  442. }
  443.  
  444. init();
  445. window.requestAnimationFrame(gameLoop);
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement