{"id":3077,"date":"2022-09-12T01:24:32","date_gmt":"2022-09-11T20:24:32","guid":{"rendered":"https:\/\/www.edopedia.com\/blog\/?p=3077"},"modified":"2022-09-12T01:25:32","modified_gmt":"2022-09-11T20:25:32","slug":"make-8-ball-pool-multiplayer-billiards-game-using-javascript","status":"publish","type":"post","link":"https:\/\/www.edopedia.com\/blog\/make-8-ball-pool-multiplayer-billiards-game-using-javascript\/","title":{"rendered":"Make 8 Ball POOL Multiplayer Billiards Game Using JavaScript"},"content":{"rendered":"\n<p>In this tutorial, I will teach you how to build an <strong>8 Ball Pool Multiplayer Billiards Game Using JavaScript<\/strong>. The complete source code of the <strong>JavaScript 8 Ball Pool Game<\/strong> is given in this guide.<\/p>\n\n\n\n<p>I have also added <strong>Live Demo<\/strong> and <strong>Download<\/strong> buttons at the end of this tutorial, so you can easily <strong>download the code<\/strong> of this <strong>8 Ball Pool game<\/strong> 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.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Features<\/h2>\n\n\n\n<ul class=\"wp-block-list\"><li>Player vs Player match<\/li><li>Player vs Computer match<\/li><li>Artificial Intelligence (AI) with various difficulty levels.<\/li><li>Aim by moving the mouse.<\/li><li>Left click: shoot.<\/li><li>Increase\/Decrease shot power.<\/li><\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Folder Structure<\/h2>\n\n\n\n<ul class=\"wp-block-list\"><li>assets<ul><li><strong>Note:-<\/strong> <em>All the sound and image files will go here. You can download the complete project including all the asset files at the end of this article.<\/em><\/li><\/ul><\/li><li>css<ul><li>game-layout.css<\/li><\/ul><\/li><li>script<ul><li>AI<ul><li>AIPolicy.js<\/li><li>AITrainer.js<\/li><li>Opponent.js<\/li><\/ul><\/li><li>game_objects<ul><li>Ball.js<\/li><li>Player.js<\/li><li>Score.js<\/li><li>Stick.js<\/li><\/ul><\/li><li>geom<ul><li>Vector2.js<\/li><\/ul><\/li><li>input<ul><li>ButtonState.js<\/li><li>Keyboard.js<\/li><li>Mouse.js<\/li><\/ul><\/li><li>lib<ul><li>LAB.min.js<\/li><\/ul><\/li><li>menu<ul><li>Button.js<\/li><li>Label.js<\/li><li>MainMenu.js<\/li><li>Menu.js<\/li><\/ul><\/li><li>system<ul><li>Color.js<\/li><li>Keys.js<\/li><\/ul><\/li><li>Assets.js<\/li><li>Canvas2D.js<\/li><li>Game.js<\/li><li>GamePolicy.js<\/li><li>GameWorld.js<\/li><li>Global.js<\/li><\/ul><\/li><li>index.html<\/li><\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">JavaScript 8 Ball Pool Game Full Source Code<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">index.html<\/h3>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;htmlmixed&quot;,&quot;mime&quot;:&quot;text\/html&quot;,&quot;theme&quot;:&quot;material&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:true,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;HTML&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;html&quot;}\">&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n        &lt;meta http-equiv=&quot;content-type&quot; content=&quot;text\/html; charset=UTF8&quot;&gt;\n    &lt;title&gt;Classic Pool Game&lt;\/title&gt;\n    &lt;link rel=&quot;stylesheet&quot; type=&quot;text\/css&quot; href=&quot;css\/game-layout.css&quot;\/&gt;\n    &lt;link rel=&quot;shortcut icon&quot; type=&quot;image\/png&quot; href=&quot;assets\/sprites\/favicon.png&quot;\/&gt;\n    &lt;script src=&quot;script\/lib\/LAB.min.js&quot;&gt;&lt;\/script&gt;\n    &lt;script&gt;\n        $LAB\n                .script('script\/system\/Keys.js').wait()\n                .script('script\/system\/Color.js').wait()\n                .script('script\/geom\/Vector2.js').wait()\n                .script('script\/input\/ButtonState.js').wait()\n                .script('script\/input\/Keyboard.js').wait()\n                .script('script\/input\/Mouse.js').wait()\n                .script('script\/Global.js').wait()\n                .script('script\/Canvas2D.js').wait()\n                .script('script\/game_objects\/Score.js').wait()\n                .script('script\/game_objects\/Ball.js').wait()\n                .script('script\/game_objects\/Stick.js').wait()\n                .script('script\/menu\/Label.js').wait()\n                .script('script\/menu\/Button.js').wait()\n                .script('script\/menu\/Menu.js').wait()\n                .script('script\/menu\/MainMenu.js').wait()\n                .script('script\/AI\/Opponent.js').wait()\n                .script('script\/AI\/AIPolicy.js').wait()\n                .script('script\/AI\/AITrainer.js').wait()\n                .script('script\/game_objects\/Player.js').wait()\n                .script('script\/GamePolicy.js').wait()\n                .script('script\/GameWorld.js').wait()\n                .script('script\/Game.js').wait()\n                .script('script\/Assets.js').wait(function () {\n                    Game.start('gameArea','screen', 1500, 825);\n                });\n\n    &lt;\/script&gt;\n&lt;\/head&gt;\n\n&lt;body style = &quot;background-color:black&quot;&gt;\n&lt;div id=&quot;gameArea&quot;&gt;\n    &lt;canvas id=&quot;screen&quot; width=&quot;2000&quot; height=&quot;1000&quot;&gt;&lt;\/canvas&gt;\n&lt;\/div&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">css\/game-layout.css<\/h3>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;css&quot;,&quot;mime&quot;:&quot;text\/css&quot;,&quot;theme&quot;:&quot;material&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:true,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;CSS&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;css&quot;}\">html, body {\n    margin: 0;\n}\n\n\/*#screen{\n\tpadding-left: 0;\n    padding-right: 0;\n    margin-left: auto;\n    margin-right: auto;\n    display: block;\n}*\/<\/pre><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">script\/AI\/AIPolicy.js<\/h3>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;javascript&quot;,&quot;mime&quot;:&quot;text\/javascript&quot;,&quot;theme&quot;:&quot;material&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:true,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;JavaScript&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;js&quot;}\">function AIPolicy(){\n    \n}\n\nAIPolicy.prototype.evaluate = function(state, gamePolicy){\n\n    let evaluation = 1;\n\n    for (var i = 0 ; i &lt; state.balls.length; i++){\n        for(var j = i + 1 ; j &lt; state.balls.length ; j++){\n\n            let firstBall = state.balls[i];\n            let secondBall = state.balls[j];\n\n            if(firstBall === state.whiteBall || secondBall === state.whiteBall \n                || \n                firstBall.inHole || secondBall.inHole){\n                continue;\n            }\n            evaluation += firstBall.position.distanceFrom(secondBall.position);\n        }\n    }\n\n    evaluation = evaluation\/5800;\n\n    if(!gamePolicy.firstCollision){\n        evaluation+= 100;\n    }\n\n    evaluation += 2000 * gamePolicy.validBallsInsertedOnTurn;\n\n    gamePolicy.updateTurnOutcome();\n\n\n    if(gamePolicy.won){\n        if(!gamePolicy.foul){\n            evaluation += 10000;\n        }\n        else{\n            evaluation -= 10000;\n        }\n    }\n\n    if(gamePolicy.foul){\n        evaluation = evaluation - 3000;\n    }\n\n    return evaluation;\n}<\/pre><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">script\/AI\/AITrainer.js<\/h3>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;javascript&quot;,&quot;mime&quot;:&quot;text\/javascript&quot;,&quot;theme&quot;:&quot;material&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:true,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;JavaScript&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;js&quot;}\">function AITrainer(){\n\n    this.AIPolicy = new AIPolicy();\n\n}\n\nAITrainer.prototype.init = function(state, gamePolicy){\n\n    AI.opponents = [];\n    AI.currentOpponent = new Opponent();\n    AI.finishedSession = true;\n    AI.iteration = 0;\n\n    AI.bestOpponentIndex = 0;\n    AI.bestOpponentEval = 0;\n\n    if(gamePolicy.foul){\n        \/\/TO DO: Pick best position for the white ball.\n        state.whiteBall.position.x = 413;\n        state.whiteBall.position.y = 413;\n        state.whiteBall.inHole = false;\n        gamePolicy.foul = false;\n    }\n    AI.initialState = JSON.parse(JSON.stringify(state));\n    AI.initialGamePolicyState = JSON.parse(JSON.stringify(gamePolicy));\n\n    AI.state = state;\n    AI.gamePolicy = gamePolicy;\n\n}\n\nAITrainer.prototype.train = function(){\n\n    if(AI.iteration === TRAIN_ITER){\n        AI.finishedSession = true;\n        AI.playTurn();\n        return;\n    }\n\n    let ballsMoving = AI.state.ballsMoving();\n\n    if(!ballsMoving){\n\n        if(AI.iteration !== 0){\n            AI.currentOpponent.evaluation = AI.AIPolicy.evaluate(this.state, this.gamePolicy);\n\n            AI.opponents.push(JSON.parse(JSON.stringify(AI.currentOpponent)));\n\n            if(AI.currentOpponent.evaluation &gt; AI.bestOpponentEval){\n                AI.bestOpponentEval = AI.currentOpponent.evaluation;\n                AI.bestOpponentIndex =  AI.opponents.length - 1;\n            }\n\n            if(LOG){\n                console.log('-------------'+new Number(AI.iteration+1)+'--------------------');\n                console.log('Current evaluation: ' + AI.currentOpponent.evaluation);\n                console.log('Current power: ' + AI.currentOpponent.power);\n                console.log('Current rotation: ' + AI.currentOpponent.rotation);\n                console.log('---------------------------------');\n            }\n        }\n\n        AI.state.initiateState(AI.initialState.balls);\n        AI.gamePolicy.initiateState(AI.initialGamePolicyState);\n        AI.buildNewOpponent();\n        AI.simulate();\n    }\n\n}\n\nAITrainer.prototype.buildNewOpponent = function(){\n\n    if(AI.iteration % 10 === 0){\n        AI.currentOpponent = new Opponent();\n        AI.iteration++;\n        return;\n    }\n\n    let bestOpponent = AI.opponents[AI.bestOpponentIndex];\n\n    let newPower = bestOpponent.power;\n    newPower += + ((Math.random() * 30) - 15);\n    newPower = newPower &lt; 20 ? 20 : newPower;\n    newPower = newPower &gt; 75 ? 75 : newPower;\n\n    let newRotation = bestOpponent.rotation;\n\n    if(bestOpponent.evaluation &gt; 0){\n        newRotation += (1\/bestOpponent.evaluation)*(Math.random() * 2 * Math.PI - Math.PI)\n    }\n    else{\n        newRotation = (Math.random() * 2 * Math.PI - Math.PI);\n    }\n\n    AI.currentOpponent = new Opponent(newPower,newRotation);\n\n    AI.iteration++;\n\n}\n\nAITrainer.prototype.simulate = function(){\n    AI.state.stick.shoot(AI.currentOpponent.power, AI.currentOpponent.rotation);\n}\n\nAITrainer.prototype.playTurn = function(){\n\n    bestOpponent = AI.opponents[AI.bestOpponentIndex];\n    Game.gameWorld.stick.rotation = bestOpponent.rotation;\n    Game.gameWorld.stick.trackMouse = false;\n\n    setTimeout(() =&gt; {\n\n        Game.gameWorld.stick.visible = true;\n        Canvas2D.clear();\n        Game.gameWorld.draw();\n\n        Game.sound = true;\n        Game.gameWorld.initiateState(AI.initialState.balls);\n        Game.policy.initiateState(AI.initialGamePolicyState);\n\n        DISPLAY = true;\n        \n        requestAnimationFrame(Game.mainLoop);\n\n        Game.gameWorld.stick\n        .shoot(\n            bestOpponent.power, \n            bestOpponent.rotation\n        );\n        Game.gameWorld.stick.trackMouse = true;\n\n    }, 1000);\n}\n\nAITrainer.prototype.opponentTrainingLoop = function(){\n\n    Game.sound = false;\n    DISPLAY = false;\n\n    if(DISPLAY_TRAINING){\n        if(!AI.finishedSession){\n            AI.train();\n            Game.gameWorld.handleInput(DELTA);\n            Game.gameWorld.update(DELTA);\n            Canvas2D.clear();\n            Game.gameWorld.draw();\n            Mouse.reset();\n            setTimeout(AI.opponentTrainingLoop,0.00000000001);\n        }\n    }\n    else{\n        while(!AI.finishedSession){\n            AI.train();\n            Game.gameWorld.handleInput(DELTA);\n            Game.gameWorld.update(DELTA);\n            Mouse.reset();\n        }\n    }\n\n}\n\nAITrainer.prototype.startSession = function(){\n        setTimeout(\n            ()=&gt;{\n                Game.gameWorld.stick.visible = false;\n                Canvas2D.clear();\n                Game.gameWorld.draw();\n\n                AI.init(Game.gameWorld, Game.policy);\n                AI.finishedSession = false;\n                AI.opponentTrainingLoop();\n            },\n            1000\n        );\n}\n\nconst AI = new AITrainer();<\/pre><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">script\/AI\/Opponent.js<\/h3>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;javascript&quot;,&quot;mime&quot;:&quot;text\/javascript&quot;,&quot;theme&quot;:&quot;material&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:true,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;JavaScript&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;js&quot;}\">function Opponent(power, rotation){\n    this.power = power || (Math.random() * 75 + 1);\n    this.rotation = rotation || (Math.random()*6.283)-3.141;\n    this.evaluation = 0;\n}<\/pre><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">script\/game_objects\/Ball.js<\/h3>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;javascript&quot;,&quot;mime&quot;:&quot;text\/javascript&quot;,&quot;theme&quot;:&quot;material&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:true,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;JavaScript&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;js&quot;}\">&quot;use strict&quot;;\n\nfunction Ball(initPos,color){\n\tthis.initPos = initPos;\n    this.position = initPos.copy();\n    this.origin = new Vector2(25,25);\n    this.velocity = Vector2.zero;\n    this.color = color; \n    this.moving = false;\n    this.visible = true;\n    this.inHole = false;\n}\n\nObject.defineProperty(Ball.prototype, &quot;color&quot;,\n    {\n    \tget: function(){\n    \t\tif(this.sprite == sprites.redBall){\n    \t\t\treturn Color.red;\n    \t\t}\n    \t\telse if(this.sprite == sprites.yellowBall){\n    \t\t\treturn Color.yellow;\n    \t\t}\n\t\t\telse if(this.sprite == sprites.blackBall){\n    \t\t\treturn Color.black;\n    \t\t}\n    \t\telse{\n    \t\t\treturn Color.white;\n    \t\t}\n    \t},\n        set: function (value) {\n            if (value === Color.red){\n                this.sprite = sprites.redBall;\n            }\n            else if(value == Color.yellow){\n            \tthis.sprite = sprites.yellowBall;\n            }\n\t\t\telse if(value == Color.black){\n            \tthis.sprite = sprites.blackBall;\n            }\n            else{\n            \tthis.sprite = sprites.ball;\n            }\n        }\n    });\n\nBall.prototype.shoot = function(power, angle){\n    if(power &lt;= 0)\n        return;\n\n    this.moving = true;\n\n    this.velocity = calculateBallVelocity(power,angle);\n}\n\nvar calculateBallVelocity = function(power, angle){\n\n    return new Vector2(100*Math.cos(angle)*power,100*Math.sin(angle)*power);\n}\n\nBall.prototype.update = function(delta){\n\n    this.updatePosition(delta);\n\n    this.velocity.multiplyWith(0.98);\n\n\tif(this.moving &amp;&amp; Math.abs(this.velocity.x) &lt; 1 &amp;&amp; Math.abs(this.velocity.y) &lt; 1){\n        this.stop();\n    }\n}\n\nBall.prototype.updatePosition = function(delta){\n\n    if(!this.moving || this.inHole)\n        return;\n    var ball = this;\n    var newPos = this.position.add(this.velocity.multiply(delta));\n\n\n\tif(Game.policy.isInsideHole(newPos)){\n        if(Game.sound &amp;&amp; SOUND_ON){\n            var holeSound = sounds.hole.cloneNode(true);\n            holeSound.volume = 0.5;\n            holeSound.play();\n        }\n\t\tthis.position = newPos;\n        this.inHole = true;\n        setTimeout(function(){ball.visible=false;ball.velocity = Vector2.zero;}, 100);\n        Game.policy.handleBallInHole(this);\n\t\treturn;\n\t}\n\n    var collision = this.handleCollision(newPos);\n\n    if(collision){\n\t\tthis.velocity.multiplyWith(0.95);\n    }else{\n    \tthis.position = newPos;\n    }\n}\n\nBall.prototype.handleCollision = function(newPos){\n\n\tvar collision = false;\n\n\tif(Game.policy.isXOutsideLeftBorder(newPos, this.origin)){\n        this.velocity.x = -this.velocity.x;\n        this.position.x = Game.policy.leftBorderX + this.origin.x;\n        collision = true;\n    }\n    else if(Game.policy.isXOutsideRightBorder(newPos, this.origin)){\n        this.velocity.x = -this.velocity.x;\n        this.position.x = Game.policy.rightBorderX - this.origin.x;\n        collision = true;\n    }\n\n    if(Game.policy.isYOutsideTopBorder(newPos, this.origin)){\n        this.velocity.y = -this.velocity.y;\n        this.position.y = Game.policy.topBorderY + this.origin.y;\n        collision = true;\n    }\n    else if(Game.policy.isYOutsideBottomBorder(newPos, this.origin)){\n        this.velocity.y = -this.velocity.y;\n        this.position.y = Game.policy.bottomBorderY - this.origin.y;\n        collision = true;\n    }\n\n    return collision;\n}\n\nBall.prototype.stop = function(){\n\n    this.moving = false;\n    this.velocity = Vector2.zero;\n}\n\nBall.prototype.reset = function(){\n\tthis.inHole = false;\n\tthis.moving = false;\n\tthis.velocity = Vector2.zero;\n\tthis.position = this.initPos;\n\tthis.visible = true;\n}\n\nBall.prototype.out = function(){\n\n\tthis.position = new Vector2(0, 900);\n\tthis.visible = false;\n\tthis.inHole = true;\n\n}\n\nBall.prototype.draw = function () {\n    if(!this.visible)\n        return;\n\n\tCanvas2D.drawImage(this.sprite, this.position, 0, 1, new Vector2(25,25));\n};<\/pre><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">script\/game_objects\/Player.js<\/h3>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;javascript&quot;,&quot;mime&quot;:&quot;text\/javascript&quot;,&quot;theme&quot;:&quot;material&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:true,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;JavaScript&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;js&quot;}\">\nfunction Player(matchScore, totalScore){\n    this.color = undefined;\n    this.matchScore = matchScore;\n    this.totalScore = totalScore;\n}<\/pre><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">script\/game_objects\/Score.js<\/h3>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;javascript&quot;,&quot;mime&quot;:&quot;text\/javascript&quot;,&quot;theme&quot;:&quot;material&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:true,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;JavaScript&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;js&quot;}\">&quot;use strict&quot;;\n\nfunction Score(position){\n    this.position = position;\n    this.origin = new Vector2(47,82);\n    this.value = 0;\n}\n\nScore.prototype.reset = function(){\n    this.position = position;\n    this.origin = new Vector2(30,0);\n    this.value = 0;\n};\n\nScore.prototype.draw = function () {\n  Canvas2D.drawText(\n      this.value, \n      this.position, \n      this.origin, \n      &quot;#096834&quot;, \n      &quot;top&quot;, \n      &quot;Impact&quot;, \n      &quot;200px&quot;\n    );\n};\n\nScore.prototype.drawLines = function (color) {\n    \n    for(let i=0; i&lt;this.value; i++){\n\n        let pos = this.position.add(new Vector2(i*15,0));\n\n        Canvas2D.drawText(\n            &quot;I&quot;, \n            pos, \n            this.origin, \n            color, \n            &quot;top&quot;, \n            &quot;Arial&quot;, \n            &quot;20px&quot;\n        );\n\n    }\n  };\n\nScore.prototype.increment = function(){\n    this.value++;\n};<\/pre><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">script\/game_objects\/Stick.js<\/h3>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;javascript&quot;,&quot;mime&quot;:&quot;text\/javascript&quot;,&quot;theme&quot;:&quot;material&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:true,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;JavaScript&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;js&quot;}\">&quot;use strict&quot;;\n\nfunction Stick(position){\n    this.position = position;\n    this.origin = new Vector2(970,11);\n    this.shotOrigin = new Vector2(950,11);\n    this.shooting = false;\n    this.visible = true;\n    this.rotation = 0;\n    this.power = 0;\n    this.trackMouse = true;\n}\n\nStick.prototype.handleInput = function (delta) {\n\n    if(AI_ON &amp;&amp; Game.policy.turn === AI_PLAYER_NUM)\n      return;\n\n    if(Game.policy.turnPlayed)\n      return;\n\n    if(Keyboard.down(Keys.W) &amp;&amp; KEYBOARD_INPUT_ON){\n      if(this.power &lt; 75){\n        this.origin.x+=2;\n        this.power+=1.2;\n      }\n    }\n\n    if(Keyboard.down(Keys.S) &amp;&amp; KEYBOARD_INPUT_ON){\n      if(this.power&gt;0){\n        this.origin.x-=2;\n        this.power-=1.2;\n      }\n    }\n\n    else if (this.power&gt;0 &amp;&amp; Mouse.left.down){\n      var strike = sounds.strike.cloneNode(true);\n      strike.volume = (this.power\/(10))&lt;1?(this.power\/(10)):1;\n      strike.play();\n      Game.policy.turnPlayed = true;\n      this.shooting = true;\n      this.origin = this.shotOrigin.copy();\n\n      Game.gameWorld.whiteBall.shoot(this.power, this.rotation);\n      var stick = this;\n      setTimeout(function(){stick.visible = false;}, 500);\n    }\n    else if(this.trackMouse){\n      var opposite = Mouse.position.y - this.position.y;\n      var adjacent = Mouse.position.x - this.position.x;\n      this.rotation = Math.atan2(opposite, adjacent);\n    }\n};\n\nStick.prototype.shoot = function(power, rotation){\n  this.power = power;\n  this.rotation = rotation;\n\n  if(Game.sound &amp;&amp; SOUND_ON){\n    var strike = sounds.strike.cloneNode(true);\n    strike.volume = (this.power\/(10))&lt;1?(this.power\/(10)):1;\n    strike.play();\n  }\n  Game.policy.turnPlayed = true;\n  this.shooting = true;\n  this.origin = this.shotOrigin.copy();\n\n  Game.gameWorld.whiteBall.shoot(this.power, this.rotation);\n  var stick = this;\n  setTimeout(function(){stick.visible = false;}, 500);\n}\n\nStick.prototype.update = function(){\n  if(this.shooting &amp;&amp; !Game.gameWorld.whiteBall.moving)\n    this.reset();\n};\n\nStick.prototype.reset = function(){\n  this.position.x = Game.gameWorld.whiteBall.position.x;\n  this.position.y = Game.gameWorld.whiteBall.position.y;\n\tthis.origin = new Vector2(970,11);\n  this.shooting = false;\n  this.visible = true;\n\tthis.power = 0;\n};\n\nStick.prototype.draw = function () {\n  if(!this.visible)\n    return;\n  Canvas2D.drawImage(sprites.stick, this.position,this.rotation,1, this.origin);\n};<\/pre><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">script\/geom\/Vector2.js<\/h3>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;javascript&quot;,&quot;mime&quot;:&quot;text\/javascript&quot;,&quot;theme&quot;:&quot;material&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:true,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;JavaScript&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;js&quot;}\">&quot;use strict&quot;;\n\nfunction Vector2(x, y) {\n    this.x = typeof x !== 'undefined' ? x : 0;\n    this.y = typeof y !== 'undefined' ? y : 0;\n}\n\nObject.defineProperty(Vector2, &quot;zero&quot;,\n    {\n        get: function () {\n            return new Vector2();\n        }\n    });\n\nObject.defineProperty(Vector2.prototype, &quot;isZero&quot;,\n    {\n        get: function () {\n            return this.x === 0 &amp;&amp; this.y === 0;\n        }\n    });\n\nObject.defineProperty(Vector2.prototype, &quot;length&quot;,\n    {\n        get: function () {\n            return Math.sqrt(this.x * this.x + this.y * this.y);\n        }\n    });\n\nVector2.prototype.addTo = function (v) {\n    if (v.constructor === Vector2) {\n        this.x += v.x;\n        this.y += v.y;\n    }\n    else if (v.constructor === Number) {\n        this.x += v;\n        this.y += v;\n    }\n    return this;\n};\n\nVector2.prototype.add = function (v) {\n    var result = this.copy();\n    return result.addTo(v);\n};\n\nVector2.prototype.subtractFrom = function (v) {\n    if (v.constructor === Vector2) {\n        this.x -= v.x;\n        this.y -= v.y;\n    }\n    else if (v.constructor === Number) {\n        this.x -= v;\n        this.y -= v;\n    }\n    return this;\n};\n\nVector2.prototype.subtract = function (v) {\n    var result = this.copy();\n    return result.subtractFrom(v);\n};\n\nVector2.prototype.divideBy = function (v) {\n    if (v.constructor === Vector2) {\n        this.x \/= v.x;\n        this.y \/= v.y;\n    }\n    else if (v.constructor === Number) {\n        this.x \/= v;\n        this.y \/= v;\n    }\n    return this;\n};\n\nVector2.prototype.divide = function (v) {\n    var result = this.copy();\n    return result.divideBy(v);\n};\n\nVector2.prototype.multiplyWith = function (v) {\n    if (v.constructor === Vector2) {\n        this.x *= v.x;\n        this.y *= v.y;\n    }\n    else if (v.constructor === Number) {\n        this.x *= v;\n        this.y *= v;\n    }\n    return this;\n};\n\nVector2.prototype.multiply = function (v) {\n    var result = this.copy();\n    return result.multiplyWith(v);\n};\n\nVector2.prototype.toString = function () {\n    return &quot;(&quot; + this.x + &quot;, &quot; + this.y + &quot;)&quot;;\n};\n\nVector2.prototype.normalize = function () {\n    var length = this.length;\n    if (length === 0)\n        return;\n    this.divideBy(length);\n};\n\nVector2.prototype.copy = function () {\n    return new Vector2(this.x, this.y);\n};\n\nVector2.prototype.equals = function (obj) {\n    return this.x === obj.x &amp;&amp; this.y === obj.y;\n};\n\nVector2.prototype.distanceFrom = function(obj){\n    return Math.sqrt((this.x-obj.x)*(this.x-obj.x) + (this.y-obj.y)*(this.y-obj.y));\n}<\/pre><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">script\/input\/ButtonState.js<\/h3>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;javascript&quot;,&quot;mime&quot;:&quot;text\/javascript&quot;,&quot;theme&quot;:&quot;material&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:true,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;JavaScript&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;js&quot;}\">&quot;use strict&quot;;\n\nfunction ButtonState() {\n    this.down = false;\n    this.pressed = false;\n}<\/pre><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">script\/input\/Keyboard.js<\/h3>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;javascript&quot;,&quot;mime&quot;:&quot;text\/javascript&quot;,&quot;theme&quot;:&quot;material&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:true,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;JavaScript&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;js&quot;}\">&quot;use strict&quot;;\n\nfunction handleKeyDown(evt) {\n    var code = evt.keyCode;\n    if (code &lt; 0 || code &gt; 255)\n        return;\n    if (!Keyboard._keyStates[code].down)\n        Keyboard._keyStates[code].pressed = true;\n    Keyboard._keyStates[code].down = true;\n}\n\nfunction handleKeyUp(evt) {\n    var code = evt.keyCode;\n    if (code &lt; 0 || code &gt; 255)\n        return;\n    Keyboard._keyStates[code].down = false;\n}\n\nfunction Keyboard_Singleton() {\n    this._keyStates = [];\n    for (var i = 0; i &lt; 256; ++i)\n        this._keyStates.push(new ButtonState());\n    document.onkeydown = handleKeyDown;\n    document.onkeyup = handleKeyUp;\n}\n\nKeyboard_Singleton.prototype.reset = function () {\n    for (var i = 0; i &lt; 256; ++i)\n        this._keyStates[i].pressed = false;\n};\n\nKeyboard_Singleton.prototype.pressed = function (key) {\n    return this._keyStates[key].pressed;\n};\n\nKeyboard_Singleton.prototype.down = function (key) {\n    return this._keyStates[key].down;\n};\n\nvar Keyboard = new Keyboard_Singleton();<\/pre><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">script\/input\/Mouse.js<\/h3>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;javascript&quot;,&quot;mime&quot;:&quot;text\/javascript&quot;,&quot;theme&quot;:&quot;material&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:true,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;JavaScript&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;js&quot;}\">&quot;use strict&quot;;\n\nfunction handleMouseMove(evt) {\n    var canvasScale = Canvas2D.scale;\n    var canvasOffset = Canvas2D.offset;\n    var mx = (evt.pageX - canvasOffset.x) \/ canvasScale.x;\n    var my = (evt.pageY - canvasOffset.y) \/ canvasScale.y;\n    Mouse._position = new Vector2(mx, my);\n}\n\nfunction handleMouseDown(evt) {\n    handleMouseMove(evt);\n\n    if (evt.which === 1) {\n        if (!Mouse._left.down)\n            Mouse._left.pressed = true;\n        Mouse._left.down = true;\n    } else if (evt.which === 2) {\n        if (!Mouse._middle.down)\n            Mouse._middle.pressed = true;\n        Mouse._middle.down = true;\n    } else if (evt.which === 3) {\n        if (!Mouse._right.down)\n            Mouse._right.pressed = true;\n        Mouse._right.down = true;\n    }\n}\n\nfunction handleMouseUp(evt) {\n    handleMouseMove(evt);\n\n    if (evt.which === 1)\n        Mouse._left.down = false;\n    else if (evt.which === 2)\n        Mouse._middle.down = false;\n    else if (evt.which === 3)\n        Mouse._right.down = false;\n}\n\nfunction Mouse_Singleton() {\n    this._position = Vector2.zero;\n    this._left = new ButtonState();\n    this._middle = new ButtonState();\n    this._right = new ButtonState();\n    document.onmousemove = handleMouseMove;\n    document.onmousedown = handleMouseDown;\n    document.onmouseup = handleMouseUp;\n}\n\nObject.defineProperty(Mouse_Singleton.prototype, &quot;left&quot;,\n    {\n        get: function () {\n            return this._left;\n        }\n    });\n\nObject.defineProperty(Mouse_Singleton.prototype, &quot;middle&quot;,\n    {\n        get: function () {\n            return this._middle;\n        }\n    });\n\nObject.defineProperty(Mouse_Singleton.prototype, &quot;right&quot;,\n    {\n        get: function () {\n            return this._right;\n        }\n    });\n\nObject.defineProperty(Mouse_Singleton.prototype, &quot;position&quot;,\n    {\n        get: function () {\n            return this._position;\n        }\n    });\n\nMouse_Singleton.prototype.reset = function () {\n    this._left.pressed = false;\n    this._middle.pressed = false;\n    this._right.pressed = false;\n};\n\nMouse_Singleton.prototype.containsMouseDown = function (rect) {\n    return this._left.down &amp;&amp; rect.contains(this._position);\n};\n\nMouse_Singleton.prototype.containsMousePress = function (rect) {\n    return this._left.pressed &amp;&amp; rect.contains(this._position);\n};\n\nvar Mouse = new Mouse_Singleton();<\/pre><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">script\/lib\/LAB.min.js<\/h3>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;javascript&quot;,&quot;mime&quot;:&quot;text\/javascript&quot;,&quot;theme&quot;:&quot;material&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:true,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;JavaScript&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;js&quot;}\">\/*! LAB.js (LABjs :: Loading And Blocking JavaScript)\n    v2.0.3 (c) Kyle Simpson\n    MIT License\n*\/\n(function(o){var K=o.$LAB,y=&quot;UseLocalXHR&quot;,z=&quot;AlwaysPreserveOrder&quot;,u=&quot;AllowDuplicates&quot;,A=&quot;CacheBust&quot;,B=&quot;BasePath&quot;,C=\/^[^?#]*\\\/\/.exec(location.href)[0],D=\/^\\w+\\:\\\/\\\/\\\/?[^\\\/]+\/.exec(C)[0],i=document.head||document.getElementsByTagName(&quot;head&quot;),L=(o.opera&amp;&amp;Object.prototype.toString.call(o.opera)==&quot;[object Opera]&quot;)||(&quot;MozAppearance&quot;in document.documentElement.style),q=document.createElement(&quot;script&quot;),E=typeof q.preload==&quot;boolean&quot;,r=E||(q.readyState&amp;&amp;q.readyState==&quot;uninitialized&quot;),F=!r&amp;&amp;q.async===true,M=!r&amp;&amp;!F&amp;&amp;!L;function G(a){return Object.prototype.toString.call(a)==&quot;[object Function]&quot;}function H(a){return Object.prototype.toString.call(a)==&quot;[object Array]&quot;}function N(a,c){var b=\/^\\w+\\:\\\/\\\/\/;if(\/^\\\/\\\/\\\/?\/.test(a)){a=location.protocol+a}else if(!b.test(a)&amp;&amp;a.charAt(0)!=&quot;\/&quot;){a=(c||&quot;&quot;)+a}return b.test(a)?a:((a.charAt(0)==&quot;\/&quot;?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&lt;a.scripts.length;b++){if(a.scripts[b].ready&amp;&amp;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&amp;&amp;a.readyState!=&quot;complete&quot;&amp;&amp;a.readyState!=&quot;loaded&quot;)||c[b])return;a.onload=a.onreadystatechange=null;d()}}function I(a){a.ready=a.finished=true;for(var c=0;c&lt;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(&quot;item&quot;in i){if(!i[0]){setTimeout(arguments.callee,25);return}i=i[0]}a=document.createElement(&quot;script&quot;);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==&quot;loaded&quot;)g()}}a.src=c}else if(h&amp;&amp;c.indexOf(D)==0&amp;&amp;d[y]){b=new XMLHttpRequest();b.onreadystatechange=function(){if(b.readyState==4){b.onreadystatechange=function(){};e.text=b.responseText+&quot;\\n\/\/@ sourceURL=&quot;+c;g()}};b.open(&quot;GET&quot;,c);b.send()}else{a.type=&quot;text\/cache-script&quot;;t(a,e,&quot;ready&quot;,function(){i.removeChild(a);g()});a.src=c;i.insertBefore(a,i.firstChild)}}else if(F){a.async=false;t(a,e,&quot;finished&quot;,g);a.src=c;i.insertBefore(a,i.firstChild)}else{t(a,e,&quot;finished&quot;,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]=&quot;&quot;;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(&quot;script&quot;);if(c.type)d.type=c.type;if(c.charset)d.charset=c.charset;t(d,b,&quot;finished&quot;,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)?&quot;&amp;_&quot;:&quot;?_&quot;)+~~(Math.random()*1E9)+&quot;=&quot;):&quot;&quot;);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&lt;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&lt;c.scripts.length;b++){if(!c.scripts[b].finished)return}c.finished=true;x()}function x(){while(j&lt;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&lt;arguments.length;f++){(function(a,c){var b;if(!H(a)){c=[a]}for(var d=0;d&lt;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==&quot;string&quot;)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&amp;&amp;w));w=true;if(g[z])e.wait()}})(arguments[f],arguments[f])}return e},wait:function(){if(arguments.length&gt;0){for(var a=0;a&lt;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:&quot;script&quot;,args:[].slice.call(arguments)};return m},queueWait:function(){n[n.length]={type:&quot;wait&quot;,args:[].slice.call(arguments)};return m},runQueue:function(){var a=m,c=n.length,b=c,d;for(;--b&gt;=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&amp;&amp;document[a]){document.readyState=&quot;loading&quot;;document[a](c,b=function(){document.removeEventListener(c,b,false);document.readyState=&quot;complete&quot;},false)}})(&quot;addEventListener&quot;,&quot;DOMContentLoaded&quot;)})(this);<\/pre><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">script\/menu\/Button.js<\/h3>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;javascript&quot;,&quot;mime&quot;:&quot;text\/javascript&quot;,&quot;theme&quot;:&quot;material&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:true,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;JavaScript&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;js&quot;}\">function Button(sprite, position, callback, hoverSprite){\n\n    this.sprite = sprite;\n    this.hoverSprite = hoverSprite ? hoverSprite : sprite;\n    this.position = position;\n    this.callback = callback;\n}\n\nButton.prototype.draw = function(){\n\n    if(this.mouseInsideBorders()){\n        Canvas2D.drawImage(this.hoverSprite, this.position, 0, 1);\n        Canvas2D._canvas.style.cursor = &quot;pointer&quot;;\n    }\n    else{\n        Canvas2D.drawImage(this.sprite, this.position, 0, 0.98);\n    }\n}\n\nButton.prototype.handleInput = function(){\n\n    if(Mouse.left.pressed &amp;&amp; this.mouseInsideBorders()){\n        this.callback();\n    }\n}\n\nButton.prototype.mouseInsideBorders = function(){\n    \n    mousePos = Mouse.position;\n\n    if(mousePos.x &gt; this.position.x \n        &amp;&amp;\n        mousePos.x &lt; this.position.x + this.sprite.width\n        &amp;&amp;\n        mousePos.y &gt; this.position.y\n        &amp;&amp;\n        mousePos.y &lt; this.position.y + this.sprite.height\n    ){\n        return true;\n    }\n\n    return false;\n}<\/pre><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">script\/menu\/Label.js<\/h3>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;javascript&quot;,&quot;mime&quot;:&quot;text\/javascript&quot;,&quot;theme&quot;:&quot;material&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:true,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;JavaScript&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;js&quot;}\">function Label(text, position, origin, color, textAlign, fontname, fontsize){\n\n    this.text = typeof text !== 'undefined' ? text : '';\n    this.position = typeof position !== 'undefined' ? position : Vector2.zero;\n    this.origin = typeof origin !== 'undefined' ? origin : Vector2.zero;\n    this.color = typeof color !== 'undefined' ? color : Color.black;\n    this.textAlign = typeof textAlign !== 'undefined' ? textAlign : &quot;top&quot;;\n    this.fontname = typeof fontname !== 'undefined' ? fontname : &quot;Courier New&quot;;\n    this.fontsize = typeof fontsize !== 'undefined' ? fontsize : &quot;20px&quot;;\n}\n\nLabel.prototype.draw = function(){\n\n    Canvas2D.drawText(\n        this.text, \n        this.position,\n        this.origin,\n        this.color,\n        this.textAlign,\n        this.fontname,\n        this.fontsize\n    );\n\n}<\/pre><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">script\/menu\/MainMenu.js<\/h3>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;javascript&quot;,&quot;mime&quot;:&quot;text\/javascript&quot;,&quot;theme&quot;:&quot;material&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:true,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;JavaScript&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;js&quot;}\">\nfunction generateMainMenuLabels(headerText){\n\n    let labels = [\n\n        new Label(\n            headerText, \n            new Vector2(100,0),\n            Vector2.zero,\n            &quot;white&quot;,\n            &quot;left&quot;,\n            &quot;Bookman&quot;,\n            &quot;100px&quot;\n        ),\n        new Label(\n            &quot;\u00a9 2018 Chen Shmilovich&quot;, \n            new Vector2(1250,700),\n            Vector2.zero,\n            &quot;white&quot;,\n            &quot;left&quot;,\n            &quot;Bookman&quot;,\n            &quot;20px&quot;\n        )\n    ];\n\n\n    return labels;\n}\n\n\nfunction generateMainMenuButtons(inGame){\n\n\n\n    let buttons = [];\n\n    let dev = 0;\n\n    if(inGame){\n        dev = 200;\n        buttons.push(\n            new Button\n                (\n                    \/\/ CONTINUE BUTTON\n                    sprites.continueButton, \n                    new Vector2(200,200),\n                    function(){\n                        Game.mainMenu.active = false;\n                        GAME_STOPPED = false;\n                        setTimeout(Game.continueGame,200);\n                        sounds.fadeOut(Game.mainMenu.sound);\n                    },\n                    sprites.continueButtonHover\n                )\n        )\n    }\n\n    let muteSprite = sprites.muteButton;\n    let muteSpriteHover = sprites.muteButtonHover;\n\n    if(Game.mainMenu.sound &amp;&amp; Game.mainMenu.sound.volume === 0){\n        muteSprite = sprites.muteButtonPressed;\n        muteSpriteHover = sprites.muteButtonPressedHover;\n    }\n\n\n    let muteButton = new Button\n    (\n        \/\/ MUTE BUTTON\n        muteSprite, \n        new Vector2(1430,10),\n        function(){\n            if(Game.mainMenu.sound.volume == 0){\n                SOUND_ON = true;\n                Game.mainMenu.sound.volume = 0.8;\n                this.sprite = sprites.muteButton;\n                this.hoverSprite = sprites.muteButtonHover;\n            }\n            else{\n                SOUND_ON = false;\n                Game.mainMenu.sound.volume = 0.0;\n                this.sprite = sprites.muteButtonPressed;\n                this.hoverSprite = sprites.muteButtonPressedHover;\n            }\n        },\n        muteSpriteHover\n    );\n\n    let backButton = new Button\n    (\n        \/\/BACK\n        sprites.backButton, \n        new Vector2(100,150),\n        function(){\n            Game.mainMenu.labels = generateMainMenuLabels(&quot;Classic 8-Ball&quot;);\n            Game.mainMenu.buttons = generateMainMenuButtons(inGame);\n        },\n        sprites.backButtonHover\n    );\n\n    buttons = buttons.concat([\n        new Button\n        (\n            \/\/ PLAYER vs PLAYER\n            sprites.twoPlayersButton, \n            new Vector2(200,dev+200),\n            function(){\n                AI_ON = false;\n                Game.mainMenu.active = false;\n                GAME_STOPPED = false;\n                setTimeout(Game.startNewGame,200);\n                sounds.fadeOut(Game.mainMenu.sound);\n            },\n            sprites.twoPlayersButtonHover\n        ),\n        new Button\n        (\n            \/\/ PLAYER vs COMPUTER\n            sprites.onePlayersButton, \n            new Vector2(200,dev+400),\n            function(){\n                Game.mainMenu.labels = generateMainMenuLabels(&quot;Choose Difficulty&quot;);\n\n                Mouse.reset();\n                Game.mainMenu.buttons = [\n                    new Button\n                    (\n                        \/\/EASY\n                        sprites.easyButton, \n                        new Vector2(200,150),\n                        function(){\n                            AI_PLAYER_NUM = 1;\n                            AI_ON = true;\n                            TRAIN_ITER = 30;\n                            Game.mainMenu.active = false;\n                            GAME_STOPPED = false;\n                            setTimeout(Game.startNewGame,200);\n                            sounds.fadeOut(Game.mainMenu.sound);\n                        },\n                        sprites.easyButtonHover\n                    ),\n                    new Button\n                    (\n                        \/\/MEDIUM\n                        sprites.mediumButton, \n                        new Vector2(200,300),\n                        function(){\n                            AI_PLAYER_NUM = 1;\n                            AI_ON = true;\n                            TRAIN_ITER = 50;\n                            Game.mainMenu.active = false;\n                            GAME_STOPPED = false;\n                            setTimeout(Game.startNewGame,200);\n                            sounds.fadeOut(Game.mainMenu.sound);\n                        },\n                        sprites.mediumButtonHover\n                    ),\n                    new Button\n                    (\n                        \/\/HARD\n                        sprites.hardButton, \n                        new Vector2(200,450),\n                        function(){\n                            AI_PLAYER_NUM = 1;\n                            AI_ON = true;\n                            TRAIN_ITER = 100;\n                            Game.mainMenu.active = false;\n                            GAME_STOPPED = false;\n                            setTimeout(Game.startNewGame,200);\n                            sounds.fadeOut(Game.mainMenu.sound);\n                        },\n                        sprites.hardButtonHover\n                    ),\n                    new Button\n                    (\n                        \/\/INSANE\n                        sprites.insaneButton, \n                        new Vector2(200,600),\n                        function(){\n                            AI_PLAYER_NUM = 0;\n                            AI_ON = true;\n                            TRAIN_ITER = 700;\n                            Game.mainMenu.active = false;\n                            GAME_STOPPED = false;\n                            setTimeout(Game.startNewGame,200);\n                            sounds.fadeOut(Game.mainMenu.sound);\n                        },\n                        sprites.insaneButtonHover\n                    ),\n                    muteButton,\n                    backButton\n\n                ];\n            },\n            sprites.onePlayersButtonHover\n        ),\n        muteButton\n    ]);\n\n    return buttons;\n}<\/pre><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">script\/menu\/Menu.js<\/h3>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;javascript&quot;,&quot;mime&quot;:&quot;text\/javascript&quot;,&quot;theme&quot;:&quot;material&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:true,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;JavaScript&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;js&quot;}\">function Menu(){\n    \n}\n\nMenu.prototype.init = function\n(\n    backgroundSprite,\n    labels,\n    buttons,\n    sound\n){  \n    this.background = backgroundSprite;\n    this.labels = labels || [];\n    this.buttons = buttons || [];\n    this.sound = sound ? sound : undefined;\n\n    this.active = false;\n}\n\nMenu.prototype.load = function(){\n    this.sound.currentTime = 0;\n    this.active = true;\n\n    requestAnimationFrame(this.menuLoop.bind(this));\n    if(SOUND_ON){\n        this.sound.volume = 0.8;\n    }\n\n    this.sound.play();\n}\n\nMenu.prototype.draw = function(){\n\n    Canvas2D._canvas.style.cursor = &quot;auto&quot;; \n\n    Canvas2D.drawImage(\n        this.background, \n        Vector2.zero, \n        0, \n        1, \n        Vector2.zero\n    );\n\n\n    for(let i = 0 ; i &lt; this.labels.length ; i++){\n        this.labels[i].draw();\n    }\n\n    for(let i = 0 ; i &lt; this.buttons.length ; i++){\n        this.buttons[i].draw();\n    }\n}\n\nMenu.prototype.handleInput = function(){\n\n    for(let i = 0 ; i &lt; this.buttons.length ; i++){\n        this.buttons[i].handleInput();\n    }\n}\n\nMenu.prototype.menuLoop = function(){\n\n    if(this.active){\n        this.handleInput();\n        Canvas2D.clear();\n        this.draw();\n        Mouse.reset();\n        requestAnimationFrame(this.menuLoop.bind(this));\n    }\n\n}\n<\/pre><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">script\/system\/Color.js<\/h3>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;javascript&quot;,&quot;mime&quot;:&quot;text\/javascript&quot;,&quot;theme&quot;:&quot;material&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:true,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;JavaScript&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;js&quot;}\">&quot;use strict&quot;;\n\nvar Color = {\n    aliceBlue: &quot;#F0F8FF&quot;,\n    antiqueWhite: &quot;#FAEBD7&quot;,\n    aqua: &quot;#00FFFF&quot;,\n    aquamarine: &quot;#7FFFD4&quot;,\n    azure: &quot;#F0FFFF&quot;,\n    beige: &quot;#F5F5DC&quot;,\n    bisque: &quot;#FFE4C4&quot;,\n    black: &quot;#000000&quot;,\n    blanchedAlmond: &quot;#FFEBCD&quot;,\n    blue: &quot;#0000FF&quot;,\n    blueViolet: &quot;#8A2BE2&quot;,\n    brown: &quot;#A52A2A&quot;,\n    burlyWood: &quot;#DEB887&quot;,\n    cadetBlue: &quot;#5F9EA0&quot;,\n    chartreuse: &quot;#7FFF00&quot;,\n    chocolate: &quot;#D2691E&quot;,\n    coral: &quot;#FF7F50&quot;,\n    cornflowerBlue: &quot;#6495ED&quot;,\n    cornsilk: &quot;#FFF8DC&quot;,\n    crimson: &quot;#DC143C&quot;,\n    cyan: &quot;#00FFFF&quot;,\n    darkBlue: &quot;#00008B&quot;,\n    darkCyan: &quot;#008B8B&quot;,\n    darkGoldenrod: &quot;#B8860B&quot;,\n    darkGray: &quot;#A9A9A9&quot;,\n    darkGreen: &quot;#006400&quot;,\n    darkKhaki: &quot;#BDB76B&quot;,\n    darkMagenta: &quot;#8B008B&quot;,\n    darkOliveGreen: &quot;#556B2F&quot;,\n    darkOrange: &quot;#FF8C00&quot;,\n    darkOrchid: &quot;#9932CC&quot;,\n    darkRed: &quot;#8B0000&quot;,\n    darkSalmon: &quot;#E9967A&quot;,\n    darkSeaGreen: &quot;#8FBC8B&quot;,\n    darkSlateBlue: &quot;#483D8B&quot;,\n    darkSlateGray: &quot;#2F4F4F&quot;,\n    darkTurquoise: &quot;#00CED1&quot;,\n    darkViolet: &quot;#9400D3&quot;,\n    deepPink: &quot;#FF1493&quot;,\n    deepSkyBlue: &quot;#00BFFF&quot;,\n    dimGray: &quot;#696969&quot;,\n    dodgerBlue: &quot;#1E90FF&quot;,\n    firebrick: &quot;#B22222&quot;,\n    floralWhite: &quot;#FFFAF0&quot;,\n    forestGreen: &quot;#228B22&quot;,\n    fuchsia: &quot;#FF00FF&quot;,\n    gainsboro: &quot;#DCDCDC&quot;,\n    ghostWhite: &quot;#F8F8FF&quot;,\n    gold: &quot;#FFD700&quot;,\n    goldenrod: &quot;#DAA520&quot;,\n    gray: &quot;#808080&quot;,\n    green: &quot;#008000&quot;,\n    greenYellow: &quot;#ADFF2F&quot;,\n    honeydew: &quot;#F0FFF0&quot;,\n    hotPink: &quot;#FF69B4&quot;,\n    indianRed: &quot;#CD5C5C&quot;,\n    indigo: &quot;#4B0082&quot;,\n    ivory: &quot;#FFFFF0&quot;,\n    khaki: &quot;#F0E68C&quot;,\n    lavender: &quot;#E6E6FA&quot;,\n    lavenderBlush: &quot;#FFF0F5&quot;,\n    lawnGreen: &quot;#7CFC00&quot;,\n    lemonChiffon: &quot;#FFFACD&quot;,\n    lightBlue: &quot;#ADD8E6&quot;,\n    lightCoral: &quot;#F080FF&quot;,\n    lightCyan: &quot;#E0FFFF&quot;,\n    lightGoldenrodYellow: &quot;#FAFAD2&quot;,\n    lightGray: &quot;#D3D3D3&quot;,\n    lightGreen: &quot;#90EE90&quot;,\n    lightPink: &quot;#FFB6C1&quot;,\n    lightSalmon: &quot;#FFA07A&quot;,\n    lightSeaGreen: &quot;#20B2AA&quot;,\n    lightSkyBlue: &quot;#87CEFA&quot;,\n    lightSlateGray: &quot;#778899&quot;,\n    lightSteelBlue: &quot;#B0C4DE&quot;,\n    lightYellow: &quot;#FFFFE0&quot;,\n    lime: &quot;#00FF00&quot;,\n    limeGreen: &quot;#32CD32&quot;,\n    linen: &quot;#FAF0E6&quot;,\n    magenta: &quot;#FF00FF&quot;,\n    maroon: &quot;#800000&quot;,\n    mediumAquamarine: &quot;#66CDAA&quot;,\n    mediumBlue: &quot;#0000CD&quot;,\n    mediumOrchid: &quot;#BA55D3&quot;,\n    mediumPurple: &quot;#9370DB&quot;,\n    mediumSeaGreen: &quot;#3CB371&quot;,\n    mediumSlateBlue: &quot;#7B68EE&quot;,\n    mediumSpringGreen: &quot;#00FA9A&quot;,\n    mediumTurquoise: &quot;#48D1CC&quot;,\n    mediumVioletRed: &quot;#C71585&quot;,\n    midnightBlue: &quot;#191970&quot;,\n    mintCream: &quot;#F5FFFA&quot;,\n    mistyRose: &quot;#FFE4E1&quot;,\n    moccasin: &quot;#FFE4B5&quot;,\n    navajoWhite: &quot;#FFDEAD&quot;,\n    navy: &quot;#000080&quot;,\n    oldLace: &quot;#FDF5E6&quot;,\n    olive: &quot;#808000&quot;,\n    oliveDrab: &quot;#6B8E23&quot;,\n    orange: &quot;#FFA500&quot;,\n    orangeRed: &quot;#FF4500&quot;,\n    orchid: &quot;#DA70D6&quot;,\n    paleGoldenrod: &quot;#EEE8AA&quot;,\n    paleGreen: &quot;#98FB98&quot;,\n    paleTurquoise: &quot;#AFEEEE&quot;,\n    paleVioletRed: &quot;#DB7093&quot;,\n    papayaWhip: &quot;#FFEFD5&quot;,\n    peachPuff: &quot;#FFDAB9&quot;,\n    peru: &quot;#CD853F&quot;,\n    pink: &quot;#FFC0CB&quot;,\n    plum: &quot;#DDA0DD&quot;,\n    powderBlue: &quot;#B0E0E6&quot;,\n    purple: &quot;#800080&quot;,\n    red: &quot;#FF0000&quot;,\n    rosyBrown: &quot;#BC8F8F&quot;,\n    royalBlue: &quot;#4169E1&quot;,\n    saddleBrown: &quot;#8B4513&quot;,\n    salmon: &quot;#FA8072&quot;,\n    sandyBrown: &quot;#F4A460&quot;,\n    seaGreen: &quot;#2E8B57&quot;,\n    seaShell: &quot;#FFF5EE&quot;,\n    sienna: &quot;#A0522D&quot;,\n    silver: &quot;#C0C0C0&quot;,\n    skyBlue: &quot;#87CEEB&quot;,\n    slateBlue: &quot;#6A5ACD&quot;,\n    slateGray: &quot;#708090&quot;,\n    snow: &quot;#FFFAFA&quot;,\n    springGreen: &quot;#00FF7F&quot;,\n    steelBlue: &quot;#4682B4&quot;,\n    tan: &quot;#D2B48C&quot;,\n    teal: &quot;#008080&quot;,\n    thistle: &quot;#D8BFD8&quot;,\n    tomato: &quot;#FF6347&quot;,\n    turquoise: &quot;#40E0D0&quot;,\n    violet: &quot;#EE82EE&quot;,\n    wheat: &quot;#F5DEB3&quot;,\n    white: &quot;#FFFFFF&quot;,\n    whiteSmoke: &quot;#F5F5F5&quot;,\n    yellow: &quot;#FFFF00&quot;,\n    yellowGreen: &quot;#9ACD32&quot;\n};<\/pre><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">script\/system\/Keys.js<\/h3>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;javascript&quot;,&quot;mime&quot;:&quot;text\/javascript&quot;,&quot;theme&quot;:&quot;material&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:true,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;JavaScript&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;js&quot;}\">&quot;use strict&quot;;\n\nvar Keys = {\n    none: 0,\n    back: 8,\n    tab: 9,\n    enter: 13,\n    pause: 19,\n    escape: 27,\n\n    space: 32,\n\n    pageUp: 33,\n    pageDown: 34,\n    end: 35,\n    home: 36,\n    left: 37,\n    up: 38,\n    right: 39,\n    down: 40,\n\n    insert: 45,\n    del: 46,\n\n    d0: 48,\n    d1: 49,\n    d2: 50,\n    d3: 51,\n    d4: 52,\n    d5: 53,\n    d6: 54,\n    d7: 55,\n    d8: 56,\n    d9: 57,\n\n    A: 65,     B: 66,      C: 67,      D: 68,       E: 69,      F: 70,\n    G: 71,     H: 72,      I: 73,      J: 74,       K: 75,      L: 76,\n    M: 77,     N: 78,      O: 79,      P: 80,       Q: 81,      R: 82,\n    S: 83,     T: 84,      U: 85,      V: 86,       W: 87,      X: 88,\n    Y: 89,     Z: 90,\n\n    multiply: 42,\n    add: 43,\n    subtract: 45,\n    decimal: 46,\n    divide: 47\n};<\/pre><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">script\/Assets.js<\/h3>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;javascript&quot;,&quot;mime&quot;:&quot;text\/javascript&quot;,&quot;theme&quot;:&quot;material&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:true,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;JavaScript&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;js&quot;}\">&quot;use strict&quot;;\n\nvar sprites = {};\nvar sounds = {};\n\nGame.loadAssets = function () {\n    var loadSprite = function (sprite) {\n        return Game.loadSprite(&quot;assets\/sprites\/&quot; + sprite);\n    };\n\n     var loadSound = function (sound) {\n        return new Audio(&quot;assets\/sounds\/&quot; + sound);\n    };\n\n    sprites.mainMenuBackground = loadSprite(&quot;main_menu_background.png&quot;);\n    sprites.background = loadSprite(&quot;spr_background4.png&quot;);\n    sprites.ball = loadSprite(&quot;spr_ball2.png&quot;);\n    sprites.redBall = loadSprite(&quot;spr_redBall2.png&quot;);\n    sprites.yellowBall = loadSprite(&quot;spr_yellowBall2.png&quot;);\n    sprites.blackBall = loadSprite(&quot;spr_blackBall2.png&quot;);\n    sprites.stick = loadSprite(&quot;spr_stick.png&quot;);\n    sprites.twoPlayersButton = loadSprite(&quot;2_players_button.png&quot;);\n    sprites.twoPlayersButtonHover = loadSprite(&quot;2_players_button_hover.png&quot;);\n    sprites.onePlayersButton = loadSprite(&quot;1_player_button.png&quot;);\n    sprites.onePlayersButtonHover = loadSprite(&quot;1_player_button_hover.png&quot;);\n    sprites.muteButton = loadSprite(&quot;mute_button.png&quot;);\n    sprites.muteButtonHover = loadSprite(&quot;mute_button_hover.png&quot;);\n    sprites.muteButtonPressed = loadSprite(&quot;mute_button_pressed.png&quot;);\n    sprites.muteButtonPressedHover = loadSprite(&quot;mute_button_pressed_hover.png&quot;);\n    sprites.easyButton = loadSprite(&quot;easy_button.png&quot;);\n    sprites.easyButtonHover = loadSprite(&quot;easy_button_hover.png&quot;);\n    sprites.mediumButton = loadSprite(&quot;medium_button.png&quot;);\n    sprites.mediumButtonHover = loadSprite(&quot;medium_button_hover.png&quot;);\n    sprites.hardButton = loadSprite(&quot;hard_button.png&quot;);\n    sprites.hardButtonHover = loadSprite(&quot;hard_button_hover.png&quot;);\n    sprites.backButton = loadSprite(&quot;back_button.png&quot;);\n    sprites.backButtonHover = loadSprite(&quot;back_button_hover.png&quot;);\n    sprites.continueButton = loadSprite(&quot;continue_button.png&quot;);\n    sprites.continueButtonHover = loadSprite(&quot;continue_button_hover.png&quot;);\n    sprites.insaneButton = loadSprite(&quot;insane_button.png&quot;);\n    sprites.insaneButtonHover = loadSprite(&quot;insane_button_hover.png&quot;);\n    sprites.aboutButton = loadSprite(&quot;about_button.png&quot;);\n    sprites.aboutButtonHover = loadSprite(&quot;about_button_hover.png&quot;);\n    sprites.controls = loadSprite(&quot;controls.png&quot;);\n\n    sounds.side = loadSound(&quot;Side.wav&quot;);\n    sounds.ballsCollide = loadSound(&quot;BallsCollide.wav&quot;);\n    sounds.strike = loadSound(&quot;Strike.wav&quot;);\n    sounds.hole = loadSound(&quot;Hole.wav&quot;);\n    \n    \/\/ Bossa Antigua Kevin MacLeod (incompetech.com)\n    \/\/ Licensed under Creative Commons: By Attribution 3.0 License\n    \/\/ http:\/\/creativecommons.org\/licenses\/by\/3.0\/\n    sounds.jazzTune = loadSound(&quot;Bossa Antigua.mp3&quot;);\n}\n\nsounds.fadeOut = function(sound) {\n\n    var fadeAudio = setInterval(function () {\n\n        if(GAME_STOPPED)\n            return;\n\n        \/\/ Only fade if past the fade out point or not at zero already\n        if ((sound.volume &gt;= 0.05)) {\n            sound.volume -= 0.05;\n        }\n        else{\n            sound.pause();\n            clearInterval(fadeAudio);\n        }\n    }, 400);\n}<\/pre><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">script\/Canvas2D.js<\/h3>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;javascript&quot;,&quot;mime&quot;:&quot;text\/javascript&quot;,&quot;theme&quot;:&quot;material&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:true,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;JavaScript&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;js&quot;}\">&quot;use strict&quot;;\n\nfunction Canvas2D_Singleton() {\n    this._canvas = null;\n    this._canvasContext = null;\n    this._canvasOffset = Vector2.zero;\n}\n\nObject.defineProperty(Canvas2D_Singleton.prototype, &quot;offset&quot;,\n    {\n        get: function () {\n            return this._canvasOffset;\n        }\n    });\n\nObject.defineProperty(Canvas2D_Singleton.prototype, &quot;scale&quot;,\n    {\n        get: function () {\n            return new Vector2(this._canvas.width \/ Game.size.x,\n                this._canvas.height \/ Game.size.y);\n        }\n    });\n\nCanvas2D_Singleton.prototype.initialize = function (divName, canvasName) {\n    this._canvas = document.getElementById(canvasName);\n    this._div = document.getElementById(divName);\n\n    if (this._canvas.getContext)\n        this._canvasContext = this._canvas.getContext('2d');\n    else {\n        alert('Your browser is not HTML5 compatible.!');\n        return;\n    }\n    window.onresize = Canvas2D_Singleton.prototype.resize;\n    this.resize();\n};\n\nCanvas2D_Singleton.prototype.clear = function () {\n    this._canvasContext.clearRect(0, 0, this._canvas.width, this._canvas.height);\n};\n\nCanvas2D_Singleton.prototype.resize = function () {\n    var gameCanvas = Canvas2D._canvas;\n    var gameArea = Canvas2D._div;\n    var widthToHeight = Game.size.x \/ Game.size.y;\n    var newWidth = window.innerWidth;\n    var newHeight = window.innerHeight;\n    var newWidthToHeight = newWidth \/ newHeight;\n\n    if (newWidthToHeight &gt; widthToHeight) {\n        newWidth = newHeight * widthToHeight;\n    } else {\n        newHeight = newWidth \/ widthToHeight;\n    }\n    gameArea.style.width = newWidth + 'px';\n    gameArea.style.height = newHeight + 'px';\n\n    gameArea.style.marginTop = (window.innerHeight - newHeight) \/ 2 + 'px';\n    gameArea.style.marginLeft = (window.innerWidth - newWidth) \/ 2 + 'px';\n    gameArea.style.marginBottom = (window.innerHeight - newHeight) \/ 2 + 'px';\n    gameArea.style.marginRight = (window.innerWidth - newWidth) \/ 2 + 'px';\n\n    gameCanvas.width = newWidth;\n    gameCanvas.height = newHeight;\n\n    var offset = Vector2.zero;\n    if (gameCanvas.offsetParent) {\n        do {\n            offset.x += gameCanvas.offsetLeft;\n            offset.y += gameCanvas.offsetTop;\n        } while ((gameCanvas = gameCanvas.offsetParent));\n    }\n    Canvas2D._canvasOffset = offset;\n};\n\nCanvas2D_Singleton.prototype.drawImage = function (sprite, position, rotation, scale, origin) {\n    var canvasScale = this.scale;\n\n    position = typeof position !== 'undefined' ? position : Vector2.zero;\n    rotation = typeof rotation !== 'undefined' ? rotation : 0;\n    scale = typeof scale !== 'undefined' ? scale : 1;\n    origin = typeof origin !== 'undefined' ? origin : Vector2.zero;\n\n    this._canvasContext.save();\n    this._canvasContext.scale(canvasScale.x, canvasScale.y);\n    this._canvasContext.translate(position.x, position.y);\n    this._canvasContext.rotate(rotation);\n    this._canvasContext.drawImage(sprite, 0, 0,\n        sprite.width, sprite.height,\n        -origin.x * scale, -origin.y * scale,\n        sprite.width * scale, sprite.height * scale);\n    this._canvasContext.restore();\n};\n\nCanvas2D_Singleton.prototype.drawText = function (text, position, origin, color, textAlign, fontname, fontsize) {\n    var canvasScale = this.scale;\n\n    position = typeof position !== 'undefined' ? position : Vector2.zero;\n    origin = typeof origin !== 'undefined' ? origin : Vector2.zero;\n    color = typeof color !== 'undefined' ? color : Color.black;\n    textAlign = typeof textAlign !== 'undefined' ? textAlign : &quot;top&quot;;\n    fontname = typeof fontname !== 'undefined' ? fontname : &quot;sans-serif&quot;;\n    fontsize = typeof fontsize !== 'undefined' ? fontsize : &quot;20px&quot;;\n\n    this._canvasContext.save();\n    this._canvasContext.scale(canvasScale.x, canvasScale.y);\n    this._canvasContext.translate(position.x - origin.x, position.y - origin.y);\n    this._canvasContext.textBaseline = 'top';\n    this._canvasContext.font = fontsize + &quot; &quot; + fontname;\n    this._canvasContext.fillStyle = color.toString();\n    this._canvasContext.textAlign = textAlign;\n    this._canvasContext.fillText(text, 0, 0);\n    this._canvasContext.restore();\n};\n\nvar Canvas2D = new Canvas2D_Singleton();\n<\/pre><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">script\/Game.js<\/h3>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;javascript&quot;,&quot;mime&quot;:&quot;text\/javascript&quot;,&quot;theme&quot;:&quot;material&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:true,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;JavaScript&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;js&quot;}\">&quot;use strict&quot;;\n\nvar requestAnimationFrame = (function () {\n    return  window.requestAnimationFrame ||\n        window.webkitRequestAnimationFrame ||\n        window.mozRequestAnimationFrame ||\n        window.oRequestAnimationFrame ||\n        window.msRequestAnimationFrame ||\n        function (callback) {\n            window.setTimeout(callback, 1000 \/ 60);\n        };\n})();\n\nfunction Game_Singleton() {\n    this.size = undefined;\n    this.spritesStillLoading = 0;\n    this.gameWorld = undefined;\n    this.sound = true;\n\n    this.mainMenu = new Menu();\n}\n\nGame_Singleton.prototype.start = function (divName, canvasName, x, y) {\n    this.size = new Vector2(x,y);\n    Canvas2D.initialize(divName, canvasName);\n    this.loadAssets();\n    this.assetLoadingLoop();\n};\n\nGame_Singleton.prototype.initialize = function () {\n    this.gameWorld = new GameWorld();\n    this.policy = new GamePolicy();\n    \n    this.initMenus();\n\n    AI.init(this.gameWorld, this.policy);\n};\n\nGame_Singleton.prototype.initMenus = function(inGame){\n\n    let labels = generateMainMenuLabels(&quot;Classic 8-Ball&quot;);\n\n    let buttons = generateMainMenuButtons(inGame);\n\n    this.mainMenu.init\n    (\n        sprites.mainMenuBackground,\n        labels,\n        buttons,\n        sounds.jazzTune\n    );\n}\n\nGame_Singleton.prototype.loadSprite = function (imageName) {\n    console.log(&quot;Loading sprite: &quot; + imageName);\n    var image = new Image();\n    image.src = imageName;\n    this.spritesStillLoading += 1;\n    image.onload = function () {\n        Game.spritesStillLoading -= 1;\n    };\n    return image;\n};\n\nGame_Singleton.prototype.assetLoadingLoop = function () {\n    if (!this.spritesStillLoading &gt; 0)\n        requestAnimationFrame(Game.assetLoadingLoop);\n    else {\n        Game.initialize();\n        requestAnimationFrame(this.mainMenu.load.bind(this.mainMenu));\n    }\n};\n\nGame_Singleton.prototype.handleInput = function(){\n\n    if(Keyboard.down(Keys.escape)){\n        GAME_STOPPED = true;\n        Game.initMenus(true);\n        requestAnimationFrame(Game.mainMenu.load.bind(this.mainMenu));\n    }\n}\n\nGame_Singleton.prototype.startNewGame = function(){\n    Canvas2D._canvas.style.cursor = &quot;auto&quot;;\n\n    Game.gameWorld = new GameWorld();\n    Game.policy = new GamePolicy();\n\n    Canvas2D.clear();\n    Canvas2D.drawImage(\n        sprites.controls, \n        new Vector2(Game.size.x\/2,Game.size.y\/2), \n        0, \n        1, \n        new Vector2(sprites.controls.width\/2,sprites.controls.height\/2)\n    );\n\n    setTimeout(()=&gt;{\n        AI.init(Game.gameWorld, Game.policy);\n\n        if(AI_ON &amp;&amp; AI_PLAYER_NUM == 0){\n            AI.startSession();\n        }\n        Game.mainLoop();\n    },5000);\n}\n\nGame_Singleton.prototype.continueGame = function(){\n    Canvas2D._canvas.style.cursor = &quot;auto&quot;;\n\n    requestAnimationFrame(Game.mainLoop);\n}\n\nGame_Singleton.prototype.mainLoop = function () {\n    \n\n    if(DISPLAY &amp;&amp; !GAME_STOPPED){\n        Game.gameWorld.handleInput(DELTA);\n        Game.gameWorld.update(DELTA);\n        Canvas2D.clear();\n        Game.gameWorld.draw();\n        Mouse.reset();\n        Game.handleInput();\n        requestAnimationFrame(Game.mainLoop);\n    }\n};\n\nvar Game = new Game_Singleton();\n<\/pre><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">script\/GamePolicy.js<\/h3>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;javascript&quot;,&quot;mime&quot;:&quot;text\/javascript&quot;,&quot;theme&quot;:&quot;material&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:true,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;JavaScript&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;js&quot;}\">\nfunction GamePolicy(){\n\n    this.turn = 0;\n    this.firstCollision = true;\n    let player1TotalScore = new Score(new Vector2(Game.size.x\/2 - 75,Game.size.y\/2 - 45));\n    let player2TotalScore = new Score(new Vector2(Game.size.x\/2 + 75,Game.size.y\/2 - 45));\n\n    let player1MatchScore = new Score(new Vector2(Game.size.x\/2 - 280,108));\n    let player2MatchScore = new Score(new Vector2(Game.size.x\/2 + 230,108));\n\n    this.players = [new Player(player1MatchScore,player1TotalScore), new Player(player2MatchScore,player2TotalScore)];\n    this.foul = false;\n    this.scored = false;\n    this.won = false;\n    this.turnPlayed = false;\n    this.validBallsInsertedOnTurn = 0;\n\n    this.leftBorderX = BORDER_SIZE;\n    this.rightBorderX = Game.size.x - BORDER_SIZE;\n    this.topBorderY = BORDER_SIZE;\n    this.bottomBorderY = Game.size.y - BORDER_SIZE;\n\n    this.topCenterHolePos = new Vector2(750,32);\n    this.bottomCenterHolePos = new Vector2(750,794);\n    this.topLeftHolePos = new Vector2(62,62);\n    this.topRightHolePos = new Vector2(1435,62);\n    this.bottomLeftHolePos = new Vector2(62,762)\n    this.bottomRightHolePos = new Vector2(1435,762);\n}\n\nGamePolicy.prototype.reset = function(){\n    this.turn = 0;\n    this.players[0].matchScore.value = 0;\n    this.players[0].color = undefined;\n    this.players[1].matchScore.value = 0;\n    this.players[1].color = undefined;\n    this.foul = false;\n    this.scored = false;\n    this.turnPlayed = false;\n    this.won = false;\n    this.firstCollision = true;\n    this.validBallsInsertedOnTurn = 0;\n}\nGamePolicy.prototype.drawScores = function(){\n    Canvas2D.drawText(&quot;PLAYER &quot; + (this.turn+1), new Vector2(Game.size.x\/2 + 40,200), new Vector2(150,0), &quot;#096834&quot;, &quot;top&quot;, &quot;Impact&quot;, &quot;70px&quot;);\n    this.players[0].totalScore.draw();\n    this.players[1].totalScore.draw();\n\n    this.players[0].matchScore.drawLines(this.players[0].color);\n    this.players[1].matchScore.drawLines(this.players[1].color);\n}\n\nGamePolicy.prototype.checkColisionValidity = function(ball1,ball2){\n\n    let currentPlayerColor = this.players[this.turn].color;\n\n    if(this.players[this.turn].matchScore.value == 7 &amp;&amp;\n       (ball1.color == Color.black || ball2.color == Color.black)){\n        this.firstCollision = false;\n        return;\n       }\n\n    if(!this.firstCollision)\n        return;\n\n    if(currentPlayerColor == undefined){\n        this.firstCollision = false;\n        return;\n    }\n\n    if(ball1.color == Color.white){\n        if(ball2.color != currentPlayerColor){\n            this.foul = true;\n        }\n        this.firstCollision = false;\n    }\n\n    if(ball2.color == Color.white){\n        if(ball1.color != currentPlayerColor){\n            this.foul = true;\n        }\n        this.firstCollision = false;\n    }\n}\nGamePolicy.prototype.handleBallInHole = function(ball){\n\n    setTimeout(function(){ball.out();}, 100);\n\n    let currentPlayer = this.players[this.turn];\n    let secondPlayer = this.players[(this.turn+1)%2];\n\n    if(currentPlayer.color == undefined){\n        if(ball.color === Color.red){\n            currentPlayer.color = Color.red;\n            secondPlayer.color = Color.yellow;\n        }\n        else if(ball.color === Color.yellow){\n            currentPlayer.color = Color.yellow;\n            secondPlayer.color = Color.red;\n        }\n        else if(ball.color === Color.black){\n            this.won = true; \n            this.foul = true;\n        }\n        else if(ball.color === Color.white){\n            this.foul = true;\n        }\n    }\n\n    if(currentPlayer.color === ball.color){\n        currentPlayer.matchScore.increment();\n        this.scored = true;\n        this.validBallsInsertedOnTurn++;\n    }\n    else if(ball.color === Color.white){\n\n        if(currentPlayer.color != undefined){\n            this.foul = true;\n\n            let ballsSet = Game.gameWorld.getBallsSetByColor(currentPlayer.color);\n\n            let allBallsInHole = true;\n\n            for (var i = 0 ; i &lt; ballsSet.length; i++){\n                if(!ballsSet[i].inHole){\n                    allBallsInHole = false;\n                }\n            }\n\n            if(allBallsInHole){\n                this.won = true;\n            }\n        }\n    }\n    else if(ball.color === Color.black){\n\n        if(currentPlayer.color != undefined){\n            let ballsSet = Game.gameWorld.getBallsSetByColor(currentPlayer.color);\n\n            for (var i = 0 ; i &lt; ballsSet.length; i++){\n                if(!ballsSet[i].inHole){\n                    this.foul = true;\n                }\n            }\n            \n            this.won = true;\n        }\n    }\n    else{\n        secondPlayer.matchScore.increment();\n        this.foul = true;\n    }\n}\n\nGamePolicy.prototype.switchTurns = function(){\n    this.turn++;\n    this.turn%=2;\n}\n\nGamePolicy.prototype.updateTurnOutcome = function(){\n    \n    if(!this.turnPlayed){\n        return;\n    }\n\n    if(this.firstCollision == true){\n        this.foul = true;\n    }\n\n    if(this.won){\n        \n        if(!this.foul){\n            this.players[this.turn].totalScore.increment();\n            if(AI.finishedSession){\n                this.reset()\n                setTimeout(function(){Game.gameWorld.reset();\n                }, 1000);\n            }\n        }\n        else{\n            this.players[(this.turn+1)%2].totalScore.increment();\n            if(AI.finishedSession){\n                this.reset();\n                setTimeout(function(){Game.gameWorld.reset();\n                }, 1000);\n            }\n        }\n        return;\n    }\n\n    if(!this.scored || this.foul)\n        this.switchTurns();\n\n    this.scored = false;\n    this.turnPlayed = false;\n    this.firstCollision = true;\n    this.validBallsInsertedOnTurn = 0;\n\n    setTimeout(function(){Game.gameWorld.whiteBall.visible=true;}, 200);\n\n    if(AI_ON &amp;&amp; this.turn === AI_PLAYER_NUM &amp;&amp; AI.finishedSession){\n        AI.startSession();\n    }\n}\n\nGamePolicy.prototype.handleFoul = function(){\n\n    if(!Mouse.left.down){\n        Game.gameWorld.whiteBall.position = Mouse.position;\n    }\n\n}\nGamePolicy.prototype.isXOutsideLeftBorder = function(pos, origin){\n    return (pos.x - origin.x) &lt; this.leftBorderX;\n}\nGamePolicy.prototype.isXOutsideRightBorder = function(pos, origin){\n    return (pos.x + origin.x) &gt; this.rightBorderX;\n}\nGamePolicy.prototype.isYOutsideTopBorder = function(pos, origin){\n    return (pos.y - origin.y) &lt; this.topBorderY;\n}\nGamePolicy.prototype.isYOutsideBottomBorder = function(pos , origin){\n    return (pos.y + origin.y) &gt; this.bottomBorderY;\n}\n\nGamePolicy.prototype.isOutsideBorder = function(pos,origin){\n    return this.isXOutsideLeftBorder(pos,origin) || this.isXOutsideRightBorder(pos,origin) || \n    this.isYOutsideTopBorder(pos, origin) || this.isYOutsideBottomBorder(pos , origin);\n}\n\nGamePolicy.prototype.isInsideTopLeftHole = function(pos){\n    return this.topLeftHolePos.distanceFrom(pos) &lt; HOLE_RADIUS;\n}\n\nGamePolicy.prototype.isInsideTopRightHole = function(pos){\n    return this.topRightHolePos.distanceFrom(pos) &lt; HOLE_RADIUS;\n}\n\nGamePolicy.prototype.isInsideBottomLeftHole = function(pos){\n    return this.bottomLeftHolePos.distanceFrom(pos) &lt; HOLE_RADIUS;\n}\n\nGamePolicy.prototype.isInsideBottomRightHole = function(pos){\n    return this.bottomRightHolePos.distanceFrom(pos) &lt; HOLE_RADIUS;\n}\n\nGamePolicy.prototype.isInsideTopCenterHole = function(pos){\n    return this.topCenterHolePos.distanceFrom(pos) &lt; (HOLE_RADIUS + 6);\n}\n\nGamePolicy.prototype.isInsideBottomCenterHole = function(pos){\n    return this.bottomCenterHolePos.distanceFrom(pos) &lt; (HOLE_RADIUS + 6);\n}\n\nGamePolicy.prototype.isInsideHole = function(pos){\n    return this.isInsideTopLeftHole(pos) || this.isInsideTopRightHole(pos) || \n           this.isInsideBottomLeftHole(pos) || this.isInsideBottomRightHole(pos) ||\n           this.isInsideTopCenterHole(pos) || this.isInsideBottomCenterHole(pos);\n}\n\nGamePolicy.prototype.initiateState = function(policyState){\n\n    this.turn = policyState.turn;\n    this.firstCollision = policyState.firstCollision;\n    this.foul = policyState.foul;\n    this.scored = policyState.scored;\n    this.won = policyState.won;\n    this.turnPlayed = policyState.turnPlayed;\n    this.validBallsInsertedOnTurn = policyState.validBallsInsertedOnTurn;\n\n    this.players[0].totalScore.value = policyState.players[0].totalScore.value;\n    this.players[1].totalScore.value = policyState.players[1].totalScore.value;\n\n    this.players[0].matchScore.value = policyState.players[0].matchScore.value;\n    this.players[0].color = policyState.players[0].color;\n    this.players[1].matchScore.value = policyState.players[1].matchScore.value;\n    this.players[1].color = policyState.players[1].color;\n\n}<\/pre><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">script\/GameWorld.js<\/h3>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;javascript&quot;,&quot;mime&quot;:&quot;text\/javascript&quot;,&quot;theme&quot;:&quot;material&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:true,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;JavaScript&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;js&quot;}\">&quot;use strict&quot;;\n\nfunction GameWorld() {\n\n    this.whiteBallStartingPosition = new Vector2(413,413);\n\n    this.redBalls = [\n    new Ball(new Vector2(1056,433),Color.red),\/\/3\n    new Ball(new Vector2(1090,374),Color.red),\/\/4\n    new Ball(new Vector2(1126,393),Color.red),\/\/8\n    new Ball(new Vector2(1126,472),Color.red),\/\/10;\n    new Ball(new Vector2(1162,335),Color.red),\/\/11\n    new Ball(new Vector2(1162,374),Color.red),\/\/12\n    new Ball(new Vector2(1162,452),Color.red)\/\/14\n    ]\n\n    this.yellowBalls = [\n    new Ball(new Vector2(1022,413),Color.yellow),\/\/1\n    new Ball(new Vector2(1056,393),Color.yellow),\/\/2\n    new Ball(new Vector2(1090,452),Color.yellow),\/\/6\n    new Ball(new Vector2(1126,354),Color.yellow),\/\/7\n    new Ball(new Vector2(1126,433),Color.yellow),\/\/9\n    new Ball(new Vector2(1162,413),Color.yellow),\/\/13\n    new Ball(new Vector2(1162,491),Color.yellow)\/\/15\n    ];\n\n    this.whiteBall = new Ball(new Vector2(413,413),Color.white);\n    this.blackBall = new Ball(new Vector2(1090,413),Color.black);\n\n    this.balls = [\n    this.yellowBalls[0],\n    this.yellowBalls[1],\n    this.redBalls[0],\n    this.redBalls[1],\n    this.blackBall,\n    this.yellowBalls[2],\n    this.yellowBalls[3],\n    this.redBalls[2],\n    this.yellowBalls[4],\n    this.redBalls[3],\n    this.redBalls[4],\n    this.redBalls[5],\n    this.yellowBalls[5],\n    this.redBalls[6],\n    this.yellowBalls[6],\n    this.whiteBall]\n\n    this.stick = new Stick({ x : 413, y : 413 });\n\n    this.gameOver = false;\n}\n\nGameWorld.prototype.getBallsSetByColor = function(color){\n\n    if(color === Color.red){\n        return this.redBalls;\n    }\n    if(color === Color.yellow){\n        return this.yellowBalls;\n    }\n    if(color === Color.white){\n        return this.whiteBall;\n    }\n    if(color === Color.black){\n        return this.blackBall;\n    }\n}\n\nGameWorld.prototype.handleInput = function (delta) {\n    this.stick.handleInput(delta);\n};\n\nGameWorld.prototype.update = function (delta) {\n    this.stick.update(delta);\n\n    for (var i = 0 ; i &lt; this.balls.length; i++){\n        for(var j = i + 1 ; j &lt; this.balls.length ; j++){\n            this.handleCollision(this.balls[i], this.balls[j], delta);\n        }\n    }\n\n    for (var i = 0 ; i &lt; this.balls.length; i++) {\n        this.balls[i].update(delta);\n    }\n\n    if(!this.ballsMoving() &amp;&amp; AI.finishedSession){\n        Game.policy.updateTurnOutcome();\n        if(Game.policy.foul){\n            this.ballInHand();\n        }\n    }\n\n};\n\nGameWorld.prototype.ballInHand = function(){\n    if(AI_ON &amp;&amp; Game.policy.turn === AI_PLAYER_NUM){\n        return;\n    }\n\n    KEYBOARD_INPUT_ON = false;\n    this.stick.visible = false;\n    if(!Mouse.left.down){\n        this.whiteBall.position = Mouse.position;\n    }\n    else{\n        let ballsOverlap = this.whiteBallOverlapsBalls();\n\n        if(!Game.policy.isOutsideBorder(Mouse.position,this.whiteBall.origin) &amp;&amp;\n            !Game.policy.isInsideHole(Mouse.position) &amp;&amp;\n            !ballsOverlap){\n            KEYBOARD_INPUT_ON = true;\n            Keyboard.reset();\n            Mouse.reset();\n            this.whiteBall.position = Mouse.position;\n            this.whiteBall.inHole = false;\n            Game.policy.foul = false;\n            this.stick.position = this.whiteBall.position;\n            this.stick.visible = true;\n        }\n    }\n\n}\n\nGameWorld.prototype.whiteBallOverlapsBalls = function(){\n\n    let ballsOverlap = false;\n    for (var i = 0 ; i &lt; this.balls.length; i++) {\n        if(this.whiteBall !== this.balls[i]){\n            if(this.whiteBall.position.distanceFrom(this.balls[i].position)&lt;BALL_SIZE){\n                ballsOverlap = true;\n            }\n        }\n    }\n\n    return ballsOverlap;\n}\n\nGameWorld.prototype.ballsMoving = function(){\n\n    var ballsMoving = false;\n\n    for (var i = 0 ; i &lt; this.balls.length; i++) {\n        if(this.balls[i].moving){\n            ballsMoving = true;\n        }\n    }\n\n    return ballsMoving;\n}\n\nGameWorld.prototype.handleCollision = function(ball1, ball2, delta){\n\n    if(ball1.inHole || ball2.inHole)\n        return;\n\n    if(!ball1.moving &amp;&amp; !ball2.moving)\n        return;\n\n    var ball1NewPos = ball1.position.add(ball1.velocity.multiply(delta));\n    var ball2NewPos = ball2.position.add(ball2.velocity.multiply(delta));\n\n    var dist = ball1NewPos.distanceFrom(ball2NewPos);\n\n    if(dist&lt;BALL_SIZE){\n        Game.policy.checkColisionValidity(ball1, ball2);\n\n        var power = (Math.abs(ball1.velocity.x) + Math.abs(ball1.velocity.y)) + \n                    (Math.abs(ball2.velocity.x) + Math.abs(ball2.velocity.y));\n        power = power * 0.00482;\n\n        if(Game.sound &amp;&amp; SOUND_ON){\n            var ballsCollide = sounds.ballsCollide.cloneNode(true);\n            ballsCollide.volume = (power\/(20))&lt;1?(power\/(20)):1;\n            ballsCollide.play();\n        }\n\n        var opposite = ball1.position.y - ball2.position.y;\n        var adjacent = ball1.position.x - ball2.position.x;\n        var rotation = Math.atan2(opposite, adjacent);\n\n        ball1.moving = true;\n        ball2.moving = true;\n\n        var velocity2 = new Vector2(90*Math.cos(rotation + Math.PI)*power,90*Math.sin(rotation + Math.PI)*power);\n        ball2.velocity = ball2.velocity.addTo(velocity2);\n\n        ball2.velocity.multiplyWith(0.97);\n\n        var velocity1 = new Vector2(90*Math.cos(rotation)*power,90*Math.sin(rotation)*power);\n        ball1.velocity = ball1.velocity.addTo(velocity1);\n\n        ball1.velocity.multiplyWith(0.97);\n    }\n\n}\n\nGameWorld.prototype.draw = function () {\n    Canvas2D.drawImage(sprites.background);\n    Game.policy.drawScores();\n\n    for (var i = 0; i &lt; this.balls.length; i++) {\n        this.balls[i].draw();\n    }\n\n    this.stick.draw();\n};\n\nGameWorld.prototype.reset = function () {\n    this.gameOver = false;\n\n    for (var i = 0; i &lt; this.balls.length; i++) {\n        this.balls[i].reset();\n    }\n\n    this.stick.reset();\n\n    if(AI_ON &amp;&amp; AI_PLAYER_NUM === 0){\n        AI.startSession();\n    }\n};\n\nGameWorld.prototype.initiateState = function(balls){\n    \n    for (var i = 0; i &lt; this.balls.length; i++) {\n        this.balls[i].position.x = balls[i].position.x;\n        this.balls[i].position.y = balls[i].position.y;\n        this.balls[i].visible = balls[i].visible;\n        this.balls[i].inHole = balls[i].inHole;\n    }\n\n    this.stick.position = this.whiteBall.position;\n}\n<\/pre><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">script\/Global.js<\/h3>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;javascript&quot;,&quot;mime&quot;:&quot;text\/javascript&quot;,&quot;theme&quot;:&quot;material&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:true,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;JavaScript&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;js&quot;}\">const LOG = false;\n\nconst BALL_SIZE = 38;\nconst BORDER_SIZE = 57;\nconst HOLE_RADIUS = 46;\n\nconst DELTA = 1\/100;\n\nlet DISPLAY = true;\nlet SOUND_ON = true;\nlet GAME_STOPPED = true;\n\nlet KEYBOARD_INPUT_ON = true;\n\nlet TRAIN_ITER = 100;\nlet AI_ON = true;\nlet AI_PLAYER_NUM = 1;\nlet DISPLAY_TRAINING = false;<\/pre><\/div>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">How to Play?<\/h2>\n\n\n\n<ul class=\"wp-block-list\"><li>Aim by moving the mouse.<\/li><li>Left click: shoot.<\/li><li>&#8216;W&#8217; : Increase shot power.<\/li><li>&#8216;S&#8217; : Decrease shot power.<\/li><li>&#8216;Esc&#8217; : Return to main menu.<\/li><\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Download JavaScript 8 Ball Pool Game Code<\/h2>\n\n\n<p><a class=\"ep_link_major\" href=\"https:\/\/github.com\/henshmi\/Classic-Pool-Game\" target=\"_blank\" rel=\"noopener\">Download<\/a><a class=\"ep_link_minor\" href=\"https:\/\/henshmi.github.io\/Classic-Pool-Game\/\" target=\"_blank\" rel=\"noopener\">Live Demo<\/a><\/p>","protected":false},"excerpt":{"rendered":"<p>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 &#8230; <a title=\"Make 8 Ball POOL Multiplayer Billiards Game Using JavaScript\" class=\"read-more\" href=\"https:\/\/www.edopedia.com\/blog\/make-8-ball-pool-multiplayer-billiards-game-using-javascript\/\" aria-label=\"Read more about Make 8 Ball POOL Multiplayer Billiards Game Using JavaScript\">Read more<\/a><\/p>\n","protected":false},"author":1,"featured_media":3088,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[112],"tags":[],"class_list":["post-3077","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-tutorials"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.5 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Make 8 Ball POOL Multiplayer Billiards Game Using JavaScript<\/title>\n<meta name=\"description\" content=\"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\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.edopedia.com\/blog\/make-8-ball-pool-multiplayer-billiards-game-using-javascript\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Make 8 Ball POOL Multiplayer Billiards Game Using JavaScript\" \/>\n<meta property=\"og:description\" content=\"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\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.edopedia.com\/blog\/make-8-ball-pool-multiplayer-billiards-game-using-javascript\/\" \/>\n<meta property=\"og:site_name\" content=\"Edopedia\" \/>\n<meta property=\"article:author\" content=\"trulyfurqan\" \/>\n<meta property=\"article:published_time\" content=\"2022-09-11T20:24:32+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2022-09-11T20:25:32+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.edopedia.com\/blog\/wp-content\/uploads\/2022\/09\/javascript_8_ball_pool_game.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"880\" \/>\n\t<meta property=\"og:image:height\" content=\"495\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Furqan\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Furqan\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"22 minutes\" \/>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Make 8 Ball POOL Multiplayer Billiards Game Using JavaScript","description":"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","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.edopedia.com\/blog\/make-8-ball-pool-multiplayer-billiards-game-using-javascript\/","og_locale":"en_US","og_type":"article","og_title":"Make 8 Ball POOL Multiplayer Billiards Game Using JavaScript","og_description":"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","og_url":"https:\/\/www.edopedia.com\/blog\/make-8-ball-pool-multiplayer-billiards-game-using-javascript\/","og_site_name":"Edopedia","article_author":"trulyfurqan","article_published_time":"2022-09-11T20:24:32+00:00","article_modified_time":"2022-09-11T20:25:32+00:00","og_image":[{"width":880,"height":495,"url":"https:\/\/www.edopedia.com\/blog\/wp-content\/uploads\/2022\/09\/javascript_8_ball_pool_game.jpg","type":"image\/jpeg"}],"author":"Furqan","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Furqan","Est. reading time":"22 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.edopedia.com\/blog\/make-8-ball-pool-multiplayer-billiards-game-using-javascript\/#article","isPartOf":{"@id":"https:\/\/www.edopedia.com\/blog\/make-8-ball-pool-multiplayer-billiards-game-using-javascript\/"},"author":{"name":"Furqan","@id":"https:\/\/www.edopedia.com\/blog\/#\/schema\/person\/3951cb19e3aa56df09e408c98aa02339"},"headline":"Make 8 Ball POOL Multiplayer Billiards Game Using JavaScript","datePublished":"2022-09-11T20:24:32+00:00","dateModified":"2022-09-11T20:25:32+00:00","mainEntityOfPage":{"@id":"https:\/\/www.edopedia.com\/blog\/make-8-ball-pool-multiplayer-billiards-game-using-javascript\/"},"wordCount":357,"commentCount":0,"publisher":{"@id":"https:\/\/www.edopedia.com\/blog\/#organization"},"image":{"@id":"https:\/\/www.edopedia.com\/blog\/make-8-ball-pool-multiplayer-billiards-game-using-javascript\/#primaryimage"},"thumbnailUrl":"https:\/\/www.edopedia.com\/blog\/wp-content\/uploads\/2022\/09\/javascript_8_ball_pool_game.jpg","articleSection":["Tutorials"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.edopedia.com\/blog\/make-8-ball-pool-multiplayer-billiards-game-using-javascript\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.edopedia.com\/blog\/make-8-ball-pool-multiplayer-billiards-game-using-javascript\/","url":"https:\/\/www.edopedia.com\/blog\/make-8-ball-pool-multiplayer-billiards-game-using-javascript\/","name":"Make 8 Ball POOL Multiplayer Billiards Game Using JavaScript","isPartOf":{"@id":"https:\/\/www.edopedia.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.edopedia.com\/blog\/make-8-ball-pool-multiplayer-billiards-game-using-javascript\/#primaryimage"},"image":{"@id":"https:\/\/www.edopedia.com\/blog\/make-8-ball-pool-multiplayer-billiards-game-using-javascript\/#primaryimage"},"thumbnailUrl":"https:\/\/www.edopedia.com\/blog\/wp-content\/uploads\/2022\/09\/javascript_8_ball_pool_game.jpg","datePublished":"2022-09-11T20:24:32+00:00","dateModified":"2022-09-11T20:25:32+00:00","description":"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","breadcrumb":{"@id":"https:\/\/www.edopedia.com\/blog\/make-8-ball-pool-multiplayer-billiards-game-using-javascript\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.edopedia.com\/blog\/make-8-ball-pool-multiplayer-billiards-game-using-javascript\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.edopedia.com\/blog\/make-8-ball-pool-multiplayer-billiards-game-using-javascript\/#primaryimage","url":"https:\/\/www.edopedia.com\/blog\/wp-content\/uploads\/2022\/09\/javascript_8_ball_pool_game.jpg","contentUrl":"https:\/\/www.edopedia.com\/blog\/wp-content\/uploads\/2022\/09\/javascript_8_ball_pool_game.jpg","width":880,"height":495,"caption":"JavaScript 8 Ball Pool Multiplayer Game"},{"@type":"BreadcrumbList","@id":"https:\/\/www.edopedia.com\/blog\/make-8-ball-pool-multiplayer-billiards-game-using-javascript\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.edopedia.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Make 8 Ball POOL Multiplayer Billiards Game Using JavaScript"}]},{"@type":"WebSite","@id":"https:\/\/www.edopedia.com\/blog\/#website","url":"https:\/\/www.edopedia.com\/blog\/","name":"Edopedia","description":"Coding\/Programming Blog","publisher":{"@id":"https:\/\/www.edopedia.com\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.edopedia.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/www.edopedia.com\/blog\/#organization","name":"Edopedia","url":"https:\/\/www.edopedia.com\/blog\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.edopedia.com\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/www.edopedia.com\/blog\/wp-content\/uploads\/2017\/10\/edopedia_icon_text_10.jpg","contentUrl":"https:\/\/www.edopedia.com\/blog\/wp-content\/uploads\/2017\/10\/edopedia_icon_text_10.jpg","width":400,"height":100,"caption":"Edopedia"},"image":{"@id":"https:\/\/www.edopedia.com\/blog\/#\/schema\/logo\/image\/"}},{"@type":"Person","@id":"https:\/\/www.edopedia.com\/blog\/#\/schema\/person\/3951cb19e3aa56df09e408c98aa02339","name":"Furqan","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/secure.gravatar.com\/avatar\/e5e68aef3ad8f0b83d56f4953c512c8e57bd2e6dc64daec33b5d0495d9058f51?s=96&d=mm&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/e5e68aef3ad8f0b83d56f4953c512c8e57bd2e6dc64daec33b5d0495d9058f51?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/e5e68aef3ad8f0b83d56f4953c512c8e57bd2e6dc64daec33b5d0495d9058f51?s=96&d=mm&r=g","caption":"Furqan"},"description":"Well. I've been working for the past three years as a web designer and developer. I have successfully created websites for small to medium sized companies as part of my freelance career. During that time I've also completed my bachelor's in Information Technology.","sameAs":["http:\/\/www.edopedia.com\/blog\/","trulyfurqan"],"url":"https:\/\/www.edopedia.com\/blog\/author\/furqan\/"}]}},"_links":{"self":[{"href":"https:\/\/www.edopedia.com\/blog\/wp-json\/wp\/v2\/posts\/3077","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.edopedia.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.edopedia.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.edopedia.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.edopedia.com\/blog\/wp-json\/wp\/v2\/comments?post=3077"}],"version-history":[{"count":11,"href":"https:\/\/www.edopedia.com\/blog\/wp-json\/wp\/v2\/posts\/3077\/revisions"}],"predecessor-version":[{"id":3089,"href":"https:\/\/www.edopedia.com\/blog\/wp-json\/wp\/v2\/posts\/3077\/revisions\/3089"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.edopedia.com\/blog\/wp-json\/wp\/v2\/media\/3088"}],"wp:attachment":[{"href":"https:\/\/www.edopedia.com\/blog\/wp-json\/wp\/v2\/media?parent=3077"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.edopedia.com\/blog\/wp-json\/wp\/v2\/categories?post=3077"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.edopedia.com\/blog\/wp-json\/wp\/v2\/tags?post=3077"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}