In this tutorial, I will teach you how to build an 8 Ball Pool Multiplayer Billiards Game Using JavaScript. The complete source code of the JavaScript 8 Ball Pool Game is given in this guide.
I have also added Live Demo and Download buttons at the end of this tutorial, so you can easily download the code of this 8 Ball Pool game with a single click. You can try it yourself on your computer by downloading the code or even play it online using the Live Demo.
<!DOCTYPE html> <html> <head> <meta http-equiv="content-type" content="text/html; charset=UTF8"> <title>Classic Pool Game</title> <link rel="stylesheet" type="text/css" href="css/game-layout.css"/> <link rel="shortcut icon" type="image/png" href="assets/sprites/favicon.png"/> <script src="script/lib/LAB.min.js"></script> <script> $LAB .script('script/system/Keys.js').wait() .script('script/system/Color.js').wait() .script('script/geom/Vector2.js').wait() .script('script/input/ButtonState.js').wait() .script('script/input/Keyboard.js').wait() .script('script/input/Mouse.js').wait() .script('script/Global.js').wait() .script('script/Canvas2D.js').wait() .script('script/game_objects/Score.js').wait() .script('script/game_objects/Ball.js').wait() .script('script/game_objects/Stick.js').wait() .script('script/menu/Label.js').wait() .script('script/menu/Button.js').wait() .script('script/menu/Menu.js').wait() .script('script/menu/MainMenu.js').wait() .script('script/AI/Opponent.js').wait() .script('script/AI/AIPolicy.js').wait() .script('script/AI/AITrainer.js').wait() .script('script/game_objects/Player.js').wait() .script('script/GamePolicy.js').wait() .script('script/GameWorld.js').wait() .script('script/Game.js').wait() .script('script/Assets.js').wait(function () { Game.start('gameArea','screen', 1500, 825); }); </script> </head> <body style = "background-color:black"> <div id="gameArea"> <canvas id="screen" width="2000" height="1000"></canvas> </div> </body> </html>
html, body { margin: 0; } /*#screen{ padding-left: 0; padding-right: 0; margin-left: auto; margin-right: auto; display: block; }*/
function AIPolicy(){ } AIPolicy.prototype.evaluate = function(state, gamePolicy){ let evaluation = 1; for (var i = 0 ; i < state.balls.length; i++){ for(var j = i + 1 ; j < state.balls.length ; j++){ let firstBall = state.balls[i]; let secondBall = state.balls[j]; if(firstBall === state.whiteBall || secondBall === state.whiteBall || firstBall.inHole || secondBall.inHole){ continue; } evaluation += firstBall.position.distanceFrom(secondBall.position); } } evaluation = evaluation/5800; if(!gamePolicy.firstCollision){ evaluation+= 100; } evaluation += 2000 * gamePolicy.validBallsInsertedOnTurn; gamePolicy.updateTurnOutcome(); if(gamePolicy.won){ if(!gamePolicy.foul){ evaluation += 10000; } else{ evaluation -= 10000; } } if(gamePolicy.foul){ evaluation = evaluation - 3000; } return evaluation; }
function AITrainer(){ this.AIPolicy = new AIPolicy(); } AITrainer.prototype.init = function(state, gamePolicy){ AI.opponents = []; AI.currentOpponent = new Opponent(); AI.finishedSession = true; AI.iteration = 0; AI.bestOpponentIndex = 0; AI.bestOpponentEval = 0; if(gamePolicy.foul){ //TO DO: Pick best position for the white ball. state.whiteBall.position.x = 413; state.whiteBall.position.y = 413; state.whiteBall.inHole = false; gamePolicy.foul = false; } AI.initialState = JSON.parse(JSON.stringify(state)); AI.initialGamePolicyState = JSON.parse(JSON.stringify(gamePolicy)); AI.state = state; AI.gamePolicy = gamePolicy; } AITrainer.prototype.train = function(){ if(AI.iteration === TRAIN_ITER){ AI.finishedSession = true; AI.playTurn(); return; } let ballsMoving = AI.state.ballsMoving(); if(!ballsMoving){ if(AI.iteration !== 0){ AI.currentOpponent.evaluation = AI.AIPolicy.evaluate(this.state, this.gamePolicy); AI.opponents.push(JSON.parse(JSON.stringify(AI.currentOpponent))); if(AI.currentOpponent.evaluation > AI.bestOpponentEval){ AI.bestOpponentEval = AI.currentOpponent.evaluation; AI.bestOpponentIndex = AI.opponents.length - 1; } if(LOG){ console.log('-------------'+new Number(AI.iteration+1)+'--------------------'); console.log('Current evaluation: ' + AI.currentOpponent.evaluation); console.log('Current power: ' + AI.currentOpponent.power); console.log('Current rotation: ' + AI.currentOpponent.rotation); console.log('---------------------------------'); } } AI.state.initiateState(AI.initialState.balls); AI.gamePolicy.initiateState(AI.initialGamePolicyState); AI.buildNewOpponent(); AI.simulate(); } } AITrainer.prototype.buildNewOpponent = function(){ if(AI.iteration % 10 === 0){ AI.currentOpponent = new Opponent(); AI.iteration++; return; } let bestOpponent = AI.opponents[AI.bestOpponentIndex]; let newPower = bestOpponent.power; newPower += + ((Math.random() * 30) - 15); newPower = newPower < 20 ? 20 : newPower; newPower = newPower > 75 ? 75 : newPower; let newRotation = bestOpponent.rotation; if(bestOpponent.evaluation > 0){ newRotation += (1/bestOpponent.evaluation)*(Math.random() * 2 * Math.PI - Math.PI) } else{ newRotation = (Math.random() * 2 * Math.PI - Math.PI); } AI.currentOpponent = new Opponent(newPower,newRotation); AI.iteration++; } AITrainer.prototype.simulate = function(){ AI.state.stick.shoot(AI.currentOpponent.power, AI.currentOpponent.rotation); } AITrainer.prototype.playTurn = function(){ bestOpponent = AI.opponents[AI.bestOpponentIndex]; Game.gameWorld.stick.rotation = bestOpponent.rotation; Game.gameWorld.stick.trackMouse = false; setTimeout(() => { Game.gameWorld.stick.visible = true; Canvas2D.clear(); Game.gameWorld.draw(); Game.sound = true; Game.gameWorld.initiateState(AI.initialState.balls); Game.policy.initiateState(AI.initialGamePolicyState); DISPLAY = true; requestAnimationFrame(Game.mainLoop); Game.gameWorld.stick .shoot( bestOpponent.power, bestOpponent.rotation ); Game.gameWorld.stick.trackMouse = true; }, 1000); } AITrainer.prototype.opponentTrainingLoop = function(){ Game.sound = false; DISPLAY = false; if(DISPLAY_TRAINING){ if(!AI.finishedSession){ AI.train(); Game.gameWorld.handleInput(DELTA); Game.gameWorld.update(DELTA); Canvas2D.clear(); Game.gameWorld.draw(); Mouse.reset(); setTimeout(AI.opponentTrainingLoop,0.00000000001); } } else{ while(!AI.finishedSession){ AI.train(); Game.gameWorld.handleInput(DELTA); Game.gameWorld.update(DELTA); Mouse.reset(); } } } AITrainer.prototype.startSession = function(){ setTimeout( ()=>{ Game.gameWorld.stick.visible = false; Canvas2D.clear(); Game.gameWorld.draw(); AI.init(Game.gameWorld, Game.policy); AI.finishedSession = false; AI.opponentTrainingLoop(); }, 1000 ); } const AI = new AITrainer();
function Opponent(power, rotation){ this.power = power || (Math.random() * 75 + 1); this.rotation = rotation || (Math.random()*6.283)-3.141; this.evaluation = 0; }
"use strict"; function Ball(initPos,color){ this.initPos = initPos; this.position = initPos.copy(); this.origin = new Vector2(25,25); this.velocity = Vector2.zero; this.color = color; this.moving = false; this.visible = true; this.inHole = false; } Object.defineProperty(Ball.prototype, "color", { get: function(){ if(this.sprite == sprites.redBall){ return Color.red; } else if(this.sprite == sprites.yellowBall){ return Color.yellow; } else if(this.sprite == sprites.blackBall){ return Color.black; } else{ return Color.white; } }, set: function (value) { if (value === Color.red){ this.sprite = sprites.redBall; } else if(value == Color.yellow){ this.sprite = sprites.yellowBall; } else if(value == Color.black){ this.sprite = sprites.blackBall; } else{ this.sprite = sprites.ball; } } }); Ball.prototype.shoot = function(power, angle){ if(power <= 0) return; this.moving = true; this.velocity = calculateBallVelocity(power,angle); } var calculateBallVelocity = function(power, angle){ return new Vector2(100*Math.cos(angle)*power,100*Math.sin(angle)*power); } Ball.prototype.update = function(delta){ this.updatePosition(delta); this.velocity.multiplyWith(0.98); if(this.moving && Math.abs(this.velocity.x) < 1 && Math.abs(this.velocity.y) < 1){ this.stop(); } } Ball.prototype.updatePosition = function(delta){ if(!this.moving || this.inHole) return; var ball = this; var newPos = this.position.add(this.velocity.multiply(delta)); if(Game.policy.isInsideHole(newPos)){ if(Game.sound && SOUND_ON){ var holeSound = sounds.hole.cloneNode(true); holeSound.volume = 0.5; holeSound.play(); } this.position = newPos; this.inHole = true; setTimeout(function(){ball.visible=false;ball.velocity = Vector2.zero;}, 100); Game.policy.handleBallInHole(this); return; } var collision = this.handleCollision(newPos); if(collision){ this.velocity.multiplyWith(0.95); }else{ this.position = newPos; } } Ball.prototype.handleCollision = function(newPos){ var collision = false; if(Game.policy.isXOutsideLeftBorder(newPos, this.origin)){ this.velocity.x = -this.velocity.x; this.position.x = Game.policy.leftBorderX + this.origin.x; collision = true; } else if(Game.policy.isXOutsideRightBorder(newPos, this.origin)){ this.velocity.x = -this.velocity.x; this.position.x = Game.policy.rightBorderX - this.origin.x; collision = true; } if(Game.policy.isYOutsideTopBorder(newPos, this.origin)){ this.velocity.y = -this.velocity.y; this.position.y = Game.policy.topBorderY + this.origin.y; collision = true; } else if(Game.policy.isYOutsideBottomBorder(newPos, this.origin)){ this.velocity.y = -this.velocity.y; this.position.y = Game.policy.bottomBorderY - this.origin.y; collision = true; } return collision; } Ball.prototype.stop = function(){ this.moving = false; this.velocity = Vector2.zero; } Ball.prototype.reset = function(){ this.inHole = false; this.moving = false; this.velocity = Vector2.zero; this.position = this.initPos; this.visible = true; } Ball.prototype.out = function(){ this.position = new Vector2(0, 900); this.visible = false; this.inHole = true; } Ball.prototype.draw = function () { if(!this.visible) return; Canvas2D.drawImage(this.sprite, this.position, 0, 1, new Vector2(25,25)); };
function Player(matchScore, totalScore){ this.color = undefined; this.matchScore = matchScore; this.totalScore = totalScore; }
"use strict"; function Score(position){ this.position = position; this.origin = new Vector2(47,82); this.value = 0; } Score.prototype.reset = function(){ this.position = position; this.origin = new Vector2(30,0); this.value = 0; }; Score.prototype.draw = function () { Canvas2D.drawText( this.value, this.position, this.origin, "#096834", "top", "Impact", "200px" ); }; Score.prototype.drawLines = function (color) { for(let i=0; i<this.value; i++){ let pos = this.position.add(new Vector2(i*15,0)); Canvas2D.drawText( "I", pos, this.origin, color, "top", "Arial", "20px" ); } }; Score.prototype.increment = function(){ this.value++; };
"use strict"; function Stick(position){ this.position = position; this.origin = new Vector2(970,11); this.shotOrigin = new Vector2(950,11); this.shooting = false; this.visible = true; this.rotation = 0; this.power = 0; this.trackMouse = true; } Stick.prototype.handleInput = function (delta) { if(AI_ON && Game.policy.turn === AI_PLAYER_NUM) return; if(Game.policy.turnPlayed) return; if(Keyboard.down(Keys.W) && KEYBOARD_INPUT_ON){ if(this.power < 75){ this.origin.x+=2; this.power+=1.2; } } if(Keyboard.down(Keys.S) && KEYBOARD_INPUT_ON){ if(this.power>0){ this.origin.x-=2; this.power-=1.2; } } else if (this.power>0 && Mouse.left.down){ var strike = sounds.strike.cloneNode(true); strike.volume = (this.power/(10))<1?(this.power/(10)):1; strike.play(); Game.policy.turnPlayed = true; this.shooting = true; this.origin = this.shotOrigin.copy(); Game.gameWorld.whiteBall.shoot(this.power, this.rotation); var stick = this; setTimeout(function(){stick.visible = false;}, 500); } else if(this.trackMouse){ var opposite = Mouse.position.y - this.position.y; var adjacent = Mouse.position.x - this.position.x; this.rotation = Math.atan2(opposite, adjacent); } }; Stick.prototype.shoot = function(power, rotation){ this.power = power; this.rotation = rotation; if(Game.sound && SOUND_ON){ var strike = sounds.strike.cloneNode(true); strike.volume = (this.power/(10))<1?(this.power/(10)):1; strike.play(); } Game.policy.turnPlayed = true; this.shooting = true; this.origin = this.shotOrigin.copy(); Game.gameWorld.whiteBall.shoot(this.power, this.rotation); var stick = this; setTimeout(function(){stick.visible = false;}, 500); } Stick.prototype.update = function(){ if(this.shooting && !Game.gameWorld.whiteBall.moving) this.reset(); }; Stick.prototype.reset = function(){ this.position.x = Game.gameWorld.whiteBall.position.x; this.position.y = Game.gameWorld.whiteBall.position.y; this.origin = new Vector2(970,11); this.shooting = false; this.visible = true; this.power = 0; }; Stick.prototype.draw = function () { if(!this.visible) return; Canvas2D.drawImage(sprites.stick, this.position,this.rotation,1, this.origin); };
"use strict"; function Vector2(x, y) { this.x = typeof x !== 'undefined' ? x : 0; this.y = typeof y !== 'undefined' ? y : 0; } Object.defineProperty(Vector2, "zero", { get: function () { return new Vector2(); } }); Object.defineProperty(Vector2.prototype, "isZero", { get: function () { return this.x === 0 && this.y === 0; } }); Object.defineProperty(Vector2.prototype, "length", { get: function () { return Math.sqrt(this.x * this.x + this.y * this.y); } }); Vector2.prototype.addTo = function (v) { if (v.constructor === Vector2) { this.x += v.x; this.y += v.y; } else if (v.constructor === Number) { this.x += v; this.y += v; } return this; }; Vector2.prototype.add = function (v) { var result = this.copy(); return result.addTo(v); }; Vector2.prototype.subtractFrom = function (v) { if (v.constructor === Vector2) { this.x -= v.x; this.y -= v.y; } else if (v.constructor === Number) { this.x -= v; this.y -= v; } return this; }; Vector2.prototype.subtract = function (v) { var result = this.copy(); return result.subtractFrom(v); }; Vector2.prototype.divideBy = function (v) { if (v.constructor === Vector2) { this.x /= v.x; this.y /= v.y; } else if (v.constructor === Number) { this.x /= v; this.y /= v; } return this; }; Vector2.prototype.divide = function (v) { var result = this.copy(); return result.divideBy(v); }; Vector2.prototype.multiplyWith = function (v) { if (v.constructor === Vector2) { this.x *= v.x; this.y *= v.y; } else if (v.constructor === Number) { this.x *= v; this.y *= v; } return this; }; Vector2.prototype.multiply = function (v) { var result = this.copy(); return result.multiplyWith(v); }; Vector2.prototype.toString = function () { return "(" + this.x + ", " + this.y + ")"; }; Vector2.prototype.normalize = function () { var length = this.length; if (length === 0) return; this.divideBy(length); }; Vector2.prototype.copy = function () { return new Vector2(this.x, this.y); }; Vector2.prototype.equals = function (obj) { return this.x === obj.x && this.y === obj.y; }; Vector2.prototype.distanceFrom = function(obj){ return Math.sqrt((this.x-obj.x)*(this.x-obj.x) + (this.y-obj.y)*(this.y-obj.y)); }
"use strict"; function ButtonState() { this.down = false; this.pressed = false; }
"use strict"; function handleKeyDown(evt) { var code = evt.keyCode; if (code < 0 || code > 255) return; if (!Keyboard._keyStates[code].down) Keyboard._keyStates[code].pressed = true; Keyboard._keyStates[code].down = true; } function handleKeyUp(evt) { var code = evt.keyCode; if (code < 0 || code > 255) return; Keyboard._keyStates[code].down = false; } function Keyboard_Singleton() { this._keyStates = []; for (var i = 0; i < 256; ++i) this._keyStates.push(new ButtonState()); document.onkeydown = handleKeyDown; document.onkeyup = handleKeyUp; } Keyboard_Singleton.prototype.reset = function () { for (var i = 0; i < 256; ++i) this._keyStates[i].pressed = false; }; Keyboard_Singleton.prototype.pressed = function (key) { return this._keyStates[key].pressed; }; Keyboard_Singleton.prototype.down = function (key) { return this._keyStates[key].down; }; var Keyboard = new Keyboard_Singleton();
"use strict"; function handleMouseMove(evt) { var canvasScale = Canvas2D.scale; var canvasOffset = Canvas2D.offset; var mx = (evt.pageX - canvasOffset.x) / canvasScale.x; var my = (evt.pageY - canvasOffset.y) / canvasScale.y; Mouse._position = new Vector2(mx, my); } function handleMouseDown(evt) { handleMouseMove(evt); if (evt.which === 1) { if (!Mouse._left.down) Mouse._left.pressed = true; Mouse._left.down = true; } else if (evt.which === 2) { if (!Mouse._middle.down) Mouse._middle.pressed = true; Mouse._middle.down = true; } else if (evt.which === 3) { if (!Mouse._right.down) Mouse._right.pressed = true; Mouse._right.down = true; } } function handleMouseUp(evt) { handleMouseMove(evt); if (evt.which === 1) Mouse._left.down = false; else if (evt.which === 2) Mouse._middle.down = false; else if (evt.which === 3) Mouse._right.down = false; } function Mouse_Singleton() { this._position = Vector2.zero; this._left = new ButtonState(); this._middle = new ButtonState(); this._right = new ButtonState(); document.onmousemove = handleMouseMove; document.onmousedown = handleMouseDown; document.onmouseup = handleMouseUp; } Object.defineProperty(Mouse_Singleton.prototype, "left", { get: function () { return this._left; } }); Object.defineProperty(Mouse_Singleton.prototype, "middle", { get: function () { return this._middle; } }); Object.defineProperty(Mouse_Singleton.prototype, "right", { get: function () { return this._right; } }); Object.defineProperty(Mouse_Singleton.prototype, "position", { get: function () { return this._position; } }); Mouse_Singleton.prototype.reset = function () { this._left.pressed = false; this._middle.pressed = false; this._right.pressed = false; }; Mouse_Singleton.prototype.containsMouseDown = function (rect) { return this._left.down && rect.contains(this._position); }; Mouse_Singleton.prototype.containsMousePress = function (rect) { return this._left.pressed && rect.contains(this._position); }; var Mouse = new Mouse_Singleton();
/*! LAB.js (LABjs :: Loading And Blocking JavaScript) v2.0.3 (c) Kyle Simpson MIT License */(function(o){var K=o.$LAB,y="UseLocalXHR",z="AlwaysPreserveOrder",u="AllowDuplicates",A="CacheBust",B="BasePath",C=/^[^?#]*\//.exec(location.href)[0],D=/^\w+\:\/\/\/?[^\/]+/.exec(C)[0],i=document.head||document.getElementsByTagName("head"),L=(o.opera&&Object.prototype.toString.call(o.opera)=="[object Opera]")||("MozAppearance"in document.documentElement.style),q=document.createElement("script"),E=typeof q.preload=="boolean",r=E||(q.readyState&&q.readyState=="uninitialized"),F=!r&&q.async===true,M=!r&&!F&&!L;function G(a){return Object.prototype.toString.call(a)=="[object Function]"}function H(a){return Object.prototype.toString.call(a)=="[object Array]"}function N(a,c){var b=/^\w+\:\/\//;if(/^\/\/\/?/.test(a)){a=location.protocol+a}else if(!b.test(a)&&a.charAt(0)!="/"){a=(c||"")+a}return b.test(a)?a:((a.charAt(0)=="/"?D:C)+a)}function s(a,c){for(var b in a){if(a.hasOwnProperty(b)){c[b]=a[b]}}return c}function O(a){var c=false;for(var b=0;b<a.scripts.length;b++){if(a.scripts[b].ready&&a.scripts[b].exec_trigger){c=true;a.scripts[b].exec_trigger();a.scripts[b].exec_trigger=null}}return c}function t(a,c,b,d){a.onload=a.onreadystatechange=function(){if((a.readyState&&a.readyState!="complete"&&a.readyState!="loaded")||c[b])return;a.onload=a.onreadystatechange=null;d()}}function I(a){a.ready=a.finished=true;for(var c=0;c<a.finished_listeners.length;c++){a.finished_listeners[c]()}a.ready_listeners=[];a.finished_listeners=[]}function P(d,f,e,g,h){setTimeout(function(){var a,c=f.real_src,b;if("item"in i){if(!i[0]){setTimeout(arguments.callee,25);return}i=i[0]}a=document.createElement("script");if(f.type)a.type=f.type;if(f.charset)a.charset=f.charset;if(h){if(r){e.elem=a;if(E){a.preload=true;a.onpreload=g}else{a.onreadystatechange=function(){if(a.readyState=="loaded")g()}}a.src=c}else if(h&&c.indexOf(D)==0&&d[y]){b=new XMLHttpRequest();b.onreadystatechange=function(){if(b.readyState==4){b.onreadystatechange=function(){};e.text=b.responseText+"\n//@ sourceURL="+c;g()}};b.open("GET",c);b.send()}else{a.type="text/cache-script";t(a,e,"ready",function(){i.removeChild(a);g()});a.src=c;i.insertBefore(a,i.firstChild)}}else if(F){a.async=false;t(a,e,"finished",g);a.src=c;i.insertBefore(a,i.firstChild)}else{t(a,e,"finished",g);a.src=c;i.insertBefore(a,i.firstChild)}},0)}function J(){var l={},Q=r||M,n=[],p={},m;l[y]=true;l[z]=false;l[u]=false;l[A]=false;l[B]="";function R(a,c,b){var d;function f(){if(d!=null){d=null;I(b)}}if(p[c.src].finished)return;if(!a[u])p[c.src].finished=true;d=b.elem||document.createElement("script");if(c.type)d.type=c.type;if(c.charset)d.charset=c.charset;t(d,b,"finished",f);if(b.elem){b.elem=null}else if(b.text){d.onload=d.onreadystatechange=null;d.text=b.text}else{d.src=c.real_src}i.insertBefore(d,i.firstChild);if(b.text){f()}}function S(c,b,d,f){var e,g,h=function(){b.ready_cb(b,function(){R(c,b,e)})},j=function(){b.finished_cb(b,d)};b.src=N(b.src,c[B]);b.real_src=b.src+(c[A]?((/\?.*$/.test(b.src)?"&_":"?_")+~~(Math.random()*1E9)+"="):"");if(!p[b.src])p[b.src]={items:[],finished:false};g=p[b.src].items;if(c[u]||g.length==0){e=g[g.length]={ready:false,finished:false,ready_listeners:[h],finished_listeners:[j]};P(c,b,e,((f)?function(){e.ready=true;for(var a=0;a<e.ready_listeners.length;a++){e.ready_listeners[a]()}e.ready_listeners=[]}:function(){I(e)}),f)}else{e=g[0];if(e.finished){j()}else{e.finished_listeners.push(j)}}}function v(){var e,g=s(l,{}),h=[],j=0,w=false,k;function T(a,c){a.ready=true;a.exec_trigger=c;x()}function U(a,c){a.ready=a.finished=true;a.exec_trigger=null;for(var b=0;b<c.scripts.length;b++){if(!c.scripts[b].finished)return}c.finished=true;x()}function x(){while(j<h.length){if(G(h[j])){try{h[j++]()}catch(err){}continue}else if(!h[j].finished){if(O(h[j]))continue;break}j++}if(j==h.length){w=false;k=false}}function V(){if(!k||!k.scripts){h.push(k={scripts:[],finished:true})}}e={script:function(){for(var f=0;f<arguments.length;f++){(function(a,c){var b;if(!H(a)){c=[a]}for(var d=0;d<c.length;d++){V();a=c[d];if(G(a))a=a();if(!a)continue;if(H(a)){b=[].slice.call(a);b.unshift(d,1);[].splice.apply(c,b);d--;continue}if(typeof a=="string")a={src:a};a=s(a,{ready:false,ready_cb:T,finished:false,finished_cb:U});k.finished=false;k.scripts.push(a);S(g,a,k,(Q&&w));w=true;if(g[z])e.wait()}})(arguments[f],arguments[f])}return e},wait:function(){if(arguments.length>0){for(var a=0;a<arguments.length;a++){h.push(arguments[a])}k=h[h.length-1]}else k=false;x();return e}};return{script:e.script,wait:e.wait,setOptions:function(a){s(a,g);return e}}}m={setGlobalDefaults:function(a){s(a,l);return m},setOptions:function(){return v().setOptions.apply(null,arguments)},script:function(){return v().script.apply(null,arguments)},wait:function(){return v().wait.apply(null,arguments)},queueScript:function(){n[n.length]={type:"script",args:[].slice.call(arguments)};return m},queueWait:function(){n[n.length]={type:"wait",args:[].slice.call(arguments)};return m},runQueue:function(){var a=m,c=n.length,b=c,d;for(;--b>=0;){d=n.shift();a=a[d.type].apply(null,d.args)}return a},noConflict:function(){o.$LAB=K;return m},sandbox:function(){return J()}};return m}o.$LAB=J();(function(a,c,b){if(document.readyState==null&&document[a]){document.readyState="loading";document[a](c,b=function(){document.removeEventListener(c,b,false);document.readyState="complete"},false)}})("addEventListener","DOMContentLoaded")})(this);
function Button(sprite, position, callback, hoverSprite){ this.sprite = sprite; this.hoverSprite = hoverSprite ? hoverSprite : sprite; this.position = position; this.callback = callback; } Button.prototype.draw = function(){ if(this.mouseInsideBorders()){ Canvas2D.drawImage(this.hoverSprite, this.position, 0, 1); Canvas2D._canvas.style.cursor = "pointer"; } else{ Canvas2D.drawImage(this.sprite, this.position, 0, 0.98); } } Button.prototype.handleInput = function(){ if(Mouse.left.pressed && this.mouseInsideBorders()){ this.callback(); } } Button.prototype.mouseInsideBorders = function(){ mousePos = Mouse.position; if(mousePos.x > this.position.x && mousePos.x < this.position.x + this.sprite.width && mousePos.y > this.position.y && mousePos.y < this.position.y + this.sprite.height ){ return true; } return false; }
function Label(text, position, origin, color, textAlign, fontname, fontsize){ this.text = typeof text !== 'undefined' ? text : ''; this.position = typeof position !== 'undefined' ? position : Vector2.zero; this.origin = typeof origin !== 'undefined' ? origin : Vector2.zero; this.color = typeof color !== 'undefined' ? color : Color.black; this.textAlign = typeof textAlign !== 'undefined' ? textAlign : "top"; this.fontname = typeof fontname !== 'undefined' ? fontname : "Courier New"; this.fontsize = typeof fontsize !== 'undefined' ? fontsize : "20px"; } Label.prototype.draw = function(){ Canvas2D.drawText( this.text, this.position, this.origin, this.color, this.textAlign, this.fontname, this.fontsize ); }
function generateMainMenuLabels(headerText){ let labels = [ new Label( headerText, new Vector2(100,0), Vector2.zero, "white", "left", "Bookman", "100px" ), new Label( "© 2018 Chen Shmilovich", new Vector2(1250,700), Vector2.zero, "white", "left", "Bookman", "20px" ) ]; return labels; } function generateMainMenuButtons(inGame){ let buttons = []; let dev = 0; if(inGame){ dev = 200; buttons.push( new Button ( // CONTINUE BUTTON sprites.continueButton, new Vector2(200,200), function(){ Game.mainMenu.active = false; GAME_STOPPED = false; setTimeout(Game.continueGame,200); sounds.fadeOut(Game.mainMenu.sound); }, sprites.continueButtonHover ) ) } let muteSprite = sprites.muteButton; let muteSpriteHover = sprites.muteButtonHover; if(Game.mainMenu.sound && Game.mainMenu.sound.volume === 0){ muteSprite = sprites.muteButtonPressed; muteSpriteHover = sprites.muteButtonPressedHover; } let muteButton = new Button ( // MUTE BUTTON muteSprite, new Vector2(1430,10), function(){ if(Game.mainMenu.sound.volume == 0){ SOUND_ON = true; Game.mainMenu.sound.volume = 0.8; this.sprite = sprites.muteButton; this.hoverSprite = sprites.muteButtonHover; } else{ SOUND_ON = false; Game.mainMenu.sound.volume = 0.0; this.sprite = sprites.muteButtonPressed; this.hoverSprite = sprites.muteButtonPressedHover; } }, muteSpriteHover ); let backButton = new Button ( //BACK sprites.backButton, new Vector2(100,150), function(){ Game.mainMenu.labels = generateMainMenuLabels("Classic 8-Ball"); Game.mainMenu.buttons = generateMainMenuButtons(inGame); }, sprites.backButtonHover ); buttons = buttons.concat([ new Button ( // PLAYER vs PLAYER sprites.twoPlayersButton, new Vector2(200,dev+200), function(){ AI_ON = false; Game.mainMenu.active = false; GAME_STOPPED = false; setTimeout(Game.startNewGame,200); sounds.fadeOut(Game.mainMenu.sound); }, sprites.twoPlayersButtonHover ), new Button ( // PLAYER vs COMPUTER sprites.onePlayersButton, new Vector2(200,dev+400), function(){ Game.mainMenu.labels = generateMainMenuLabels("Choose Difficulty"); Mouse.reset(); Game.mainMenu.buttons = [ new Button ( //EASY sprites.easyButton, new Vector2(200,150), function(){ AI_PLAYER_NUM = 1; AI_ON = true; TRAIN_ITER = 30; Game.mainMenu.active = false; GAME_STOPPED = false; setTimeout(Game.startNewGame,200); sounds.fadeOut(Game.mainMenu.sound); }, sprites.easyButtonHover ), new Button ( //MEDIUM sprites.mediumButton, new Vector2(200,300), function(){ AI_PLAYER_NUM = 1; AI_ON = true; TRAIN_ITER = 50; Game.mainMenu.active = false; GAME_STOPPED = false; setTimeout(Game.startNewGame,200); sounds.fadeOut(Game.mainMenu.sound); }, sprites.mediumButtonHover ), new Button ( //HARD sprites.hardButton, new Vector2(200,450), function(){ AI_PLAYER_NUM = 1; AI_ON = true; TRAIN_ITER = 100; Game.mainMenu.active = false; GAME_STOPPED = false; setTimeout(Game.startNewGame,200); sounds.fadeOut(Game.mainMenu.sound); }, sprites.hardButtonHover ), new Button ( //INSANE sprites.insaneButton, new Vector2(200,600), function(){ AI_PLAYER_NUM = 0; AI_ON = true; TRAIN_ITER = 700; Game.mainMenu.active = false; GAME_STOPPED = false; setTimeout(Game.startNewGame,200); sounds.fadeOut(Game.mainMenu.sound); }, sprites.insaneButtonHover ), muteButton, backButton ]; }, sprites.onePlayersButtonHover ), muteButton ]); return buttons; }
function Menu(){ } Menu.prototype.init = function ( backgroundSprite, labels, buttons, sound ){ this.background = backgroundSprite; this.labels = labels || []; this.buttons = buttons || []; this.sound = sound ? sound : undefined; this.active = false; } Menu.prototype.load = function(){ this.sound.currentTime = 0; this.active = true; requestAnimationFrame(this.menuLoop.bind(this)); if(SOUND_ON){ this.sound.volume = 0.8; } this.sound.play(); } Menu.prototype.draw = function(){ Canvas2D._canvas.style.cursor = "auto"; Canvas2D.drawImage( this.background, Vector2.zero, 0, 1, Vector2.zero ); for(let i = 0 ; i < this.labels.length ; i++){ this.labels[i].draw(); } for(let i = 0 ; i < this.buttons.length ; i++){ this.buttons[i].draw(); } } Menu.prototype.handleInput = function(){ for(let i = 0 ; i < this.buttons.length ; i++){ this.buttons[i].handleInput(); } } Menu.prototype.menuLoop = function(){ if(this.active){ this.handleInput(); Canvas2D.clear(); this.draw(); Mouse.reset(); requestAnimationFrame(this.menuLoop.bind(this)); } }
"use strict"; var Color = { aliceBlue: "#F0F8FF", antiqueWhite: "#FAEBD7", aqua: "#00FFFF", aquamarine: "#7FFFD4", azure: "#F0FFFF", beige: "#F5F5DC", bisque: "#FFE4C4", black: "#000000", blanchedAlmond: "#FFEBCD", blue: "#0000FF", blueViolet: "#8A2BE2", brown: "#A52A2A", burlyWood: "#DEB887", cadetBlue: "#5F9EA0", chartreuse: "#7FFF00", chocolate: "#D2691E", coral: "#FF7F50", cornflowerBlue: "#6495ED", cornsilk: "#FFF8DC", crimson: "#DC143C", cyan: "#00FFFF", darkBlue: "#00008B", darkCyan: "#008B8B", darkGoldenrod: "#B8860B", darkGray: "#A9A9A9", darkGreen: "#006400", darkKhaki: "#BDB76B", darkMagenta: "#8B008B", darkOliveGreen: "#556B2F", darkOrange: "#FF8C00", darkOrchid: "#9932CC", darkRed: "#8B0000", darkSalmon: "#E9967A", darkSeaGreen: "#8FBC8B", darkSlateBlue: "#483D8B", darkSlateGray: "#2F4F4F", darkTurquoise: "#00CED1", darkViolet: "#9400D3", deepPink: "#FF1493", deepSkyBlue: "#00BFFF", dimGray: "#696969", dodgerBlue: "#1E90FF", firebrick: "#B22222", floralWhite: "#FFFAF0", forestGreen: "#228B22", fuchsia: "#FF00FF", gainsboro: "#DCDCDC", ghostWhite: "#F8F8FF", gold: "#FFD700", goldenrod: "#DAA520", gray: "#808080", green: "#008000", greenYellow: "#ADFF2F", honeydew: "#F0FFF0", hotPink: "#FF69B4", indianRed: "#CD5C5C", indigo: "#4B0082", ivory: "#FFFFF0", khaki: "#F0E68C", lavender: "#E6E6FA", lavenderBlush: "#FFF0F5", lawnGreen: "#7CFC00", lemonChiffon: "#FFFACD", lightBlue: "#ADD8E6", lightCoral: "#F080FF", lightCyan: "#E0FFFF", lightGoldenrodYellow: "#FAFAD2", lightGray: "#D3D3D3", lightGreen: "#90EE90", lightPink: "#FFB6C1", lightSalmon: "#FFA07A", lightSeaGreen: "#20B2AA", lightSkyBlue: "#87CEFA", lightSlateGray: "#778899", lightSteelBlue: "#B0C4DE", lightYellow: "#FFFFE0", lime: "#00FF00", limeGreen: "#32CD32", linen: "#FAF0E6", magenta: "#FF00FF", maroon: "#800000", mediumAquamarine: "#66CDAA", mediumBlue: "#0000CD", mediumOrchid: "#BA55D3", mediumPurple: "#9370DB", mediumSeaGreen: "#3CB371", mediumSlateBlue: "#7B68EE", mediumSpringGreen: "#00FA9A", mediumTurquoise: "#48D1CC", mediumVioletRed: "#C71585", midnightBlue: "#191970", mintCream: "#F5FFFA", mistyRose: "#FFE4E1", moccasin: "#FFE4B5", navajoWhite: "#FFDEAD", navy: "#000080", oldLace: "#FDF5E6", olive: "#808000", oliveDrab: "#6B8E23", orange: "#FFA500", orangeRed: "#FF4500", orchid: "#DA70D6", paleGoldenrod: "#EEE8AA", paleGreen: "#98FB98", paleTurquoise: "#AFEEEE", paleVioletRed: "#DB7093", papayaWhip: "#FFEFD5", peachPuff: "#FFDAB9", peru: "#CD853F", pink: "#FFC0CB", plum: "#DDA0DD", powderBlue: "#B0E0E6", purple: "#800080", red: "#FF0000", rosyBrown: "#BC8F8F", royalBlue: "#4169E1", saddleBrown: "#8B4513", salmon: "#FA8072", sandyBrown: "#F4A460", seaGreen: "#2E8B57", seaShell: "#FFF5EE", sienna: "#A0522D", silver: "#C0C0C0", skyBlue: "#87CEEB", slateBlue: "#6A5ACD", slateGray: "#708090", snow: "#FFFAFA", springGreen: "#00FF7F", steelBlue: "#4682B4", tan: "#D2B48C", teal: "#008080", thistle: "#D8BFD8", tomato: "#FF6347", turquoise: "#40E0D0", violet: "#EE82EE", wheat: "#F5DEB3", white: "#FFFFFF", whiteSmoke: "#F5F5F5", yellow: "#FFFF00", yellowGreen: "#9ACD32" };
"use strict"; var Keys = { none: 0, back: 8, tab: 9, enter: 13, pause: 19, escape: 27, space: 32, pageUp: 33, pageDown: 34, end: 35, home: 36, left: 37, up: 38, right: 39, down: 40, insert: 45, del: 46, d0: 48, d1: 49, d2: 50, d3: 51, d4: 52, d5: 53, d6: 54, d7: 55, d8: 56, d9: 57, A: 65, B: 66, C: 67, D: 68, E: 69, F: 70, G: 71, H: 72, I: 73, J: 74, K: 75, L: 76, M: 77, N: 78, O: 79, P: 80, Q: 81, R: 82, S: 83, T: 84, U: 85, V: 86, W: 87, X: 88, Y: 89, Z: 90, multiply: 42, add: 43, subtract: 45, decimal: 46, divide: 47 };
"use strict"; var sprites = {}; var sounds = {}; Game.loadAssets = function () { var loadSprite = function (sprite) { return Game.loadSprite("assets/sprites/" + sprite); }; var loadSound = function (sound) { return new Audio("assets/sounds/" + sound); }; sprites.mainMenuBackground = loadSprite("main_menu_background.png"); sprites.background = loadSprite("spr_background4.png"); sprites.ball = loadSprite("spr_ball2.png"); sprites.redBall = loadSprite("spr_redBall2.png"); sprites.yellowBall = loadSprite("spr_yellowBall2.png"); sprites.blackBall = loadSprite("spr_blackBall2.png"); sprites.stick = loadSprite("spr_stick.png"); sprites.twoPlayersButton = loadSprite("2_players_button.png"); sprites.twoPlayersButtonHover = loadSprite("2_players_button_hover.png"); sprites.onePlayersButton = loadSprite("1_player_button.png"); sprites.onePlayersButtonHover = loadSprite("1_player_button_hover.png"); sprites.muteButton = loadSprite("mute_button.png"); sprites.muteButtonHover = loadSprite("mute_button_hover.png"); sprites.muteButtonPressed = loadSprite("mute_button_pressed.png"); sprites.muteButtonPressedHover = loadSprite("mute_button_pressed_hover.png"); sprites.easyButton = loadSprite("easy_button.png"); sprites.easyButtonHover = loadSprite("easy_button_hover.png"); sprites.mediumButton = loadSprite("medium_button.png"); sprites.mediumButtonHover = loadSprite("medium_button_hover.png"); sprites.hardButton = loadSprite("hard_button.png"); sprites.hardButtonHover = loadSprite("hard_button_hover.png"); sprites.backButton = loadSprite("back_button.png"); sprites.backButtonHover = loadSprite("back_button_hover.png"); sprites.continueButton = loadSprite("continue_button.png"); sprites.continueButtonHover = loadSprite("continue_button_hover.png"); sprites.insaneButton = loadSprite("insane_button.png"); sprites.insaneButtonHover = loadSprite("insane_button_hover.png"); sprites.aboutButton = loadSprite("about_button.png"); sprites.aboutButtonHover = loadSprite("about_button_hover.png"); sprites.controls = loadSprite("controls.png"); sounds.side = loadSound("Side.wav"); sounds.ballsCollide = loadSound("BallsCollide.wav"); sounds.strike = loadSound("Strike.wav"); sounds.hole = loadSound("Hole.wav"); // Bossa Antigua Kevin MacLeod (incompetech.com) // Licensed under Creative Commons: By Attribution 3.0 License // http://creativecommons.org/licenses/by/3.0/ sounds.jazzTune = loadSound("Bossa Antigua.mp3"); } sounds.fadeOut = function(sound) { var fadeAudio = setInterval(function () { if(GAME_STOPPED) return; // Only fade if past the fade out point or not at zero already if ((sound.volume >= 0.05)) { sound.volume -= 0.05; } else{ sound.pause(); clearInterval(fadeAudio); } }, 400); }
"use strict"; function Canvas2D_Singleton() { this._canvas = null; this._canvasContext = null; this._canvasOffset = Vector2.zero; } Object.defineProperty(Canvas2D_Singleton.prototype, "offset", { get: function () { return this._canvasOffset; } }); Object.defineProperty(Canvas2D_Singleton.prototype, "scale", { get: function () { return new Vector2(this._canvas.width / Game.size.x, this._canvas.height / Game.size.y); } }); Canvas2D_Singleton.prototype.initialize = function (divName, canvasName) { this._canvas = document.getElementById(canvasName); this._div = document.getElementById(divName); if (this._canvas.getContext) this._canvasContext = this._canvas.getContext('2d'); else { alert('Your browser is not HTML5 compatible.!'); return; } window.onresize = Canvas2D_Singleton.prototype.resize; this.resize(); }; Canvas2D_Singleton.prototype.clear = function () { this._canvasContext.clearRect(0, 0, this._canvas.width, this._canvas.height); }; Canvas2D_Singleton.prototype.resize = function () { var gameCanvas = Canvas2D._canvas; var gameArea = Canvas2D._div; var widthToHeight = Game.size.x / Game.size.y; var newWidth = window.innerWidth; var newHeight = window.innerHeight; var newWidthToHeight = newWidth / newHeight; if (newWidthToHeight > widthToHeight) { newWidth = newHeight * widthToHeight; } else { newHeight = newWidth / widthToHeight; } gameArea.style.width = newWidth + 'px'; gameArea.style.height = newHeight + 'px'; gameArea.style.marginTop = (window.innerHeight - newHeight) / 2 + 'px'; gameArea.style.marginLeft = (window.innerWidth - newWidth) / 2 + 'px'; gameArea.style.marginBottom = (window.innerHeight - newHeight) / 2 + 'px'; gameArea.style.marginRight = (window.innerWidth - newWidth) / 2 + 'px'; gameCanvas.width = newWidth; gameCanvas.height = newHeight; var offset = Vector2.zero; if (gameCanvas.offsetParent) { do { offset.x += gameCanvas.offsetLeft; offset.y += gameCanvas.offsetTop; } while ((gameCanvas = gameCanvas.offsetParent)); } Canvas2D._canvasOffset = offset; }; Canvas2D_Singleton.prototype.drawImage = function (sprite, position, rotation, scale, origin) { var canvasScale = this.scale; position = typeof position !== 'undefined' ? position : Vector2.zero; rotation = typeof rotation !== 'undefined' ? rotation : 0; scale = typeof scale !== 'undefined' ? scale : 1; origin = typeof origin !== 'undefined' ? origin : Vector2.zero; this._canvasContext.save(); this._canvasContext.scale(canvasScale.x, canvasScale.y); this._canvasContext.translate(position.x, position.y); this._canvasContext.rotate(rotation); this._canvasContext.drawImage(sprite, 0, 0, sprite.width, sprite.height, -origin.x * scale, -origin.y * scale, sprite.width * scale, sprite.height * scale); this._canvasContext.restore(); }; Canvas2D_Singleton.prototype.drawText = function (text, position, origin, color, textAlign, fontname, fontsize) { var canvasScale = this.scale; position = typeof position !== 'undefined' ? position : Vector2.zero; origin = typeof origin !== 'undefined' ? origin : Vector2.zero; color = typeof color !== 'undefined' ? color : Color.black; textAlign = typeof textAlign !== 'undefined' ? textAlign : "top"; fontname = typeof fontname !== 'undefined' ? fontname : "sans-serif"; fontsize = typeof fontsize !== 'undefined' ? fontsize : "20px"; this._canvasContext.save(); this._canvasContext.scale(canvasScale.x, canvasScale.y); this._canvasContext.translate(position.x - origin.x, position.y - origin.y); this._canvasContext.textBaseline = 'top'; this._canvasContext.font = fontsize + " " + fontname; this._canvasContext.fillStyle = color.toString(); this._canvasContext.textAlign = textAlign; this._canvasContext.fillText(text, 0, 0); this._canvasContext.restore(); }; var Canvas2D = new Canvas2D_Singleton();
"use strict"; var requestAnimationFrame = (function () { return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function (callback) { window.setTimeout(callback, 1000 / 60); }; })(); function Game_Singleton() { this.size = undefined; this.spritesStillLoading = 0; this.gameWorld = undefined; this.sound = true; this.mainMenu = new Menu(); } Game_Singleton.prototype.start = function (divName, canvasName, x, y) { this.size = new Vector2(x,y); Canvas2D.initialize(divName, canvasName); this.loadAssets(); this.assetLoadingLoop(); }; Game_Singleton.prototype.initialize = function () { this.gameWorld = new GameWorld(); this.policy = new GamePolicy(); this.initMenus(); AI.init(this.gameWorld, this.policy); }; Game_Singleton.prototype.initMenus = function(inGame){ let labels = generateMainMenuLabels("Classic 8-Ball"); let buttons = generateMainMenuButtons(inGame); this.mainMenu.init ( sprites.mainMenuBackground, labels, buttons, sounds.jazzTune ); } Game_Singleton.prototype.loadSprite = function (imageName) { console.log("Loading sprite: " + imageName); var image = new Image(); image.src = imageName; this.spritesStillLoading += 1; image.onload = function () { Game.spritesStillLoading -= 1; }; return image; }; Game_Singleton.prototype.assetLoadingLoop = function () { if (!this.spritesStillLoading > 0) requestAnimationFrame(Game.assetLoadingLoop); else { Game.initialize(); requestAnimationFrame(this.mainMenu.load.bind(this.mainMenu)); } }; Game_Singleton.prototype.handleInput = function(){ if(Keyboard.down(Keys.escape)){ GAME_STOPPED = true; Game.initMenus(true); requestAnimationFrame(Game.mainMenu.load.bind(this.mainMenu)); } } Game_Singleton.prototype.startNewGame = function(){ Canvas2D._canvas.style.cursor = "auto"; Game.gameWorld = new GameWorld(); Game.policy = new GamePolicy(); Canvas2D.clear(); Canvas2D.drawImage( sprites.controls, new Vector2(Game.size.x/2,Game.size.y/2), 0, 1, new Vector2(sprites.controls.width/2,sprites.controls.height/2) ); setTimeout(()=>{ AI.init(Game.gameWorld, Game.policy); if(AI_ON && AI_PLAYER_NUM == 0){ AI.startSession(); } Game.mainLoop(); },5000); } Game_Singleton.prototype.continueGame = function(){ Canvas2D._canvas.style.cursor = "auto"; requestAnimationFrame(Game.mainLoop); } Game_Singleton.prototype.mainLoop = function () { if(DISPLAY && !GAME_STOPPED){ Game.gameWorld.handleInput(DELTA); Game.gameWorld.update(DELTA); Canvas2D.clear(); Game.gameWorld.draw(); Mouse.reset(); Game.handleInput(); requestAnimationFrame(Game.mainLoop); } }; var Game = new Game_Singleton();
function GamePolicy(){ this.turn = 0; this.firstCollision = true; let player1TotalScore = new Score(new Vector2(Game.size.x/2 - 75,Game.size.y/2 - 45)); let player2TotalScore = new Score(new Vector2(Game.size.x/2 + 75,Game.size.y/2 - 45)); let player1MatchScore = new Score(new Vector2(Game.size.x/2 - 280,108)); let player2MatchScore = new Score(new Vector2(Game.size.x/2 + 230,108)); this.players = [new Player(player1MatchScore,player1TotalScore), new Player(player2MatchScore,player2TotalScore)]; this.foul = false; this.scored = false; this.won = false; this.turnPlayed = false; this.validBallsInsertedOnTurn = 0; this.leftBorderX = BORDER_SIZE; this.rightBorderX = Game.size.x - BORDER_SIZE; this.topBorderY = BORDER_SIZE; this.bottomBorderY = Game.size.y - BORDER_SIZE; this.topCenterHolePos = new Vector2(750,32); this.bottomCenterHolePos = new Vector2(750,794); this.topLeftHolePos = new Vector2(62,62); this.topRightHolePos = new Vector2(1435,62); this.bottomLeftHolePos = new Vector2(62,762) this.bottomRightHolePos = new Vector2(1435,762); } GamePolicy.prototype.reset = function(){ this.turn = 0; this.players[0].matchScore.value = 0; this.players[0].color = undefined; this.players[1].matchScore.value = 0; this.players[1].color = undefined; this.foul = false; this.scored = false; this.turnPlayed = false; this.won = false; this.firstCollision = true; this.validBallsInsertedOnTurn = 0; } GamePolicy.prototype.drawScores = function(){ Canvas2D.drawText("PLAYER " + (this.turn+1), new Vector2(Game.size.x/2 + 40,200), new Vector2(150,0), "#096834", "top", "Impact", "70px"); this.players[0].totalScore.draw(); this.players[1].totalScore.draw(); this.players[0].matchScore.drawLines(this.players[0].color); this.players[1].matchScore.drawLines(this.players[1].color); } GamePolicy.prototype.checkColisionValidity = function(ball1,ball2){ let currentPlayerColor = this.players[this.turn].color; if(this.players[this.turn].matchScore.value == 7 && (ball1.color == Color.black || ball2.color == Color.black)){ this.firstCollision = false; return; } if(!this.firstCollision) return; if(currentPlayerColor == undefined){ this.firstCollision = false; return; } if(ball1.color == Color.white){ if(ball2.color != currentPlayerColor){ this.foul = true; } this.firstCollision = false; } if(ball2.color == Color.white){ if(ball1.color != currentPlayerColor){ this.foul = true; } this.firstCollision = false; } } GamePolicy.prototype.handleBallInHole = function(ball){ setTimeout(function(){ball.out();}, 100); let currentPlayer = this.players[this.turn]; let secondPlayer = this.players[(this.turn+1)%2]; if(currentPlayer.color == undefined){ if(ball.color === Color.red){ currentPlayer.color = Color.red; secondPlayer.color = Color.yellow; } else if(ball.color === Color.yellow){ currentPlayer.color = Color.yellow; secondPlayer.color = Color.red; } else if(ball.color === Color.black){ this.won = true; this.foul = true; } else if(ball.color === Color.white){ this.foul = true; } } if(currentPlayer.color === ball.color){ currentPlayer.matchScore.increment(); this.scored = true; this.validBallsInsertedOnTurn++; } else if(ball.color === Color.white){ if(currentPlayer.color != undefined){ this.foul = true; let ballsSet = Game.gameWorld.getBallsSetByColor(currentPlayer.color); let allBallsInHole = true; for (var i = 0 ; i < ballsSet.length; i++){ if(!ballsSet[i].inHole){ allBallsInHole = false; } } if(allBallsInHole){ this.won = true; } } } else if(ball.color === Color.black){ if(currentPlayer.color != undefined){ let ballsSet = Game.gameWorld.getBallsSetByColor(currentPlayer.color); for (var i = 0 ; i < ballsSet.length; i++){ if(!ballsSet[i].inHole){ this.foul = true; } } this.won = true; } } else{ secondPlayer.matchScore.increment(); this.foul = true; } } GamePolicy.prototype.switchTurns = function(){ this.turn++; this.turn%=2; } GamePolicy.prototype.updateTurnOutcome = function(){ if(!this.turnPlayed){ return; } if(this.firstCollision == true){ this.foul = true; } if(this.won){ if(!this.foul){ this.players[this.turn].totalScore.increment(); if(AI.finishedSession){ this.reset() setTimeout(function(){Game.gameWorld.reset(); }, 1000); } } else{ this.players[(this.turn+1)%2].totalScore.increment(); if(AI.finishedSession){ this.reset(); setTimeout(function(){Game.gameWorld.reset(); }, 1000); } } return; } if(!this.scored || this.foul) this.switchTurns(); this.scored = false; this.turnPlayed = false; this.firstCollision = true; this.validBallsInsertedOnTurn = 0; setTimeout(function(){Game.gameWorld.whiteBall.visible=true;}, 200); if(AI_ON && this.turn === AI_PLAYER_NUM && AI.finishedSession){ AI.startSession(); } } GamePolicy.prototype.handleFoul = function(){ if(!Mouse.left.down){ Game.gameWorld.whiteBall.position = Mouse.position; } } GamePolicy.prototype.isXOutsideLeftBorder = function(pos, origin){ return (pos.x - origin.x) < this.leftBorderX; } GamePolicy.prototype.isXOutsideRightBorder = function(pos, origin){ return (pos.x + origin.x) > this.rightBorderX; } GamePolicy.prototype.isYOutsideTopBorder = function(pos, origin){ return (pos.y - origin.y) < this.topBorderY; } GamePolicy.prototype.isYOutsideBottomBorder = function(pos , origin){ return (pos.y + origin.y) > this.bottomBorderY; } GamePolicy.prototype.isOutsideBorder = function(pos,origin){ return this.isXOutsideLeftBorder(pos,origin) || this.isXOutsideRightBorder(pos,origin) || this.isYOutsideTopBorder(pos, origin) || this.isYOutsideBottomBorder(pos , origin); } GamePolicy.prototype.isInsideTopLeftHole = function(pos){ return this.topLeftHolePos.distanceFrom(pos) < HOLE_RADIUS; } GamePolicy.prototype.isInsideTopRightHole = function(pos){ return this.topRightHolePos.distanceFrom(pos) < HOLE_RADIUS; } GamePolicy.prototype.isInsideBottomLeftHole = function(pos){ return this.bottomLeftHolePos.distanceFrom(pos) < HOLE_RADIUS; } GamePolicy.prototype.isInsideBottomRightHole = function(pos){ return this.bottomRightHolePos.distanceFrom(pos) < HOLE_RADIUS; } GamePolicy.prototype.isInsideTopCenterHole = function(pos){ return this.topCenterHolePos.distanceFrom(pos) < (HOLE_RADIUS + 6); } GamePolicy.prototype.isInsideBottomCenterHole = function(pos){ return this.bottomCenterHolePos.distanceFrom(pos) < (HOLE_RADIUS + 6); } GamePolicy.prototype.isInsideHole = function(pos){ return this.isInsideTopLeftHole(pos) || this.isInsideTopRightHole(pos) || this.isInsideBottomLeftHole(pos) || this.isInsideBottomRightHole(pos) || this.isInsideTopCenterHole(pos) || this.isInsideBottomCenterHole(pos); } GamePolicy.prototype.initiateState = function(policyState){ this.turn = policyState.turn; this.firstCollision = policyState.firstCollision; this.foul = policyState.foul; this.scored = policyState.scored; this.won = policyState.won; this.turnPlayed = policyState.turnPlayed; this.validBallsInsertedOnTurn = policyState.validBallsInsertedOnTurn; this.players[0].totalScore.value = policyState.players[0].totalScore.value; this.players[1].totalScore.value = policyState.players[1].totalScore.value; this.players[0].matchScore.value = policyState.players[0].matchScore.value; this.players[0].color = policyState.players[0].color; this.players[1].matchScore.value = policyState.players[1].matchScore.value; this.players[1].color = policyState.players[1].color; }
"use strict"; function GameWorld() { this.whiteBallStartingPosition = new Vector2(413,413); this.redBalls = [ new Ball(new Vector2(1056,433),Color.red),//3 new Ball(new Vector2(1090,374),Color.red),//4 new Ball(new Vector2(1126,393),Color.red),//8 new Ball(new Vector2(1126,472),Color.red),//10; new Ball(new Vector2(1162,335),Color.red),//11 new Ball(new Vector2(1162,374),Color.red),//12 new Ball(new Vector2(1162,452),Color.red)//14 ] this.yellowBalls = [ new Ball(new Vector2(1022,413),Color.yellow),//1 new Ball(new Vector2(1056,393),Color.yellow),//2 new Ball(new Vector2(1090,452),Color.yellow),//6 new Ball(new Vector2(1126,354),Color.yellow),//7 new Ball(new Vector2(1126,433),Color.yellow),//9 new Ball(new Vector2(1162,413),Color.yellow),//13 new Ball(new Vector2(1162,491),Color.yellow)//15 ]; this.whiteBall = new Ball(new Vector2(413,413),Color.white); this.blackBall = new Ball(new Vector2(1090,413),Color.black); this.balls = [ this.yellowBalls[0], this.yellowBalls[1], this.redBalls[0], this.redBalls[1], this.blackBall, this.yellowBalls[2], this.yellowBalls[3], this.redBalls[2], this.yellowBalls[4], this.redBalls[3], this.redBalls[4], this.redBalls[5], this.yellowBalls[5], this.redBalls[6], this.yellowBalls[6], this.whiteBall] this.stick = new Stick({ x : 413, y : 413 }); this.gameOver = false; } GameWorld.prototype.getBallsSetByColor = function(color){ if(color === Color.red){ return this.redBalls; } if(color === Color.yellow){ return this.yellowBalls; } if(color === Color.white){ return this.whiteBall; } if(color === Color.black){ return this.blackBall; } } GameWorld.prototype.handleInput = function (delta) { this.stick.handleInput(delta); }; GameWorld.prototype.update = function (delta) { this.stick.update(delta); for (var i = 0 ; i < this.balls.length; i++){ for(var j = i + 1 ; j < this.balls.length ; j++){ this.handleCollision(this.balls[i], this.balls[j], delta); } } for (var i = 0 ; i < this.balls.length; i++) { this.balls[i].update(delta); } if(!this.ballsMoving() && AI.finishedSession){ Game.policy.updateTurnOutcome(); if(Game.policy.foul){ this.ballInHand(); } } }; GameWorld.prototype.ballInHand = function(){ if(AI_ON && Game.policy.turn === AI_PLAYER_NUM){ return; } KEYBOARD_INPUT_ON = false; this.stick.visible = false; if(!Mouse.left.down){ this.whiteBall.position = Mouse.position; } else{ let ballsOverlap = this.whiteBallOverlapsBalls(); if(!Game.policy.isOutsideBorder(Mouse.position,this.whiteBall.origin) && !Game.policy.isInsideHole(Mouse.position) && !ballsOverlap){ KEYBOARD_INPUT_ON = true; Keyboard.reset(); Mouse.reset(); this.whiteBall.position = Mouse.position; this.whiteBall.inHole = false; Game.policy.foul = false; this.stick.position = this.whiteBall.position; this.stick.visible = true; } } } GameWorld.prototype.whiteBallOverlapsBalls = function(){ let ballsOverlap = false; for (var i = 0 ; i < this.balls.length; i++) { if(this.whiteBall !== this.balls[i]){ if(this.whiteBall.position.distanceFrom(this.balls[i].position)<BALL_SIZE){ ballsOverlap = true; } } } return ballsOverlap; } GameWorld.prototype.ballsMoving = function(){ var ballsMoving = false; for (var i = 0 ; i < this.balls.length; i++) { if(this.balls[i].moving){ ballsMoving = true; } } return ballsMoving; } GameWorld.prototype.handleCollision = function(ball1, ball2, delta){ if(ball1.inHole || ball2.inHole) return; if(!ball1.moving && !ball2.moving) return; var ball1NewPos = ball1.position.add(ball1.velocity.multiply(delta)); var ball2NewPos = ball2.position.add(ball2.velocity.multiply(delta)); var dist = ball1NewPos.distanceFrom(ball2NewPos); if(dist<BALL_SIZE){ Game.policy.checkColisionValidity(ball1, ball2); var power = (Math.abs(ball1.velocity.x) + Math.abs(ball1.velocity.y)) + (Math.abs(ball2.velocity.x) + Math.abs(ball2.velocity.y)); power = power * 0.00482; if(Game.sound && SOUND_ON){ var ballsCollide = sounds.ballsCollide.cloneNode(true); ballsCollide.volume = (power/(20))<1?(power/(20)):1; ballsCollide.play(); } var opposite = ball1.position.y - ball2.position.y; var adjacent = ball1.position.x - ball2.position.x; var rotation = Math.atan2(opposite, adjacent); ball1.moving = true; ball2.moving = true; var velocity2 = new Vector2(90*Math.cos(rotation + Math.PI)*power,90*Math.sin(rotation + Math.PI)*power); ball2.velocity = ball2.velocity.addTo(velocity2); ball2.velocity.multiplyWith(0.97); var velocity1 = new Vector2(90*Math.cos(rotation)*power,90*Math.sin(rotation)*power); ball1.velocity = ball1.velocity.addTo(velocity1); ball1.velocity.multiplyWith(0.97); } } GameWorld.prototype.draw = function () { Canvas2D.drawImage(sprites.background); Game.policy.drawScores(); for (var i = 0; i < this.balls.length; i++) { this.balls[i].draw(); } this.stick.draw(); }; GameWorld.prototype.reset = function () { this.gameOver = false; for (var i = 0; i < this.balls.length; i++) { this.balls[i].reset(); } this.stick.reset(); if(AI_ON && AI_PLAYER_NUM === 0){ AI.startSession(); } }; GameWorld.prototype.initiateState = function(balls){ for (var i = 0; i < this.balls.length; i++) { this.balls[i].position.x = balls[i].position.x; this.balls[i].position.y = balls[i].position.y; this.balls[i].visible = balls[i].visible; this.balls[i].inHole = balls[i].inHole; } this.stick.position = this.whiteBall.position; }
const LOG = false; const BALL_SIZE = 38; const BORDER_SIZE = 57; const HOLE_RADIUS = 46; const DELTA = 1/100; let DISPLAY = true; let SOUND_ON = true; let GAME_STOPPED = true; let KEYBOARD_INPUT_ON = true; let TRAIN_ITER = 100; let AI_ON = true; let AI_PLAYER_NUM = 1; let DISPLAY_TRAINING = false;
We evaluated the performance of Llama 3.1 vs GPT-4 models on over 150 benchmark datasets…
The manufacturing industry is undergoing a significant transformation with the advent of Industrial IoT Solutions.…
If you're reading this, you must have heard the buzz about ChatGPT and its incredible…
How to Use ChatGPT in Cybersecurity If you're a cybersecurity geek, you've probably heard about…
Introduction In the dynamic world of cryptocurrencies, staying informed about the latest market trends is…
The Events Calendar Widgets for Elementor has become easiest solution for managing events on WordPress…