Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- var canvas = document.getElementById("myCanvas");
- var ctx = canvas.getContext("2d");
- const gravity = 21;
- const delta = 0.016;
- const opening = 100;
- const minOpen = 25;
- const divisions = 10;
- const nColums = divisions / 2 - 1;
- const offset = 300;
- const tubesVelocity = 1.5;
- const minOpenSosia = 50;
- const populationSize = 200;
- var score = 0;
- var visualScore = 0;
- var generation = 0;
- var bestScore = 0;
- canvas.width = window.innerWidth -50;
- canvas.height = 300;
- class Matrix{
- constructor(rows, columns){
- this.rows = rows;
- this.columns = columns;
- this.data = [];
- for (let row = 0; row < this.rows; row++){
- this.data.push([]);
- }
- //this.randomize();
- }
- randomize(){
- for (let row = 0; row < this.rows; row++){
- for (let column = 0; column < this.columns; column++){
- this.data[row][column] = Math.random() * 2 - 1;
- }
- }
- }
- static multiply(a, b){
- if(a.columns != b.rows)
- return;
- let prodotto = new Matrix(a.rows, b.columns);
- for (let i = 0; i < prodotto.rows; i++){
- for (let y = 0; y < prodotto.columns; y++){
- let somma = 0;
- for (let z = 0; z < b.rows; z++){
- somma += a.data[i][z] * b.data[z][y];
- }
- prodotto.data[i][y] = somma;
- }
- }
- return prodotto;
- }
- static convertArray(array){
- let risultato = new Matrix(array.length, 1);
- for (let row = 0; row < risultato.rows; row++){
- risultato.data[row][0] = array[row];
- }
- return risultato;
- }
- map(funzione){
- for (let row = 0; row < this.rows; row++){
- for (let column = 0; column < this.columns; column++){
- this.data[row][column] = funzione(this.data[row][column]);
- }
- }
- }
- add(n){
- for (let row = 0; row < this.rows; row++){
- for (let column = 0; column < this.columns; column++){
- this.data[row][column] += n.data[row][column];
- }
- }
- }
- mutate(rate){
- for (let row = 0; row < this.rows; row++){
- for (let column = 0; column < this.columns; column++){
- if(Math.random() < rate){
- this.data[row][column] = (Math.random() * 2) - 1;
- }
- }
- }
- }
- stampa(){
- console.log(this);
- console.table(this.data);
- }
- }
- function sigmoid(n){
- return 1 / (1 + Math.exp(-n));
- }
- class NeuralNetwork{
- //TODO: I BIAS
- constructor(numeroNodiInput, numeroNodiHidden, numeroNodiOutput){
- this.numeroNodiInput = numeroNodiInput;
- this.numeroNodiHidden = numeroNodiHidden;
- this.numeroNodiOutput = numeroNodiOutput;
- this.pesiInputHidden = new Matrix(numeroNodiHidden, numeroNodiInput);
- this.pesiInputHidden.randomize();
- this.weightBiasIH = new Matrix(numeroNodiHidden, 1);
- this.weightBiasIH.randomize();
- this.pesiHiddenOutput = new Matrix(numeroNodiOutput, numeroNodiHidden);
- this.pesiHiddenOutput.randomize();
- this.weightBiasHO = new Matrix(numeroNodiOutput, 1);
- this.weightBiasHO.randomize();
- }
- //feed forward
- predict(inputs){
- inputs = Matrix.convertArray(inputs);
- let nodiHidden = Matrix.multiply(this.pesiInputHidden, inputs);
- nodiHidden.add(this.weightBiasIH);
- nodiHidden.map(sigmoid);
- let nodiOutput = Matrix.multiply(this.pesiHiddenOutput, nodiHidden);
- nodiOutput.add(this.weightBiasHO);
- nodiOutput.map(sigmoid);
- return nodiOutput;
- }
- mutate(rate){
- this.pesiInputHidden.mutate(rate);
- this.pesiHiddenOutput.mutate(rate);
- }
- }
- class Tube{
- constructor(x, y, width, height){
- this.x = x;
- this.y = y;
- this.width = width / 1.5;
- this.height = height;
- }
- update(){
- this.x -= tubesVelocity;
- if(this.x < - canvas.width / divisions){
- this.x = canvas.width + (canvas.width / divisions) * (divisions / 2);
- this.height = Math.floor(Math.random() * (canvas.height - opening - minOpen - minOpenSosia)) + minOpen;
- }
- }
- render(){
- ctx.fillStyle = "lightgreen";
- ctx.fillRect(this.x, this.y, this.width, this.height);
- ctx.fillRect(this.x, this.height + opening, this.width, canvas.height);
- ctx.lineWidth = 3;
- ctx.strokeStyle = "black";
- ctx.strokeRect(this.x, this.y, this.width, this.height);
- ctx.strokeRect(this.x, this.height + opening, this.width, canvas.height);
- }
- }
- class Tubes{
- constructor(){
- this.init();
- }
- init(){
- this.tubes = [];
- for(let i = 0; i < nColums; i++){
- 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));
- }
- }
- update(){
- for(let i = 0; i < this.tubes.length; i++){
- this.tubes[i].update();
- }
- }
- render(){
- for(let i = 0; i < this.tubes.length; i++){
- this.tubes[i].render();
- }
- }
- }
- class Bird{
- constructor(brain = undefined){
- this.fitness = 0;
- this.x = canvas.width / 5;
- this.y = canvas.height / 2;
- this.radius = 7;
- this.isJumping = false;
- this.velocity = 0;
- this.nearestTube = 0;
- this.isDead = false;
- this.score = 0;
- this.gravity = gravity;
- if(brain){
- this.brain = brain;
- }else{
- this.brain = new NeuralNetwork(4, 8, 2);
- }
- }
- update(tubes){
- if(this.isDead)
- return;
- //al massimo togli se rallenta//
- let tube = tubes.tubes[this.findFirstTube(tubes)];
- let output = this.brain.predict([tube.x / canvas.width, tube.height / canvas.height, (tube.height + opening) / canvas.height, this.y / canvas.height]);
- if(output.data[0][0] > 0.5){
- this.velocity -= 11;
- }
- this.gravity += output.data[1][0];
- this.velocity += this.gravity * delta;
- //clamp value of velocity
- if(this.velocity > 5){
- this.velocity = 5;
- }else if(this.velocity < -8){
- this.velocity = -8;
- }
- this.y += this.velocity;
- //collisioni
- for(let i = 0; i < tubes.tubes.length; i++){
- let tube = tubes.tubes[i];
- 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)){
- //this.y = canvas.height / 2;
- this.nearestTube = 0;
- this.isDead = true;
- this.score = score;
- }
- }
- }
- findFirstTube(tubes){
- if(this.x - this.radius > tubes.tubes[this.nearestTube].x + tubes.tubes[this.nearestTube].width){
- this.nearestTube++;
- visualScore++;
- }
- this.nearestTube %= tubes.tubes.length;
- return this.nearestTube;
- }
- render(){
- if(this.isDead)
- return;
- ctx.beginPath();
- ctx.lineWidth = 1;
- ctx.fillStyle = "rgba(255, 0, 0, 0.5)";
- ctx.strokeStyle = "black";
- ctx.arc(this.x, this.y, this.radius, 0, 2 * Math.PI);
- ctx.stroke();
- ctx.fill();
- ctx.closePath();
- }
- calculateFitness(totalScore){
- this.fitness = this.score / totalScore;
- return this.score / totalScore;
- }
- mutate(rate){
- this.brain.mutate(rate);
- }
- }
- function random(min, max){
- return (Math.random() * (max - min)) + min;
- }
- class Population{
- constructor(size){
- this.size = size;
- this.mutationRate = 0.01;
- this.birds = [];
- for(let i = 0; i < this.size; i++){
- this.birds.push(new Bird());
- }
- }
- update(tubes){
- for(let i = 0; i < this.birds.length; i++){
- this.birds[i].update(tubes);
- }
- if(this.isAllDead()){
- if(visualScore > bestScore)
- bestScore = visualScore;
- let totalFitness = 0;
- let totalScore = this.calculateTotalScore();
- //calculate fitness
- for(let i = 0; i < this.birds.length; i++){
- totalFitness += this.birds[i].calculateFitness(totalScore);
- }
- let newBirds = [];
- for(let i = 0; i < this.size; i++){
- let mother = this.pickOne(this.birds);
- let father = this.pickOne(this.birds);
- let child = this.crossover(mother.brain, father.brain);
- //slider.value = 0;
- child.mutate(this.mutationRate);
- newBirds.push(child);
- }
- this.birds = newBirds;
- tubes.init();
- score = 0;
- visualScore = 0;
- generation++;
- generationCounter.innerHTML = "Generation: " + generation;
- bestScoreCounter.innerHTML = "Best Score : " + bestScore;
- }else{
- score += 1;
- }
- }
- render(){
- for(let i = 0; i < this.birds.length; i++){
- this.birds[i].render();
- }
- }
- isAllDead(){
- let counter = 0;
- for(let i = 0; i < this.birds.length; i++){
- if(this.birds[i].isDead){
- counter++;
- }
- }
- if(counter === this.birds.length)
- return true;
- return false;
- }
- crossover(motherBrain, fatherBrain){
- let childBrain = new NeuralNetwork(4, 8, 2);
- for(let i = 0; i < motherBrain.pesiInputHidden.rows; i++){
- for(let j = 0; j < motherBrain.pesiInputHidden.columns; j++){
- if(random(0, 100) > 50){
- childBrain.pesiInputHidden.data[i][j] = fatherBrain.pesiInputHidden.data[i][j];
- }else{
- childBrain.pesiInputHidden.data[i][j] = motherBrain.pesiInputHidden.data[i][j];
- }
- }
- }
- for(let i = 0; i < motherBrain.pesiHiddenOutput.rows; i++){
- for(let j = 0; j < motherBrain.pesiHiddenOutput.columns; j++){
- if(random(0, 100) > 50){
- childBrain.pesiHiddenOutput.data[i][j] = fatherBrain.pesiHiddenOutput.data[i][j];
- }else{
- childBrain.pesiHiddenOutput.data[i][j] = motherBrain.pesiHiddenOutput.data[i][j];
- }
- }
- }
- let child = new Bird(childBrain);
- return child;
- }
- calculateTotalScore(){
- let score = 0;
- for(let i = 0; i < this.birds.length; i++){
- score += this.birds[i].score;
- }
- return score;
- }
- pickOne(birds){
- let index = 0;
- let r = Math.random();
- while(r > 0){
- r = r - birds[index].fitness;
- index++;
- }
- index--;
- return birds[index];
- }
- }
- var tubes;
- var population;
- var slider = document.getElementById("myRange");
- var generationCounter = document.getElementById("generation");
- var bestScoreCounter = document.getElementById("bestScore");
- function init(){
- tubes = new Tubes();
- population = new Population(populationSize);
- }
- function update(){
- population.update(tubes);
- tubes.update();
- }
- function render(){
- ctx.beginPath();
- ctx.fillStyle = "lightblue";
- ctx.fillRect(0, 0, canvas.width, canvas.height);
- tubes.render();
- population.render();
- ctx.font = "25px Arial";
- ctx.fillStyle = "black";
- ctx.fillText("Score: " + visualScore , 10, 25);
- ctx.closePath();
- }
- function gameLoop(){
- for(let i = 0; i < slider.value; i++){
- update();
- }
- render();
- window.requestAnimationFrame(gameLoop);
- }
- init();
- window.requestAnimationFrame(gameLoop);
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement