Repository navigation
Expand file tree
/
Copy pathmain.js
More file actions
34 lines (34 loc) · 19.1 KB
/
Copy pathmain.js
File metadata and controls
34 lines (34 loc) · 19.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
var $jscomp=$jscomp||{};$jscomp.scope={};$jscomp.ASSUME_ES5=!1;$jscomp.ASSUME_NO_NATIVE_MAP=!1;$jscomp.ASSUME_NO_NATIVE_SET=!1;$jscomp.objectCreate=$jscomp.ASSUME_ES5||"function"==typeof Object.create?Object.create:function(a){var b=function(){};b.prototype=a;return new b};$jscomp.underscoreProtoCanBeSet=function(){var a={a:!0},b={};try{return b.__proto__=a,b.a}catch(c){}return!1};
$jscomp.setPrototypeOf="function"==typeof Object.setPrototypeOf?Object.setPrototypeOf:$jscomp.underscoreProtoCanBeSet()?function(a,b){a.__proto__=b;if(a.__proto__!==b)throw new TypeError(a+" is not extensible");return a}:null;
$jscomp.inherits=function(a,b){a.prototype=$jscomp.objectCreate(b.prototype);a.prototype.constructor=a;if($jscomp.setPrototypeOf){var c=$jscomp.setPrototypeOf;c(a,b)}else for(c in b)if("prototype"!=c)if(Object.defineProperties){var d=Object.getOwnPropertyDescriptor(b,c);d&&Object.defineProperty(a,c,d)}else a[c]=b[c];a.superClass_=b.prototype};
var Data={get:function(a){return JSON.parse(localStorage.getItem(a))},set:function(a,b){localStorage.setItem(a,JSON.stringify(b))},exists:function(a){return null!=this.get(a)}};var Switch=function(a,b,c,d){this.states=a;this.labels=b;this.stateName=c;Data.exists(c)?this.selectedState=this.getStateIndex(Data.get(c)):this.selectedState=0;this.btn=createButton(this.labels[this.selectedState]);var f=this;this.btn.mousePressed(function(){f.selectedState=1-f.selectedState;Data.set(f.stateName,f.states[f.selectedState]);f.btn.html(f.labels[f.selectedState]);d&&d.call(this,f.states[f.selectedState])})};
Switch.prototype.getStateIndex=function(a){for(var b=0;b<this.states.length;b++)if(this.states[b]==a)return b;return null};Switch.prototype.show=function(){this.btn.show()};Switch.prototype.hide=function(){this.btn.hide()};var Debug={flag:!1,debugWorker:null,log:function(a){this.flag&&this.debugWorker.postMessage({type:"log",content:a})},err:function(a){this.flag&&this.debugWorker.postMessage({type:"error",content:a})},on:function(){this.flag=!0;this.debugWorker=new Worker("gh-pages/debugWorker.js");this.debugWorker.onerror=function(a){console.error(a)}},off:function(){this.flag=!1;this.debugWorker.terminate()}};var Audio={directory:"app/audio/",soundFlag:!0,musicFlag:!0,sounds:[],load:function(){Data.exists("soundFlag")?this.soundFlag=Data.get("soundFlag"):Data.set("soundFlag",!0);Data.exists("musicFlag")?this.musicFlag=Data.get("musicFlag"):Data.set("musicFlag",!0);this.sounds.playMode=loadSound(this.directory+"playModeSelected.mp3");this.sounds.trainMode=loadSound(this.directory+"trainingModeSelected.mp3");this.sounds.edgeBounce=loadSound(this.directory+"edgeBounce.wav");this.sounds.paddleBounce=loadSound(this.directory+
"paddleBounce.wav");this.sounds.gamePaused=loadSound(this.directory+"gamePaused.wav");this.sounds.gameResumed=loadSound(this.directory+"gameResumed.wav");this.sounds.bgMusic=loadSound(this.directory+"bgMusic.mp3");this.sounds.musicOn=loadSound(this.directory+"musicOn.wav");this.sounds.musicOff=loadSound(this.directory+"musicOff.wav");this.sounds.soundOn=loadSound(this.directory+"soundOn.wav");this.sounds.soundOff=loadSound(this.directory+"soundOff.wav")},_play:function(a){this.sounds[a].play();this.sounds[a].setVolume(.5)},
play:function(a){this.soundFlag&&this._play(a)},stop:function(a){this.sounds[a].stop()},loop:function(a){this.musicFlag&&(this.sounds[a].loop(),this.sounds[a].setVolume(.05))},soundOn:function(){this._play("soundOn");this.soundFlag=!0;Data.set("soundFlag",!0)},soundOff:function(){this._play("soundOff");this.soundFlag=!1;Data.set("soundFlag",!1)},musicOn:function(){this._play("musicOn");this.musicFlag=!0;Data.set("musicFlag",!0);this.bgMusic()},musicOff:function(){this._play("musicOff");this.musicFlag=
!1;Data.set("musicFlag",!1);this.stopBgMusic()},bgMusic:function(){this.loop("bgMusic")},stopBgMusic:function(){this.stop("bgMusic")},playMode:function(){this._play("playMode")},trainMode:function(){this._play("trainMode")},edgeBounce:function(){this.play("edgeBounce")},paddleBounce:function(){this.play("paddleBounce")},gamePaused:function(){this._play("gamePaused")},gameResumed:function(){this._play("gameResumed")}};var Direction={RIGHT:1,LEFT:-1};var Mode={TRAIN:1,PLAY:-1};var GameEvent={BALL_BOUNCED:2,RIGHT_PLAYER_SCORED:3,LEFT_PLAYER_SCORED:-3,NONE:0};var GameObject=function(a){this.x=a.x;this.y=a.y;this.w=a.w;this.h=a.h};GameObject.prototype.show=function(){rectMode(CENTER);noFill();stroke(255);rect(this.x,this.y,this.w,this.h)};GameObject.prototype.upperEdge=function(){return this.y-this.h/2};GameObject.prototype.lowerEdge=function(){return this.y+this.h/2};GameObject.prototype.rightEdge=function(){return this.x+this.w/2};GameObject.prototype.leftEdge=function(){return this.x-this.w/2};
GameObject.prototype.above=function(a){return e(this.lowerEdge(),"\u2191",a.upperEdge())};GameObject.prototype.below=function(a){return e(this.upperEdge(),"\u2193",a.lowerEdge())};GameObject.prototype.resize=function(){throw Error("Cannot call abstract method");};var Ball=function(a){GameObject.call(this,a);this.speed=a.speed;this.xSpeed=a.xSpeed;this.ySpeed=a.ySpeed;this.direction=Direction.LEFT;this.reset()};$jscomp.inherits(Ball,GameObject);Ball.prototype.update=function(){0==int(this.ySpeed)&&(this.ySpeed=1);this.x+=this.xSpeed;this.y+=this.ySpeed;this.x=int(this.x);this.y=int(this.y);return this.checkBoundaries()};
Ball.prototype.checkBoundaries=function(){var a=new GameObject({x:width/2,y:height/2,w:width,h:height});e(this.upperEdge(),"\u2191",a.upperEdge(),"|",this.lowerEdge(),"\u2193",a.lowerEdge())&&(Audio.edgeBounce(),this.ySpeed*=-1,e(this.upperEdge(),"\u2191",a.upperEdge())&&(this.y=int(this.h/2)),e(this.lowerEdge(),"\u2193",a.lowerEdge())&&(this.y=int(height-this.h/2)));return e(this.leftEdge(),"\u2192",a.rightEdge())?(this.direction=Direction.LEFT,GameEvent.LEFT_PLAYER_SCORED):e(this.rightEdge(),"\u2190",
a.leftEdge())?(this.direction=Direction.RIGHT,GameEvent.RIGHT_PLAYER_SCORED):null};Ball.prototype.reset=function(){this.x=int(width/2);this.y=int(height/2);var a=random(-PI/4,PI/4);this.xSpeed=this.speed*cos(a);this.ySpeed=this.speed*sin(a);this.xSpeed=float(this.xSpeed.toFixed(3));this.ySpeed=float(this.ySpeed.toFixed(3));this.direction==Direction.LEFT?0>this.xSpeed||(this.xSpeed*=-1):0<this.xSpeed||(this.xSpeed*=-1)};Ball.prototype.resize=function(){this.reset()};var Paddle=function(a){GameObject.call(this,a);this.ySpeed=0;this.movePixels=12;this.gameMode=a.gameMode};$jscomp.inherits(Paddle,GameObject);Paddle.prototype.update=function(a){this.y+=this.ySpeed;this.y=constrain(this.y,this.h/2,height-this.h/2);return this.checkCollisions(a)};Paddle.prototype.up=function(){this.ySpeed=-this.movePixels};Paddle.prototype.down=function(){this.ySpeed=this.movePixels};
Paddle.prototype.stop=function(){this.gameMode==Mode.PLAY&&keyIsPressed&&38==keyCode|40==keyCode||(this.ySpeed=0)};Paddle.prototype.checkCollisions=function(a){throw Error("Cannot call abstract method");};var Score=function(a){GameObject.call(this,a);this.value=a.value||0;this.size=a.size};$jscomp.inherits(Score,GameObject);Score.prototype.show=function(){fill(255);textSize(this.size);text(this.value,this.x,this.y)};Score.prototype.up=function(){this.value++};var LeftPaddle=function(a){Paddle.call(this,a);this.x=this.w};$jscomp.inherits(LeftPaddle,Paddle);LeftPaddle.prototype.checkCollisions=function(a){if(e(a.upperEdge(),"\u2191",this.lowerEdge(),"&",a.lowerEdge(),"\u2193",this.upperEdge(),"&",a.leftEdge(),"\u2190",this.rightEdge())){var b=a.y-this.upperEdge();b=constrain(b,0,this.h);b=map(b,0,this.h,-radians(45),radians(45));a.xSpeed=a.speed*cos(b);a.ySpeed=a.speed*sin(b);a.x=this.x+this.w/2+a.h/2;Audio.paddleBounce();return GameEvent.BALL_BOUNCED}return GameEvent.NONE};
LeftPaddle.prototype.resize=function(){this.x=this.w};var RightPaddle=function(a){Paddle.call(this,a);this.x=width-this.w};$jscomp.inherits(RightPaddle,Paddle);RightPaddle.prototype.checkCollisions=function(a){if(e(a.upperEdge(),"\u2191",this.lowerEdge(),"&",a.lowerEdge(),"\u2193",this.upperEdge(),"&",a.rightEdge(),"\u2192",this.leftEdge())){var b=a.y-this.upperEdge();b=constrain(b,0,this.h);b=map(b,0,this.h,radians(225),radians(135));a.xSpeed=a.speed*cos(b);a.ySpeed=a.speed*sin(b);a.x=this.x-this.w/2-a.h/2;Audio.paddleBounce();return GameEvent.BALL_BOUNCED}return GameEvent.NONE};
RightPaddle.prototype.resize=function(){this.x=width-this.w};var LeftScore=function(a){Score.call(this,a);this.x=width/4};$jscomp.inherits(LeftScore,Score);LeftScore.prototype.resize=function(){this.x=width/4};var RightScore=function(a){Score.call(this,a);this.x=3*width/4};$jscomp.inherits(RightScore,Score);RightScore.prototype.resize=function(){this.x=3*width/4};var Creature=function(a){this.dna=a?a:new DNA;this.prob=this.fitness=0;this.fitnessData=[];this.dead=!0;this.nbHits=0;this.nbHitsThreshold=10};
Creature.prototype.calculateFitness=function(){if(this.dead)Debug.log("dead !"),this.prob=this.fitness=0;else{for(var a=0,b=0,c=0;c<this.fitnessData.length;c++){var d=this.fitnessData[c];a=Math.max(a,d.distance);b=Math.max(b,d.error)}for(c=0;c<this.fitnessData.length;c++)d=this.fitnessData[c],d.distance/=a,0!=b&&(d.error/=b);a=[];for(c=0;c<this.fitnessData.length;c++)d=this.fitnessData[c],a.push((d.ballInSight?1:-1)*(1/(1+d.distance)+(d.ballInSight?0:d.error)));d=a.reduce(function(a,b){return Math.min(a,
b)});if(0>d)for(c=0;c<a.length;c++)a[c]+=-d;this.fitness=a.reduce(function(a,b){return a+b});this.fitness-=(1-this.nbHits/this.nbHitsThreshold)*this.fitness;this.fitness=sq(this.fitness)}};Creature.prototype.reset=function(){this.fitness=0;this.fitnessData=[];this.prob=0;this.dead=!0;this.nbHits=0};var DNA=function(a,b){this.mutationRate=b||.1;this.genes=a?a:[float(randomGaussian().toFixed(1)),float(randomGaussian().toFixed(1)),float(randomGaussian().toFixed(1)),float(randomGaussian().toFixed(1))]};DNA.prototype.crossOver=function(a){for(var b=[],c=0;c<this.genes.length;c++).5>=random(1)?b[c]=this.genes[c]:b[c]=a.genes[c];return new DNA(b,this.mutationRate)};
DNA.prototype.mutation=function(){for(var a=0;a<this.genes.length;a++){var b=random(1);b<1/3?(this.genes[a]+=.1,this.genes[a]=float(this.genes[a].toFixed(1))):b<2/3&&(this.genes[a]+=-.1,this.genes[a]=float(this.genes[a].toFixed(1)))}};DNA.random=function(){var a=[float(randomGaussian().toFixed(1)),float(randomGaussian().toFixed(1)),float(randomGaussian().toFixed(1)),float(randomGaussian().toFixed(1))];return new DNA(a)};var Population=function(a){this.creatures=[];this.populationSize=a||100;this.generationNumber=1;for(a=0;a<this.populationSize-1;a++)this.add(new Creature);this.add(new Creature(new DNA(Data.get("bestGene"))));this.badGeneration=!1};Population.prototype.add=function(a){this.creatures.push(a)};Population.prototype.pickOne=function(){if(this.badGeneration)return random(this.creatures);for(var a=0,b=random(1);0<b;)b-=this.creatures[a].prob,a++;a--;return this.creatures[a]};
Population.prototype.bestOne=function(){if(this.badGeneration)return random(this.creatures);for(var a=0,b=null,c=0;c<this.populationSize;c++)this.creatures[c].prob>a&&(a=this.creatures[c].prob,b=this.creatures[c]);return b};
Population.prototype.evaluate=function(){for(var a=0,b=!0,c=0;c<this.populationSize;c++)this.creatures[c].calculateFitness(),a+=this.creatures[c].fitness,this.creatures[c].dead||(b=!1);this.badGeneration=!1;if(0==a||b)for(this.badGeneration=!0,c=0;c<this.creatures.length;c++)this.creatures[c].prob=0;else for(c=0;c<this.creatures.length;c++)this.creatures[c].prob=this.creatures[c].fitness/a};Population.prototype.randomize=function(){this.creatures=[];for(var a=0;a<this.populationSize;a++)this.add(new Creature(DNA.random()))};
Population.prototype.selection=function(){this.badGeneration&&(Debug.log("randomizing !"),this.randomize());for(var a=[],b=0;b<this.creatures.length-1;b++){var c=this.pickOne().dna,d=this.pickOne().dna;c=c.crossOver(d);c.mutation();a.push(new Creature(c))}(b=this.bestOne())?(Debug.log(b.dna.genes),Data.set("bestGene",b.dna.genes),b.reset(),a.push(b)):a.push(new Creature(DNA.random()));this.creatures=a;this.generationNumber++};var Player=function(){this.paddle=this.score=null};Player.prototype.show=function(){this.score.show();this.paddle.show()};Player.prototype.scoreUp=function(){this.score.up()};Player.prototype.scoreDown=function(){};Player.prototype.paddleUp=function(){this.paddle.up()};Player.prototype.paddleDown=function(){this.paddle.down()};Player.prototype.paddleStop=function(){this.paddle.stop()};Player.prototype.resize=function(){this.score.resize();this.paddle.resize()};var HumanPlayer=function(a){Player.call(this,a);this.score=new RightScore(a.score);this.paddle=new RightPaddle(a.paddle)};$jscomp.inherits(HumanPlayer,Player);HumanPlayer.prototype.update=function(a){this.paddle.update(a)};var PerfectPlayer=function(a){Player.call(this,a);this.score=new RightScore(a.score);this.paddle=new RightPaddle(a.paddle)};$jscomp.inherits(PerfectPlayer,Player);PerfectPlayer.prototype.update=function(a){this.paddle.stop();a.y-a.h/2>this.paddle.y-this.paddle.h/2&&a.y+a.h/2<this.paddle.y+this.paddle.h/2||(a.y<this.paddle.y?.7>=random(1)&&this.paddle.up():a.y>this.paddle.y&&.7>=random(1)&&this.paddle.down());this.paddle.update(a);this.paddle.checkCollisions(a)};var AIPlayer=function(a){Player.call(this,a);this.score=new LeftScore(a.score);this.paddle=new LeftPaddle(a.paddle);this.gameMode=a.gameMode;this.gameMode==Mode.TRAIN?(this.population=new Population(9),this.population.add(new Creature(new DNA(Data.get("bestGene")))),this.index=0,this.genes=this.population.creatures[this.index].dna.genes):this.genes=Data.get("bestGene")};$jscomp.inherits(AIPlayer,Player);
AIPlayer.prototype.update=function(a){var b=!1,c=this.genes[2],d=1/(1+exp(-this.genes[3]*(this.genes[0]*this.paddle.y+this.genes[1]*a.y)));d<=-c?(this.paddleDown(),b=!0):d<=c?this.paddleStop():(this.paddleUp(),b=!0);if(this.gameMode==Mode.TRAIN){var f=a.lowerEdge()>this.paddle.upperEdge()&&a.upperEdge()<this.paddle.lowerEdge(),g=0;f||(a.above(this.paddle)?g=this.paddle.upperEdge()-a.lowerEdge():a.below(this.paddle)&&(g=a.upperEdge()-this.paddle.lowerEdge()))}c=this.paddle.update(a);this.gameMode==
Mode.TRAIN&&(d=this.population.creatures[this.index],d.fitnessData.push({ballInSight:f,error:g,distance:abs(a.leftEdge()-this.paddle.rightEdge())}),b&&(d.dead=!1),c==GameEvent.BALL_BOUNCED&&d.nbHits++,d.nbHits==d.nbHitsThreshold&&this.improve());this.paddle.checkCollisions(a)};AIPlayer.prototype.scoreDown=function(){this.gameMode==Mode.TRAIN&&this.improve()};
AIPlayer.prototype.improve=function(){this.index++;this.index==this.population.populationSize&&(this.index=0,this.population.evaluate(),this.population.selection());this.genes=this.population.creatures[this.index].dna.genes};
AIPlayer.prototype.show=function(){Player.prototype.show.call(this);fill(255);textSize(30);this.gameMode==Mode.TRAIN?(text("Generation # "+this.population.generationNumber,width/4-50,height-80),text("Genome # "+((this.population.generationNumber-1)*this.population.populationSize+this.index+1),width/4-50,height-50),text("Genome : "+JSON.stringify(this.population.creatures[this.index].dna.genes),width/4-50,height-20)):text("Genome : "+JSON.stringify(this.genes),width/4-50,height-20)};var World=function(a,b,c){this.w=a;this.h=b;this.mode=c;this.pause=null;this.objects=[];this.params={ball:{x:a/2,y:b/2,w:20,h:20,speed:16,xSpeed:null,ySpeed:null},rightPlayer:{gameMode:c,paddle:{y:b/2,w:20,h:80,gameMode:c},score:{y:50,size:60}},leftPlayer:{gameMode:c,paddle:{y:b/2,w:20,h:80,gameMode:c},score:{y:50,size:60}}};this.init()};World.prototype.add=function(a,b){this.objects[a]=b};
World.prototype.init=function(){var a=createCanvas(this.w,this.h);a.class("centered");a.id("gameCanvas");a=new Ball(this.params.ball);this.mode==Mode.PLAY&&(a.speed-=.25*a.speed);this.add("ball",a);this.mode==Mode.PLAY?this.add("rightPlayer",new HumanPlayer(this.params.rightPlayer)):this.mode==Mode.TRAIN&&this.add("rightPlayer",new PerfectPlayer(this.params.rightPlayer));this.add("leftPlayer",new AIPlayer(this.params.leftPlayer))};
World.prototype.show=function(){background(0);line(width/2,0,width/2,height);for(var a in this.objects)this.objects[a].show();this.pause&&(fill(255),textSize(40),text("Pause",width/2-46,height/2-10))};
World.prototype.update=function(){var a=this.get("ball"),b=a.update();b==GameEvent.LEFT_PLAYER_SCORED?(this.get("leftPlayer").scoreUp(),this.get("rightPlayer").scoreDown(),a.reset()):b==GameEvent.RIGHT_PLAYER_SCORED&&(this.get("rightPlayer").scoreUp(),this.get("leftPlayer").scoreDown(),a.reset());this.get("rightPlayer").update(a);this.get("leftPlayer").update(a)};World.prototype.get=function(a){return this.objects[a]};
World.prototype.resize=function(){for(var a in this.objects)this.objects[a].resize();this.pauseOn()};World.prototype.togglePause=function(){(this.pause=!this.pause)?(noLoop(),Audio.gamePaused()):(loop(),Audio.gameResumed())};World.prototype.pauseOn=function(){this.pause||(this.pause=!0,noLoop(),Audio.gamePaused())};World.prototype.pauseOff=function(){this.pause&&(this.pause=!1,loop(),Audio.gameResumed())};var world,w,h,myFont,modeSwitch,soundSwitch,musicSwitch;function preload(){Data.exists("bestGene")||Data.set("bestGene",[0,0,0,0]);Audio.load();myFont=loadFont("app/ui/DisposableDroidBB.ttf")}
function setup(){textFont(myFont,36);select("body").style("background-color","black");Audio.bgMusic();updateDimensions();var a=Data.exists("gameMode")?Data.get("gameMode"):Mode.TRAIN;world=new World(w,h,a);world.pause=!1;modeSwitch=new Switch([Mode.TRAIN,Mode.PLAY],["Switch to Play Mode","Switch to Train Mode"],"gameMode",function(a){updateDimensions();world=new World(w,h,a);switch(a){case Mode.TRAIN:Audio.trainMode();break;case Mode.PLAY:Audio.playMode()}});modeSwitch.btn.position(10,10);modeSwitch.btn.class("switchBtn");
soundSwitch=new Switch([!0,!1],["Sound : Off","Sound : On"],"soundFlag",function(a){a?Audio.soundOn():Audio.soundOff()});soundSwitch.btn.position(160,10);soundSwitch.btn.class("switchBtn");musicSwitch=new Switch([!0,!1],["Music : Off","Music : On"],"musicFlag",function(a){a?Audio.musicOn():Audio.musicOff()});musicSwitch.btn.position(250,10);musicSwitch.btn.class("switchBtn");Debug.on();select("#credits").show()}function draw(){world.update();world.show()}
function windowResized(){updateDimensions();resizeCanvas(w,h);world.resize()}function keyReleased(){world.mode==Mode.PLAY&&world.get("rightPlayer").paddleStop()}
function keyPressed(){if("p"===key||"P"===key)return world.togglePause(),world.pause?(modeSwitch.hide(),soundSwitch.hide(),musicSwitch.hide()):(modeSwitch.show(),soundSwitch.show(),musicSwitch.show()),!1;if(world.mode==Mode.PLAY)switch(keyCode){case 38:world.get("rightPlayer").paddleUp();break;case 40:world.get("rightPlayer").paddleDown()}}function updateDimensions(){w=min(.8*windowWidth,windowWidth-150);h=min(4*w/6,windowHeight-150)}
function e(){for(var a=arguments.length,b=0,c=0;c<a;)c+=4,b++;if(3==a)return _e(arguments[0],arguments[1],arguments[2]);if(a!=3*b+(b-1))throw Error("Invalid arguments number ! "+arguments);b=_e(arguments[0],arguments[1],arguments[2]);for(c=4;c<a;){switch(arguments[c-1]){case "&":b=b&&_e(arguments[c+0],arguments[c+1],arguments[c+2]);break;case "|":b=b||_e(arguments[c+0],arguments[c+1],arguments[c+2]);break;default:throw Error("Invalid operator: "+arguments[c-1]);}c+=4}return b}
function _e(a,b,c){switch(b){case "\u2191":return a<c;case "\u2193":return a>c;case "\u2192":return a>c;case "\u2190":return a<c;default:throw Error("Invalid operator: "+b);}};