{"version":3,"sources":["clobber/clobber/BotAction.ts","clobber/clobber/BotManager.ts","clobber/clobber/Point.ts","clobber/clobber/bots/Mushroom.ts","clobber/clobber/bots/PotentialFieldBot.ts","clobber/clobber/bots/RandomBot.ts","clobber/clobber/bots/images/index.ts","clobber/clobber/ID.ts","clobber/clobber/GameRenderer/GameRenderer.ts","clobber/clobber/State.ts","clobber/clobber/Bullet.ts","clobber/clobber/Shapes.ts","clobber/clobber/GameRenderer/ConsoleGameRenderer.ts","clobber/clobber/Game.ts","clobber/clobber/GameRenderer/GuiGameRenderer.ts","App.tsx","clobber/clobber.module.css","clobber/clobber/bots/HumanBot.ts","clobber/prismjs/prism.ts","serviceWorker.ts","index.tsx"],"names":["Action","Direction","BotAction","action","direction","this","Bot","world","id","teammates","Set","add","context","point","fillStyle","beginPath","arc","x","y","botRadius","Math","PI","fill","BotManager","bot","kills","dead","currentAction","shotClock","score","generateId","None","render","JSON","stringify","Point","hypot","eighthPi","Mushroom","frameCounter","win","image","botBulletThresh","botBotThresh","Image","src","bulletRadius","state","shootFrequency","Shoot","getShootDirection","Move","getDodgeDirection","friendlyBot","getClosestFriendlyBot","getAvoidBotDirection","myBot","targetBot","getClosestEnemyBot","getGoToBotDirection","closestBot","closestDistance","Number","MAX_VALUE","bots","forEach","getTeamName","team","distance","me","botDistance","down","abs","up","right","left","UpLeft","UpRight","DownLeft","DownRight","Up","Down","Left","Right","closestBullet","bullets","bullet","bulletWillHit","getAvoidBulletDirection","bulletStepDistance","getXPlus","getYPlus","c","pow","sqrt","ceil","shouldMoveVertical","shouldMoveHorizontal","thresh","shootTargetDirection","target","directionX","directionY","deltaX","deltaY","ratio1","ratio2","tan","drawImage","FieldObject","radius","spread","strength","tangentialModifier","PotentialFieldBot","history","historyClock","historyInterval","maxHistory","shootImage","shooting","closestEnemy","getClosestEnemy","fields","getAvoidEnemyFields","getAvoidBulletFields","getAvoidWallField","getPastBehaviorFields","push","getAttractiveField","reduce","delta","current","width","centerField","height","addDelta","getDirection","updateHistory","length","pop","addition","enemy","angle","atan2","undefined","map","getRepulsiveField","fieldObject","distanceFromWall","index","field","cos","sin","sign","IMAGES","getRandomInt","max","floor","random","RandomBot","toString","substr","GameRenderer","State","myBullets","BotState","BulletState","WorldState","botStepDistance","minStartDistance","killPoints","survivePoints","Object","assign","create","getPrototypeOf","Bullet","owner","Circle","circlesOverlap","circle1","circle2","ConsoleGameRenderer","botManagers","fps","orderedBots","bm","console","log","callback","min","Game","deadBots","gameOver","fpsTimes","renderer","setTimeout","gameLoop","otherBot","numTries","minDistance","Error","getMinDistanceToBot","botManager","renderGameOver","compileResults","collectBotActions","performBotActions","updateBulletPositions","renderFPS","handleCollisions","checkGameOver","now","performance","shift","every","i","managers","generateState","takeTurn","err","error","splice","cloneString","clone","filter","bulletManager","getUpdatedPoint","stepDistance","updatedPoint","bm1","some","bm2","collide","botsCollide","Array","from","addBotCollideKill","bulletIndex","botIndex","botAndBulletCollide","addBulletKill","killerId","killedId","find","bulletPoint","botPoint","bot1","bot2","botCircle1","botCircle2","concat","keys","sort","str","slice","GuiGameRenderer","rootElement","canvas","document","createElement","appendChild","getContext","fillRect","clearScreen","font","fillText","round","textBaseline","colX","winnerId","String","requestAnimationFrame","addBots","game","addBotToGame","HumanBot","App","gameRootRef","React","useRef","botCodeRef","gameRef","handleNewGame","setRenderer","handleAddBot","MyBot","eval","innerText","handleStartGame","start","useEffect","className","ref","type","onClick","styles","botPre","contentEditable","suppressContentEditableWarning","module","exports","KeyCodes","addEventListener","event","captureKey","releaseKey","indexOf","code","preventDefault","shootDirection","moveDirection","Prism","plugins","NormalizeWhitespace","setDefaults","Boolean","window","location","hostname","match","ReactDOM","StrictMode","getElementById","navigator","serviceWorker","ready","then","registration","unregister","catch","message"],"mappings":"0MAAYA,EAMAC,E,iBANAD,K,YAAAA,E,YAAAA,E,eAAAA,M,cAMAC,K,YAAAA,E,QAAAA,E,cAAAA,E,YAAAA,E,YAAAA,E,kBAAAA,E,sBAAAA,E,oBAAAA,E,iBAAAA,M,SAYSC,EACjB,WAA4BC,EAAgCC,GAAuB,yBAAvDD,SAAsD,KAAtBC,YACxDC,KAAKF,OAASA,EACdE,KAAKD,UAAYA,I,qGChBHE,EAAtB,WAII,WAAsBC,GAAoB,yBAApBA,QAAmB,KAH/BC,QAG+B,OAF/BC,eAE+B,EACrCJ,KAAKG,GAAK,GACVH,KAAKI,UAAY,IAAIC,IAN7B,kDASiBF,GACTH,KAAKG,GAAKA,IAVlB,8BAcQ,OAAOH,KAAKG,KAdpB,uCAiB4BA,GACpBH,KAAKI,UAAUE,IAAIH,KAlB3B,6BA4BkBI,EAAmCC,GAC7CD,EAAQE,UAAY,UACpBF,EAAQG,YACRH,EAAQI,IAAIH,EAAMI,EAAGJ,EAAMK,EAAGb,KAAKE,MAAMY,UAAW,EAAG,EAAIC,KAAKC,IAChET,EAAQU,OACRV,EAAQE,UAAY,cAjC5B,KAuCMS,E,WAUF,WAAYC,EAAUX,GAAe,yBAT9BL,QAS6B,OAR7BgB,SAQ6B,OAP7BX,WAO6B,OAN7BY,WAM6B,OAL7BC,UAK6B,OAJ7BC,mBAI6B,OAH7BC,eAG6B,OAF7BC,WAE6B,EAChCxB,KAAKG,GAAKsB,cACVzB,KAAKsB,cAAgB,IAAIzB,IAAUF,IAAO+B,KAAM9B,IAAU8B,MAC1D1B,KAAKmB,IAAMA,EACXnB,KAAKQ,MAAQA,EACbR,KAAKoB,MAAQ,GACbpB,KAAKqB,MAAO,EACZrB,KAAKuB,UAAY,EACjBvB,KAAKwB,MAAQ,E,mDAGVjB,GACHP,KAAKmB,IAAIQ,OAAOpB,EAASP,KAAKQ,S,iCAI9B,OAAOoB,KAAKC,UAAU,CAClBV,IAAKnB,KAAKmB,IACVX,MAAOR,KAAKQ,MACZY,MAAOpB,KAAKoB,MACZC,KAAMrB,KAAKqB,MACZ,KAAM,O,KAIFH,O,iFC/EMY,E,WAIjB,WAAYlB,EAAWC,GAAY,yBAH5BD,OAG2B,OAF3BC,OAE2B,EAC9Bb,KAAKY,EAAIA,EACTZ,KAAKa,EAAIA,E,qDAGJL,GACL,OAAOO,KAAKgB,MAAMvB,EAAMI,EAAIZ,KAAKY,EAAGJ,EAAMK,EAAIb,KAAKa,K,8BAInD,OAAO,IAAIiB,EAAM9B,KAAKY,EAAGZ,KAAKa,K,iCAI9B,MAAM,IAAN,OAAWb,KAAKY,EAAhB,aAAsBZ,KAAKa,EAA3B,S,oHCbFmB,EAAWjB,KAAKC,GAAK,EAIdiB,EAAb,kDAOI,WAAsB/B,GAAoB,IAAD,8BACrC,cAAMA,IADYA,QAAmB,EANjCgC,kBAMiC,IALjCC,SAKiC,IAJxBC,WAIwB,IAHxBC,qBAGwB,IAFxBC,kBAEwB,EAErC,EAAKJ,aAAe,EAEpB,EAAKC,KAAM,EAEX,EAAKC,MAAQ,IAAIG,MACjB,EAAKH,MAAMI,IAhBC,iiBAkBZ,EAAKH,gBAAmBnC,EAAMY,UAAYZ,EAAMuC,aAAgB,GAChE,EAAKH,aAAe,EAAIpC,EAAMY,UAAY,GAVL,EAP7C,oDAqBQ,OAAOd,KAAKG,KArBpB,+BAwBauC,GAEL,IAAI5C,EACAC,EAEJ,GAJAC,KAAKkC,eAIDlC,KAAKkC,aAAelC,KAAKE,MAAMyC,iBAAmB,EAClD7C,EAASH,IAAOiD,MAChB7C,EAAYC,KAAK6C,kBAAkBH,OAChC,CAIH,GAHA5C,EAASH,IAAOmD,MAChB/C,EAAYC,KAAK+C,kBAAkBL,MAEjB9C,IAAU8B,KAAM,CAC9B,IAAMsB,EAAchD,KAAKiD,sBAAsBP,GAC3B,OAAhBM,IACAjD,EAAYC,KAAKkD,qBAAqBR,EAAMS,MAAM3C,MAAOwC,EAAYxC,QAI7E,GAAIT,IAAcH,IAAU8B,KAAM,CAC9B,IAAM0B,EAAYpD,KAAKqD,mBAAmBX,GACxB,OAAdU,EACApD,KAAKmC,KAAM,EAEXpC,EAAYC,KAAKsD,oBAAoBZ,EAAMS,MAAM3C,MAAO4C,EAAU5C,QAK9E,OAAO,IAAIX,IAAUC,EAAQC,KArDrC,4CAwD0B2C,GAAgC,IAAD,OAC7Ca,EAAa,KACbC,EAAkBC,OAAOC,UAS7B,OARAhB,EAAMiB,KAAKC,SAAQ,SAAAzC,GACX,EAAK0C,gBAAkB1C,EAAI2C,MACxBpB,EAAMS,MAAM3C,MAAMuD,SAAS5C,EAAIX,OAAS,IACxCkC,EAAMS,MAAM3C,MAAMuD,SAAS5C,EAAIX,QAAUgD,IAC5CD,EAAapC,EACbqC,EAAkBd,EAAMS,MAAM3C,MAAMuD,SAAS5C,EAAIX,WAGlD+C,IAnEf,yCAsEuBb,GAAgC,IAAD,OAC1Ca,EAAa,KACbC,EAAkBC,OAAOC,UAQ7B,OAPAhB,EAAMiB,KAAKC,SAAQ,SAAAzC,GACX,EAAK0C,gBAAkB1C,EAAI2C,MACxBpB,EAAMS,MAAM3C,MAAMuD,SAAS5C,EAAIX,QAAUgD,IAC5CD,EAAapC,EACbqC,EAAkBd,EAAMS,MAAM3C,MAAMuD,SAAS5C,EAAIX,WAGlD+C,IAhFf,2CAmFyBS,EAAW7C,GAC5B,IAAKA,EACD,OAAOvB,IAAU8B,KAErB,IAAIuC,EAAcjE,KAAKsC,aAEnB4B,EAAOF,EAAGnD,EAAIE,KAAKoD,IAAIhD,EAAIN,EAAIoD,GAC/BG,EAAKJ,EAAGnD,EAAIE,KAAKoD,IAAIhD,EAAIN,EAAIoD,GAC7BI,EAAQL,EAAGpD,EAAIG,KAAKoD,IAAIhD,EAAIP,EAAIqD,GAChCK,EAAON,EAAGpD,EAAIG,KAAKoD,IAAIhD,EAAIP,EAAIqD,GAEnC,OAAIG,GAAME,EACC1E,IAAU2E,OACVH,GAAMC,EACNzE,IAAU4E,QACVN,GAAQI,EACR1E,IAAU6E,SACVP,GAAQG,EACRzE,IAAU8E,UACVN,EACAxE,IAAU+E,GACVT,EACAtE,IAAUgF,KACVN,EACA1E,IAAUiF,KACVR,EACAzE,IAAUkF,MAGdlF,IAAU8B,OAhHzB,0CAmHwBsC,EAAW7C,GAC3B,IAAKA,EACD,OAAOvB,IAAU8B,KAErB,IAEI0C,EAAKJ,EAAGnD,EAAIE,KAAKoD,IAAIhD,EAAIN,EAFX,IAGdqD,EAAOF,EAAGnD,EAAIE,KAAKoD,IAAIhD,EAAIN,EAHb,IAIdyD,EAAON,EAAGpD,EAAIG,KAAKoD,IAAIhD,EAAIP,EAJb,IAKdyD,EAAQL,EAAGpD,EAAIG,KAAKoD,IAAIhD,EAAIP,EALd,IAOlB,OAAIwD,GAAME,EACC1E,IAAU2E,OACVH,GAAMC,EACNzE,IAAU4E,QACVN,GAAQI,EACR1E,IAAU6E,SACVP,GAAQG,EACRzE,IAAU8E,UACVN,EACAxE,IAAU+E,GACVT,EACAtE,IAAUgF,KACVN,EACA1E,IAAUiF,KACVR,EACAzE,IAAUkF,MAGdlF,IAAU8B,OAhJzB,wCAmJsBgB,GAA0B,IAAD,OACnCqC,EAAgB,KAChBvB,EAAkBC,OAAOC,UAY7B,OATAhB,EAAMsC,QAAQpB,SAAQ,SAACqB,GACfvC,EAAMS,MAAM3C,MAAMuD,SAASkB,EAAOzE,OAHrB,IAIVkC,EAAMS,MAAM3C,MAAMuD,SAASkB,EAAOzE,QAAUgD,GAC5C,EAAK0B,cAAcxC,EAAMS,MAAM3C,MAAOyE,KACzCF,EAAgBE,EAChBzB,EAAkBd,EAAMS,MAAM3C,MAAMuD,SAASkB,EAAOzE,WAItC,OAAlBuE,EACOnF,IAAU8B,KAEV1B,KAAKmF,wBAAwBzC,EAAMS,MAAM3C,MAAOuE,KApKnE,+BAwKaE,GACL,OAAIA,EAAOlF,YAAcH,IAAUiF,MACvB7E,KAAKE,MAAMkF,mBAEZpF,KAAKE,MAAMkF,qBA5K9B,+BAgLaH,GACL,OAAIA,EAAOlF,YAAcH,IAAU+E,IACvB3E,KAAKE,MAAMkF,mBAEZpF,KAAKE,MAAMkF,qBApL9B,oCAwLkBpB,EAAWiB,GACrB,OAAKjF,KAAKqF,SAASJ,GAAU,GAAKA,EAAOzE,MAAMI,EAAIoD,EAAGpD,GAC9CZ,KAAKqF,SAASJ,GAAU,GAAKA,EAAOzE,MAAMI,EAAIoD,EAAGpD,GACjDZ,KAAKsF,SAASL,GAAU,GAAKA,EAAOzE,MAAMK,EAAImD,EAAGnD,GACjDb,KAAKsF,SAASL,GAAU,GAAKA,EAAOzE,MAAMK,EAAImD,EAAGnD,IAEjDmD,EAAGD,SAASkB,EAAOzE,OAASR,KAAKqC,gBAAkBrC,KAAKE,MAAMkF,oBAAiD,IAA1BpF,KAAKqF,SAASJ,IACzE,IAA1BjF,KAAKsF,SAASL,GAAiB,EAAI,KA/LnD,8CAsM4BjB,EAAWiB,GAC/B,IAAIlF,EAAYH,IAAU8B,KAC1B,GAA8B,IAA1B1B,KAAKqF,SAASJ,IAA2C,IAA1BjF,KAAKsF,SAASL,GAAe,CAC5D,IAAIM,IAAMvB,EAAGpD,EAAIqE,EAAOzE,MAAMI,GAAKZ,KAAKqF,SAASJ,IAAWjB,EAAGnD,EAAIoE,EAAOzE,MAAMK,GAAKb,KAAKsF,SAASL,KAAYlE,KAAKyE,IAAIxF,KAAKqF,SAASJ,GAAS,GAAKlE,KAAKyE,IAAIxF,KAAKsF,SAASL,GAAS,IAChLlB,EAAWhD,KAAK0E,KAAK1E,KAAKyE,IAAKxB,EAAGpD,EAAIqE,EAAOzE,MAAMI,EAAI2E,EAAIvF,KAAKqF,SAASJ,GAAU,GAAKlE,KAAKyE,IAAKxB,EAAGnD,EAAIoE,EAAOzE,MAAMK,EAAI0E,EAAIvF,KAAKsF,SAASL,GAAU,IAEtJjF,KAAKqF,SAASJ,GAAUjF,KAAKsF,SAASL,GAAU,GAAKlE,KAAK2E,KAAK3E,KAAKoD,IAAIJ,KAAc/D,KAAKqC,gBAEvFtC,EADAiE,EAAGnD,EAAImD,EAAGpD,EAAIqE,EAAOzE,MAAMK,EAAIoE,EAAOzE,MAAMI,GAAK,EACrChB,IAAU6E,SAEV7E,IAAU4E,QAGrBxE,KAAKqF,SAASJ,GAAUjF,KAAKsF,SAASL,IAAW,GAAKlE,KAAK2E,KAAK3E,KAAKoD,IAAIJ,KAAc/D,KAAKqC,kBAE7FtC,EADAiE,EAAGnD,EAAImD,EAAGpD,EAAIqE,EAAOzE,MAAMK,EAAIoE,EAAOzE,MAAMI,EAAI,EACpChB,IAAU8E,UAEV9E,IAAU2E,aAI1BvE,KAAK2F,mBAAmB3B,EAAIiB,KAExBlF,EADAiE,EAAGnD,EAAIoE,EAAOzE,MAAMK,EACRjB,IAAU+E,GAEV/E,IAAUgF,MAG1B5E,KAAK4F,qBAAqB5B,EAAIiB,KAGtBlF,EAFJiE,EAAGpD,EAAIqE,EAAOzE,MAAMI,EAChBb,IAAcH,IAAU+E,GACZ/E,IAAU2E,OACfxE,IAAcH,IAAUgF,KACnBhF,IAAU6E,SAEV7E,IAAUiF,KAGtB9E,IAAcH,IAAU+E,GACZ/E,IAAU4E,QACfzE,IAAcH,IAAUgF,KACnBhF,IAAU8E,UAEV9E,IAAUkF,OAKtC,OAAO/E,IAtPf,yCAyPuBiE,EAAWiB,GAC1B,IAAIY,EAAS9E,KAAKoD,IAAIpD,KAAKoD,IAAIH,EAAGnD,EAAIoE,EAAOzE,MAAMK,GAAKb,KAAKqC,iBAC7D,OAASrC,KAAKqF,SAASJ,GAAU,GAAKA,EAAOzE,MAAMI,EAAIoD,EAAGpD,GAClDZ,KAAKqF,SAASJ,GAAU,GAAKA,EAAOzE,MAAMI,EAAIoD,EAAGpD,IACjDiF,GAAU7F,KAAKqC,iBAAmBwD,EAAS,IA7P3D,2CAgQyB7B,EAAWiB,GAC5B,IAAIY,EAAS9E,KAAKoD,IAAIpD,KAAKoD,IAAIH,EAAGpD,EAAIqE,EAAOzE,MAAMI,GAAKZ,KAAKqC,iBAC7D,OAASrC,KAAKsF,SAASL,GAAU,GAAKA,EAAOzE,MAAMK,EAAImD,EAAGnD,GAClDb,KAAKsF,SAASL,GAAU,GAAKA,EAAOzE,MAAMK,EAAImD,EAAGnD,IACjDgF,GAAU7F,KAAKqC,iBAAmBwD,EAAS,IApQ3D,wCAuQsBnD,GAA0B,IACpCa,EADmC,OAEnCC,EAAkBC,OAAOC,UAS7B,OARAhB,EAAMiB,KAAKC,SAAQ,SAAAzC,GACX,EAAK0C,gBAAkB1C,EAAI2C,MACxBpB,EAAMS,MAAM3C,MAAMuD,SAAS5C,EAAIX,OAASgD,IAC3CD,EAAapC,EACbqC,EAAkBd,EAAMS,MAAM3C,MAAMuD,SAAS5C,EAAIX,WAIrD+C,EACOvD,KAAK8F,qBAAqBpD,EAAMS,MAAM3C,MAAO+C,EAAW/C,OAE5DZ,IAAU8B,OArRzB,2CAwRyBsC,EAAW+B,GAC5B,IAAIC,EAAapG,IAAU8B,KACvBuE,EAAarG,IAAU8B,KACvB3B,EAAYH,IAAU8B,KACtBwE,EAASH,EAAOnF,EAAIoD,EAAGpD,EACvBuF,EAASJ,EAAOlF,EAAImD,EAAGnD,EAc3B,GAZIqF,EAAS,EACTF,EAAapG,IAAUkF,MAChBoB,EAAS,IAChBF,EAAapG,IAAUiF,MAGvBsB,EAAS,EACTF,EAAarG,IAAUgF,KAChBuB,EAAS,IAChBF,EAAarG,IAAU+E,IAGZ,IAAXuB,EACAnG,EAAYkG,OACT,GAAe,IAAXE,EACPpG,EAAYiG,MACT,CACH,IAAII,EAASrF,KAAKoD,IAAIgC,GAAUpF,KAAKoD,IAAI+B,GACrCG,EAAStF,KAAKoD,IAAI+B,GAAUnF,KAAKoD,IAAIgC,GACrCC,GAAUrF,KAAKuF,IAAItE,GACnBjC,EAAYiG,EACLK,GAAUtF,KAAKuF,IAAItE,GAC1BjC,EAAYkG,EAERA,IAAerG,IAAU+E,IAAMqB,IAAepG,IAAUiF,KACxD9E,EAAYH,IAAU2E,OACf0B,IAAerG,IAAU+E,IAAMqB,IAAepG,IAAUkF,MAC/D/E,EAAYH,IAAU4E,QACfyB,IAAerG,IAAUgF,MAAQoB,IAAepG,IAAUiF,KACjE9E,EAAYH,IAAU6E,SACfwB,IAAerG,IAAUgF,MAAQoB,IAAepG,IAAUkF,QACjE/E,EAAYH,IAAU8E,WAIlC,OAAO3E,IAlUf,6BAqUWQ,EAAmCC,GACtCD,EAAQgG,UAAUvG,KAAKoC,MACnB5B,EAAMI,EAAIZ,KAAKE,MAAMY,UACrBN,EAAMK,EAAIb,KAAKE,MAAMY,UACE,EAAvBd,KAAKE,MAAMY,UACY,EAAvBd,KAAKE,MAAMY,aA1UvB,oCAoVQ,MAAO,eApVf,GAA8Bb,M,oICMxBuG,EASF,WACWhG,EACAiG,EACAC,EACAC,GAER,IADQC,EACT,uDADsC,EACtC,yBALSpG,QAKT,KAJSiG,SAIT,KAHSC,SAGT,KAFSC,WAET,KADSC,sBAKMC,E,kDAUjB,WAAsB3G,GAAoB,IAAD,8BACrC,cAAMA,IADYA,QAAmB,EATjCqB,eASiC,IARjCuF,aAQiC,IAPjCC,kBAOiC,IANxBC,qBAMwB,IALxBC,gBAKwB,IAJxB7E,WAIwB,IAHxB8E,gBAGwB,IAFjCC,cAEiC,EAErC,EAAK5F,UAAY,EAEjB,EAAKuF,QAAU,GACf,EAAKC,aAAe,EACpB,EAAKC,gBAAkB,GACvB,EAAKC,WAAa,IAElB,EAAK7E,MAAQ,IAAIG,MACjB,EAAKH,MAAMI,IA1CD,6ZA2CV,EAAK0E,WAAa,IAAI3E,MACtB,EAAK2E,WAAW1E,IA3CC,6jBA4CjB,EAAK2E,UAAW,EAbqB,E,oDAiBrC,OAAOnH,KAAKG,K,+BAGPuC,GACL1C,KAAKuB,YACLvB,KAAKmH,UAAW,EAEhB,IACIpH,EADAD,EAASH,IAAO+B,KAGd0F,EAAepH,KAAKqH,gBAAgB3E,GAE1C,GAAI1C,KAAKuB,UAAYvB,KAAKE,MAAMyC,iBAAmB,GAAKyE,EAOpD,OALApH,KAAKuB,UAAY,EACjBzB,EAASH,IAAOiD,MAChB7C,EAAYC,KAAK6C,kBAAkBH,EAAO0E,GAC1CpH,KAAKmH,UAAW,EAET,IAAItH,IAAUC,EAAQC,GAIjC,IAAIuH,EAAM,sBAGHtH,KAAKuH,oBAAoB7E,IAHtB,YAIH1C,KAAKwH,qBAAqB9E,IAJvB,CAMN1C,KAAKyH,kBAAkB/E,IANjB,YAQH1C,KAAK0H,sBAAsBhF,KAG9B0E,GACAE,EAAOK,KAAK3H,KAAK4H,mBAAmBlF,EAAO,IAAI8D,EAAYY,EAAa5G,MAAOR,KAAKE,MAAMY,UAAW,EAAG,KAhCzF,MAkCIwG,EAAOO,QAC1B,SAACC,EAAiBC,GAAlB,MAAwC,CAACD,EAAM,GAAKC,EAAQ,GAAID,EAAM,GAAKC,EAAQ,OAnCpE,mBAkCd7B,EAlCc,KAkCNC,EAlCM,KAsCnB,GACIzD,EAAMS,MAAM3C,MAAMI,EAA2B,EAAvBZ,KAAKE,MAAMY,WAC9B4B,EAAMS,MAAM3C,MAAMI,EAAIZ,KAAKE,MAAM8H,MAA+B,EAAvBhI,KAAKE,MAAMY,WACpD4B,EAAMS,MAAM3C,MAAMK,EAA2B,EAAvBb,KAAKE,MAAMY,WACjC4B,EAAMS,MAAM3C,MAAMK,EAAIb,KAAKE,MAAM8H,MAA+B,EAAvBhI,KAAKE,MAAMY,UACzD,CAEE,IAAMmH,EAAc,IAAIzB,EAAY,IAAI1E,IAAM9B,KAAKE,MAAM8H,MAAQ,EAAGhI,KAAKE,MAAMgI,OAAS,GAAI,EAAG,GAAI,EAAGnH,KAAKC,GAAK,GAFlH,EAGqBhB,KAAKmI,SAAS,CAACjC,EAAQC,GAASnG,KAAK4H,mBAAmBlF,EAAOuF,IAHpF,mBAGG/B,EAHH,KAGWC,EAHX,KAcF,OANkB,QAFlBpG,EAAYC,KAAKoI,aAAalC,EAAQC,MAGlCrG,EAASH,IAAOmD,MAGpB9C,KAAKqI,cAAc3F,GAEZ,IAAI7C,IAAUC,EAAQC,K,oCAGnB2C,GACN1C,KAAK8G,QAAQwB,QAAUtI,KAAKiH,YAC5BjH,KAAK8G,QAAQyB,MAGjBvI,KAAK+G,eACD/G,KAAK+G,aAAe/G,KAAKgH,kBAAoB,IAC7ChH,KAAK+G,aAAe,EACpB/G,KAAK8G,QAAQa,KAAKjF,EAAMS,MAAM3C,U,+BAI7BuH,EAAmBS,GACxB,MAAO,CAACT,EAAQ,GAAKS,EAAS,GAAIT,EAAQ,GAAKS,EAAS,M,wCAG1C9F,EAAc+F,GAE5B,IAAMvC,EAASuC,EAAMjI,MAAMI,EAAI8B,EAAMS,MAAM3C,MAAMI,EAC3CuF,EAASzD,EAAMS,MAAM3C,MAAMK,EAAI4H,EAAMjI,MAAMK,EAC3C6H,EAAqC,IAA7B3H,KAAK4H,MAAMxC,EAAQD,GAAgBnF,KAAKC,GAEtD,OAAI0H,IAAU,IAAMA,EAAQ,GACjB9I,IAAUkF,MAEjB4D,GAAS,IAAMA,EAAQ,GAChB9I,IAAU4E,QAEjBkE,GAAS,IAAMA,EAAQ,IAChB9I,IAAU+E,GAEjB+D,GAAS,KAAOA,EAAQ,IACjB9I,IAAU2E,OAEjBmE,GAAS,KAAOA,GAAS,IAClB9I,IAAUiF,KAEjB6D,IAAU,KAAOA,GAAS,IACnB9I,IAAU6E,SAEjBiE,IAAU,KAAOA,GAAS,GACnB9I,IAAUgF,KAEjB8D,IAAU,IAAMA,GAAS,GAClB9I,IAAU8E,UAGd9E,IAAU8B,O,mCAGRwE,EAAgBC,GACzB,IAAIpG,EAAYH,IAAU8B,KA8B1B,OA5Be,IAAXyE,IAEIpG,EADAoG,EAAS,EACGvG,IAAU+E,GAEV/E,IAAUgF,MAIf,IAAXsB,IAGQnG,EAFJmG,EAAS,EACLnG,IAAcH,IAAU+E,GACZ/E,IAAU2E,OACfxE,IAAcH,IAAUgF,KACnBhF,IAAU6E,SAEV7E,IAAUiF,KAGtB9E,IAAcH,IAAU+E,GACZ/E,IAAU4E,QACfzE,IAAcH,IAAUgF,KACnBhF,IAAU8E,UAEV9E,IAAUkF,OAK3B/E,I,sCAGK2C,GAAqC,IAAD,OAC5Ca,OAAmCqF,EACnCpF,EAAkBC,OAAOC,UAU7B,OARAhB,EAAMiB,KAAKC,SAAQ,SAAAzC,GACX,EAAK0C,gBAAkB1C,EAAI2C,MACxBpB,EAAMS,MAAM3C,MAAMuD,SAAS5C,EAAIX,QAAUgD,IAC5CD,EAAapC,EACbqC,EAAkBd,EAAMS,MAAM3C,MAAMuD,SAAS5C,EAAIX,WAIlD+C,I,0CAGSb,GAA2B,IAAD,OAC1C,OAAOA,EAAMiB,KAAKkF,KAAI,SAAA1H,GAClB,IACMuF,EAAS,EAAI,EAAKxG,MAAMY,UADH,GAE3B,OAAO,EAAKgI,kBAAkBpG,EAAO,IAAI8D,EAAYrF,EAAIX,MAAO,EAAKN,MAAMY,UAAW4F,EAAQ,S,2CAIjFhE,GAAe,IAAD,OAC/B,OAAOA,EAAMsC,QAAQ6D,KAAI,SAAA5D,GACrB,IACMyB,EAAS,EAAI,EAAKxG,MAAMuC,aADH,GAErBsG,EAAc,IAAIvC,EAAYvB,EAAOzE,MAAO,EAAKN,MAAMuC,aAAciE,EAAQ,GACnF,OAAO,EAAKoC,kBAAkBpG,EAAOqG,Q,wCAI3BrG,GAEd,IAAMsG,EAA0C,EAAvBhJ,KAAKE,MAAMY,UAChCoF,EAAS,EACTC,EAAS,EAcb,OAZIzD,EAAMS,MAAM3C,MAAMI,EAAIoI,EACtB9C,EAAS8C,EACFtG,EAAMS,MAAM3C,MAAMI,EAAIZ,KAAKE,MAAM8H,MAAQgB,IAChD9C,GAAU8C,GAGVtG,EAAMS,MAAM3C,MAAMK,EAAImI,EACtB7C,EAAS6C,EACFtG,EAAMS,MAAM3C,MAAMK,EAAIb,KAAKE,MAAM8H,MAAQgB,IAChD7C,GAAU6C,GAGP,CACHhJ,KAAK8I,kBAAkBpG,EAAO,IAAI8D,EAAY,IAAI1E,IAAM,EAAG,GAAI9B,KAAKE,MAAMY,UAAkC,EAAvBd,KAAKE,MAAMY,UAAe,IAC/Gd,KAAK8I,kBAAkBpG,EAAO,IAAI8D,EAAY,IAAI1E,IAAM,EAAG,GAAI9B,KAAKE,MAAMY,UAAkC,EAAvBd,KAAKE,MAAMY,UAAe,IAC/Gd,KAAK8I,kBAAkBpG,EAAO,IAAI8D,EAAY,IAAI1E,IAAM,EAAG,GAAI9B,KAAKE,MAAMY,UAAkC,EAAvBd,KAAKE,MAAMY,UAAe,IAC/Gd,KAAK8I,kBAAkBpG,EAAO,IAAI8D,EAAY,IAAI1E,IAAM,EAAG,GAAI9B,KAAKE,MAAMY,UAAkC,EAAvBd,KAAKE,MAAMY,UAAe,KACjH+G,QAAO,SAACC,EAAOC,GAAR,MAAoB,CAACD,EAAM,GAAKC,EAAQ,GAAID,EAAM,GAAKC,EAAQ,MAAK,CAAC7B,EAAQC,M,4CAGpEzD,GAAe,IAAD,OAChC,OAAO1C,KAAK8G,QAAQ+B,KAAI,SAACrI,EAAOyI,EAAOnC,GACnC,IAAMH,GAAYG,EAAQwB,OAASW,IAAU,EAAI,EAAKhC,YAChDP,EAAS,EAAKxG,MAAMuC,aACpBsG,EAAc,IAAIvC,EAAYhG,EAAOkG,EAAQA,EAAQC,EAAU5F,KAAKC,GAAK,GAC/E,OAAO,EAAK8H,kBAAkBpG,EAAOqG,Q,yCAI1BrG,EAAcwG,GAE7B,IAAMnF,EAAWrB,EAAMS,MAAM3C,MAAMuD,SAASmF,EAAM1I,OAC5CkI,EAAQ3H,KAAK4H,MAAOO,EAAM1I,MAAMK,EAAI6B,EAAMS,MAAM3C,MAAMK,EAAKqI,EAAM1I,MAAMI,EAAI8B,EAAMS,MAAM3C,MAAMI,GAAMsI,EAAMtC,mBAE3GV,EAAS,EACTC,EAAS,EAab,OAXIpC,EAAWmF,EAAMzC,QACjBP,EAAS,EACTC,EAAS,GACF+C,EAAMzC,QAAU1C,GAAYA,GAAYmF,EAAMxC,OAASwC,EAAMzC,QACpEP,EAASgD,EAAMvC,UAAY5C,EAAWmF,EAAMzC,QAAU1F,KAAKoI,IAAIT,GAC/DvC,EAAS+C,EAAMvC,UAAY5C,EAAWmF,EAAMzC,QAAU1F,KAAKqI,IAAIV,IACxD3E,EAAWmF,EAAMxC,OAASwC,EAAMzC,SACvCP,EAASgD,EAAMvC,SAAWuC,EAAMxC,OAAS3F,KAAKoI,IAAIT,GAClDvC,EAAS+C,EAAMvC,SAAWuC,EAAMxC,OAAS3F,KAAKqI,IAAIV,IAG/C,CAACxC,EAAQC,K,wCAGFzD,EAAcwG,GAE5B,IAAMnF,EAAWrB,EAAMS,MAAM3C,MAAMuD,SAASmF,EAAM1I,OAC5CkI,EAAQ3H,KAAK4H,MAAOO,EAAM1I,MAAMK,EAAI6B,EAAMS,MAAM3C,MAAMK,EAAKqI,EAAM1I,MAAMI,EAAI8B,EAAMS,MAAM3C,MAAMI,GAAMsI,EAAMtC,mBAE3GV,EAAS,EACTC,EAAS,EAab,OAXIpC,EAAWmF,EAAMzC,QACjBP,GAAUnF,KAAKsI,KAAKtI,KAAKoI,IAAIT,IAAUjF,OAAOC,UAC9CyC,GAAUpF,KAAKsI,KAAKtI,KAAKqI,IAAIV,IAAUjF,OAAOC,WACvCwF,EAAMzC,QAAU1C,GAAYA,GAAYmF,EAAMxC,OAASwC,EAAMzC,QACpEP,GAAUgD,EAAMvC,UAAYuC,EAAMxC,OAASwC,EAAMzC,OAAS1C,GAAYhD,KAAKoI,IAAIT,GAC/EvC,GAAU+C,EAAMvC,UAAYuC,EAAMxC,OAASwC,EAAMzC,OAAS1C,GAAYhD,KAAKqI,IAAIV,IACxE3E,EAAWmF,EAAMxC,OAASwC,EAAMzC,SACvCP,EAAS,EACTC,EAAS,GAGN,CAACD,EAAQC,K,6BAGb5F,EAAmCC,GAElCR,KAAKmH,UAAYnH,KAAKuB,UAAY,GAClChB,EAAQgG,UAAUvG,KAAKkH,WACnB1G,EAAMI,EAAIZ,KAAKE,MAAMY,UACrBN,EAAMK,EAAIb,KAAKE,MAAMY,UACE,EAAvBd,KAAKE,MAAMY,UACY,EAAvBd,KAAKE,MAAMY,WAEfP,EAAQgG,UAAUvG,KAAKoC,MACnB5B,EAAMI,EAAIZ,KAAKE,MAAMY,UACrBN,EAAMK,EAAIb,KAAKE,MAAMY,UACE,EAAvBd,KAAKE,MAAMY,UACY,EAAvBd,KAAKE,MAAMY,a,oCAKnB,MAAO,sB,iCAIP,OAAOc,KAAKC,UAAU,CAClB1B,GAAIH,KAAKG,IACV,KAAM,O,GArT8BF,M,8GC5BzCqJ,EAAS,CCNF,ylBACA,iuCACA,i4CACA,qyCACD,s2CDQZ,SAASC,EAAaC,GAClB,OAAOzI,KAAK0I,MAAM1I,KAAK2I,SAAW3I,KAAK0I,MAAMD,I,IAG5BG,E,kDAGjB,WAAsBzJ,GAAoB,IAAD,8BACrC,cAAMA,IADYA,QAAmB,EAFxBkC,WAEwB,EAErC,EAAKA,MAAQ,IAAIG,MACjB,EAAKH,MAAMI,IAbR8G,EAAOvI,KAAK0I,MAAM1I,KAAK2I,SAAWJ,EAAOhB,SAUP,E,oDAOrC,OAAOtI,KAAKG,K,iCAIZ,IAAIL,EACAC,EAEJ,OAAOwJ,EAAa,IAEpB,KAAK,EACDzJ,EAASH,IAAOmD,KAChB,MACJ,KAAK,EACDhD,EAASH,IAAOiD,MAChB,MACJ,QACI9C,EAASH,IAAO+B,KAGpB,OAAO6H,EAAa,IAEpB,KAAK,EACDxJ,EAAYH,IAAU+E,GACtB,MACJ,KAAK,EACD5E,EAAYH,IAAUgF,KACtB,MACJ,KAAK,EACD7E,EAAYH,IAAUiF,KACtB,MACJ,KAAK,EACD9E,EAAYH,IAAUkF,MACtB,MACJ,KAAK,EACD/E,EAAYH,IAAU2E,OACtB,MACJ,KAAK,EACDxE,EAAYH,IAAU4E,QACtB,MACJ,KAAK,EACDzE,EAAYH,IAAU8E,UACtB,MACJ,QACI3E,EAAYH,IAAU8B,KAG1B,OAAO,IAAI7B,IAAUC,EAAQC,K,6BAG1BQ,EAAmCC,GAGtCD,EAAQgG,UAAUvG,KAAKoC,MAAO5B,EAAMI,EAAIZ,KAAKE,MAAMY,UAAWN,EAAMK,EAAIb,KAAKE,MAAMY,UAAkC,EAAvBd,KAAKE,MAAMY,UAAsC,EAAvBd,KAAKE,MAAMY,a,oCAInI,MAAO,c,iCAIP,OAAOc,KAAKC,UAAU,CAClB1B,GAAIH,KAAKG,IACV,KAAM,O,GAxEsBF,M,6BEhBhC,SAASwB,IACZ,OAAOV,KAAK2I,SAASE,SAAS,IAAIC,OAAO,EAAG,GADhD,mC,0ECG8BC,E,4HCATC,E,WACjB,WACoB5G,EACAQ,EACAqG,EACAhF,GACjB,yBAJiB7B,QAIlB,KAHkBQ,OAGlB,KAFkBqG,YAElB,KADkBhF,U,uDAKhB,OAAOpD,KAAKC,UAAU7B,U,KAIjBiK,EAAb,WACI,WACoB9J,EACA2D,EACAtD,GACjB,yBAHiBL,KAGlB,KAFkB2D,OAElB,KADkBtD,QAJxB,uDASQ,OAAOoB,KAAKC,UAAU7B,UAT9B,KAaakK,EAAb,WACI,WACoB1J,EACAT,GACjB,yBAFiBS,QAElB,KADkBT,YAHxB,uDAQQ,OAAO6B,KAAKC,UAAU7B,UAR9B,KAYamK,EAAb,WAYI,aAAe,yBAXCnC,WAWF,OAVEE,YAUF,OATEpH,eASF,OARE2B,kBAQF,OAPE2H,qBAOF,OANEhF,wBAMF,OALEzC,oBAKF,OAJE0H,sBAIF,OAHEC,gBAGF,OAFEC,mBAEF,EACVvK,KAAKgI,MAAQ,IACbhI,KAAKkI,OAAS,IAEdlI,KAAKc,UAAY,EACjBd,KAAKyC,aAAe,EAEpBzC,KAAKoK,gBAAkB,EACvBpK,KAAKoF,mBAAqB,EAE1BpF,KAAK2C,eAAiB,GAEtB3C,KAAKqK,iBAAmB,EAAIrK,KAAKc,UAEjCd,KAAKsK,WAAa,GAClBtK,KAAKuK,cAAgB,GA3B7B,oDA+BQ,OAAOC,OAAOC,OAAOD,OAAOE,OAAOF,OAAOG,eAAe3K,OAAQA,UA/BzE,K,cCrCqB4K,E,WACjB,WACWC,EACArK,EACAT,EACUG,GAClB,yBAJQ2K,QAIT,KAHSrK,QAGT,KAFST,YAET,KADmBG,Q,mDAIdK,GACHA,EAAQE,UAAY,UACpBF,EAAQG,YACRH,EAAQI,IAAIX,KAAKQ,MAAMI,EAAGZ,KAAKQ,MAAMK,EAAGb,KAAKE,MAAMuC,aAAc,EAAG,EAAI1B,KAAKC,IAC7ET,EAAQU,OAERV,EAAQE,UAAY,W,KCpBfqK,EAKT,WAAYrE,EAAgB7F,EAAWC,GAAY,yBAJ5C4F,YAI2C,OAH3C7F,OAG2C,OAF3CC,OAE2C,EAC9Cb,KAAKyG,OAASA,EACdzG,KAAKY,EAAIA,EACTZ,KAAKa,EAAIA,GAkBV,SAASkK,EAAeC,EAAiBC,GAE5C,OADiBlK,KAAKgB,MAAMkJ,EAAQrK,EAAIoK,EAAQpK,EAAGqK,EAAQpK,EAAImK,EAAQnK,GACrDmK,EAAQvE,OAASwE,EAAQxE,O,kBCxB1ByE,E,qKACVC,EAA2BnG,M,gCAGxBoG,M,qCAGKC,GACXA,EAAYzH,SAAQ,SAAA0H,GAChBC,QAAQC,IAAR,eAAoBF,EAAGnL,GAAvB,mBAAoCmL,EAAGnK,IAAI0C,cAA3C,oBAAoEyH,EAAG9J,a,+BAItEiK,GACLA,Q,SAdyC3B,GCKjD,SAASP,EAAamC,EAAalC,GAG/B,OAFAkC,EAAM3K,KAAK2E,KAAKgG,GAChBlC,EAAMzI,KAAK0I,MAAMD,GACVzI,KAAK0I,MAAM1I,KAAK2I,UAAYF,EAAMkC,EAAM,IAAMA,E,IAGpCC,E,WAajB,aAAe,yBAZCzL,WAYF,OAXNiL,iBAWM,OAVNS,cAUM,OATG5G,aASH,OAPN6G,cAOM,OALNT,SAKM,OAJGU,cAIH,OAFNC,cAEM,EACV/L,KAAKE,MAAQ,IAAIiK,EACjBnK,KAAK+L,SAAW,IAAIb,EAEpBlL,KAAKmL,YAAc,GACnBnL,KAAK4L,SAAW,GAChB5L,KAAKgF,QAAU,GAEfhF,KAAK6L,UAAW,EAEhB7L,KAAKoL,IAAM,EACXpL,KAAK8L,SAAW,G,wDAGDC,GACf/L,KAAK+L,SAAWA,I,8BAGE,IAAD,OAEjBC,YAAW,kBAAM,EAAKC,aAAY,M,0CAGVzL,GACxB,OAAOR,KAAKmL,YAAYtD,QACpB,SAAC6D,EAAKQ,GAAN,OAAmBnL,KAAK2K,IAAIA,EAAKlL,EAAMuD,SAASmI,EAAS1L,UACzDiD,OAAOC,a,mCAIKvC,GAChB,IAEIX,EAFA2L,EAAW,EACXC,EAAc,EAElB,EAAG,CAEC,KADAD,EACe,IACX,MAAM,IAAIE,MAAM,2CAEpB7L,EAAQ,IAAIsB,IACRyH,EAAavJ,KAAKE,MAAMY,UAAWd,KAAKE,MAAM8H,MAAQhI,KAAKE,MAAMY,WACjEyI,EAAavJ,KAAKE,MAAMY,UAAWd,KAAKE,MAAMgI,OAASlI,KAAKE,MAAMY,YACtEsL,EAAcpM,KAAKsM,oBAAoB9L,SAClC4L,EAAcpM,KAAKE,MAAMmK,kBAClC,IAAMkC,EAAa,IAAIrL,IAAWC,EAAKX,GACvCR,KAAKmL,YAAYxD,KAAK4E,K,iCAGA,IAAD,OACjBvM,KAAK6L,SACL7L,KAAK+L,SAASS,eAAexM,KAAKyM,mBAElCzM,KAAK0M,oBACL1M,KAAK2M,oBACL3M,KAAK4M,wBAEL5M,KAAK+L,SAASpK,OAAO3B,KAAKmL,YAAanL,KAAKgF,SAC5ChF,KAAK+L,SAASc,UAAU7M,KAAKoL,KAE7BpL,KAAK8M,mBACL9M,KAAK+M,gBAEL/M,KAAK+L,SAASE,UAAS,WAEnB,IADA,IAAMe,EAAMC,YAAYD,MACjB,EAAKlB,SAASxD,OAAS,GAAK,EAAKwD,SAAS,IAAMkB,EAAM,KACzD,EAAKlB,SAASoB,QAElB,EAAKpB,SAASnE,KAAKqF,GACnB,EAAK5B,IAAM,EAAKU,SAASxD,OAEzB,EAAK2D,iB,sCAMWjM,KAAKmL,YAAYgC,OACrC,SAAC7B,EAAI8B,EAAGC,GAAR,OAAqB/B,EAAGnK,IAAI0C,gBAAkBwJ,EAAS,GAAGlM,IAAI0C,mBAI9D7D,KAAK6L,UAAW,K,0CAIK,IAAD,OACxB7L,KAAKmL,YAAYvH,SAAQ,SAAC2I,EAAYtD,EAAOkC,GACzC,IAAMzI,EAAQ,EAAK4K,cAAcf,GACjC,IACIA,EAAWjL,cAAgBiL,EAAWpL,IAAIoM,SAAS7K,GACrD,MAAO8K,GACLjC,QAAQkC,MAAR,cAAqBlB,EAArB,uCAAsEiB,GAEtEjB,EAAWlL,MAAO,EAClB,EAAKuK,SAASjE,KAAK4E,GACnBpB,EAAYuC,OAAOzE,EAAO,S,oCAKhBsD,GAClB,IAAMpJ,EAAQ,IAAI8G,EACd0B,EAAKgC,YAAYpB,EAAWpM,IAC5BwL,EAAKgC,YAAYpB,EAAWpL,IAAI0C,eAChC0I,EAAW/L,MAAMoN,SAEfjK,EAAO3D,KAAKmL,YACb0C,QAAO,SAAAvC,GAAE,OAAIA,EAAGnL,KAAOoM,EAAWpM,MAClC0I,KAAI,SAAAyC,GAAE,OAAI,IAAIrB,EACX0B,EAAKgC,YAAYrC,EAAGnL,IACpBwL,EAAKgC,YAAYrC,EAAGnK,IAAI0C,eACxByH,EAAG9K,MAAMoN,YAEX5D,EAAYhK,KAAKgF,QAClB6I,QAAO,SAAAC,GAAa,OAAIA,EAAcjD,MAAM1K,KAAOoM,EAAWpM,MAC9D0I,KAAI,SAAA5D,GAAM,OAAI,IAAIiF,EAAYjF,EAAOzE,MAAOyE,EAAOlF,cAElDiF,EAAUhF,KAAKgF,QAChB6I,QAAO,SAAA5I,GAAM,OAAIA,EAAO4F,MAAM1K,KAAOoM,EAAWpM,MAChD0I,KAAI,SAAA5D,GAAM,OAAI,IAAIiF,EAAYjF,EAAOzE,MAAOyE,EAAOlF,cAExD,OAAO,IAAIgK,EAAM5G,EAAOQ,EAAMqG,EAAWhF,K,0CAOhB,IAAD,OACxBhF,KAAKmL,YAAYvH,SAAQ,SAAA2I,GACjBA,EAAWhL,UAAY,EAAKrB,MAAMyC,eAAiB,GACnD4J,EAAWhL,YAEf,IAAMzB,EAASyM,EAAWjL,cAAcxB,OAClCC,EAAYwM,EAAWjL,cAAcvB,UAE3C,GAAID,IAAWH,IAAO+B,MAAQ3B,IAAcH,IAAU8B,KAItD,GAAI5B,IAAWH,IAAOmD,KAAM,CACxB,IAAMtC,EAAQ,EAAKuN,gBAAgBhO,EAAWwM,EAAW/L,MAAO,EAAKN,MAAMkK,iBACvE5J,EAAMI,EAAI,EAAKV,MAAMY,YACrBN,EAAMI,EAAI,EAAKV,MAAMY,WAErBN,EAAMK,EAAI,EAAKX,MAAMY,YACrBN,EAAMK,EAAI,EAAKX,MAAMY,WAErBN,EAAMI,GAAK,EAAKV,MAAM8H,MAAQ,EAAK9H,MAAMY,YACzCN,EAAMI,EAAI,EAAKV,MAAM8H,MAAQ,EAAK9H,MAAMY,WAExCN,EAAMK,GAAK,EAAKX,MAAMgI,OAAS,EAAKhI,MAAMY,YAC1CN,EAAMK,EAAI,EAAKX,MAAMgI,OAAS,EAAKhI,MAAMY,WAE7CyL,EAAW/L,MAAQA,OAChB,GAAIV,IAAWH,IAAOiD,OAAS2J,EAAWhL,UAAY,EAAKrB,MAAMyC,iBAAmB,EAAG,CAC1F4J,EAAWhL,UAAY,EACvB,IAAM0D,EAAS,IAAI2F,EACf2B,EAEA,EAAKwB,gBAAgBhO,EAAWwM,EAAW/L,MAAO,EAAKN,MAAMY,WAC7Df,EACA,EAAKG,MAAM0N,SAEf,EAAK5I,QAAQ2C,KAAK1C,S,8CAKG,IAAD,OAC5BjF,KAAKgF,QAAQpB,SAAQ,SAACqB,EAAQgE,EAAOjE,GACjCC,EAAOzE,MAAQ,EAAKuN,gBAAgB9I,EAAOlF,UAAWkF,EAAOzE,MAAO,EAAKN,MAAMkF,qBAC3EH,EAAOzE,MAAMI,IAAM,EAAKV,MAAMuC,cAC3BwC,EAAOzE,MAAMK,IAAM,EAAKX,MAAMuC,cAC9BwC,EAAOzE,MAAMI,GAAK,EAAKV,MAAM8H,MAAQ,EAAK9H,MAAMuC,cAChDwC,EAAOzE,MAAMK,GAAK,EAAKX,MAAMgI,OAAS,EAAKhI,MAAMuC,eACpDuC,EAAQ0I,OAAOzE,EAAO,Q,sCAKVlJ,EAAsBS,EAAcwN,GACxD,IAAIC,EAAezN,EAAMoN,QAwBzB,OAtBI7N,IAAcH,IAAU+E,GACxBsJ,EAAapN,GAAKmN,EACXjO,IAAcH,IAAUkF,MAC/BmJ,EAAarN,GAAKoN,EACXjO,IAAcH,IAAUgF,KAC/BqJ,EAAapN,GAAKmN,EACXjO,IAAcH,IAAUiF,KAC/BoJ,EAAarN,GAAKoN,EACXjO,IAAcH,IAAU4E,SAC/ByJ,EAAapN,GAAKmN,EAClBC,EAAarN,GAAKoN,GACXjO,IAAcH,IAAU8E,WAC/BuJ,EAAapN,GAAKmN,EAClBC,EAAarN,GAAKoN,GACXjO,IAAcH,IAAU6E,UAC/BwJ,EAAapN,GAAKmN,EAClBC,EAAarN,GAAKoN,GACXjO,IAAcH,IAAU2E,SAC/B0J,EAAapN,GAAKmN,EAClBC,EAAarN,GAAKoN,GAGfC,I,yCAGiB,IAAD,OACvBjO,KAAKmL,YAAcnL,KAAKmL,YAAY0C,QAChC,SAAAK,GAAG,OAAK,EAAK/C,YAAYgD,MAAK,SAAAC,GAC1B,IAAMC,EAAU,EAAKC,YAAYJ,EAAKE,GAYtC,OAVIC,IACA,EAAKzC,SAASjE,KAAKuG,EAAKE,GACxBF,EAAI7M,MAAO,EACX+M,EAAI/M,MAAO,EAEX,EAAKuK,SAAW2C,MAAMC,KAAK,IAAInO,IAAI,EAAKuL,WACxC,EAAK6C,kBAAkBP,EAAI/N,GAAIiO,EAAIjO,IACnC,EAAKsO,kBAAkBL,EAAIjO,GAAI+N,EAAI/N,KAGhCkO,QAIfrO,KAAKgF,QAAQpB,SAAQ,SAACqB,EAAQyJ,EAAa1J,GACvC,EAAKmG,YAAYvH,SAAQ,SAAC2I,EAAYoC,EAAUxD,GACxCoB,EAAWpM,KAAO8E,EAAO4F,MAAM1K,IAC5B,EAAKyO,oBAAoB3J,EAAOzE,MAAO+L,EAAW/L,SAErD+L,EAAWlL,MAAO,EAClB,EAAKuK,SAASjE,KAAK4E,GACnB,EAAKsC,cAAc5J,EAAO4F,MAAM1K,GAAIoM,EAAWpM,IAE/C6E,EAAQ0I,OAAOgB,EAAa,GAC5BvD,EAAYuC,OAAOiB,EAAU,Y,wCAMnBG,EAAkBC,GACxC,IAAM5N,EAAMnB,KAAKmL,YACZ6D,MAAK,SAAAzC,GAAU,OAAIA,EAAWpM,KAAO2O,KACtC3N,IACAA,EAAIC,MAAM2N,IAAY,K,oCAIRD,EAAkBC,GACpC,IAAI5N,EAAMnB,KAAKmL,YACV6D,MAAK,SAAA1D,GAAE,OAAIA,EAAGnL,KAAO2O,KACrB3N,IACDA,EAAMnB,KAAK4L,SAASoD,MAAK,SAAA1D,GAAE,OAAIA,EAAGnL,KAAO2O,MAEzC3N,IACAA,EAAIC,MAAM2N,IAAY,K,0CAIFE,EAAoBC,GAG5C,OAAOnE,EAFW,IAAID,EAAO9K,KAAKE,MAAMY,UAAWoO,EAAStO,EAAGsO,EAASrO,GACnD,IAAIiK,EAAO9K,KAAKE,MAAMuC,aAAcwM,EAAYrO,EAAGqO,EAAYpO,M,kCAIpEsO,EAAkBC,GAClC,IAAMC,EAAa,IAAIvE,EAAO9K,KAAKE,MAAMY,UAAWqO,EAAK3O,MAAMI,EAAGuO,EAAK3O,MAAMK,GACvEyO,EAAa,IAAIxE,EAAO9K,KAAKE,MAAMY,UAAWsO,EAAK5O,MAAMI,EAAGwO,EAAK5O,MAAMK,GAC7E,OAAOsO,IAASC,GACTrE,EAAesE,EAAYC,K,uCAGE,IAAD,OACnC,OAAOtP,KAAKmL,YAAYoE,OAAOvP,KAAK4L,UAC/B/C,KAAI,SAAAyC,GAGD,OAFAA,EAAG9J,MAAQgJ,OAAOgF,KAAKlE,EAAGlK,OAAOkH,OAAS,EAAKpI,MAAMoK,YAC7CgB,EAAGjK,KAAkC,EAA3B,EAAKnB,MAAMqK,eACtBe,KACRmE,MAAK,SAACvB,EAAKE,GACd,OAAOA,EAAI5M,MAAQ0M,EAAI1M,Y,mCA/JJkO,GACvB,OAAQ,IAAMA,GAAKC,MAAM,O,sHCnJZC,E,kDAIjB,WAAoBC,EAAqC3P,GAAoB,IAAD,uBACxE,gBADgB2P,cAAwD,EAAnB3P,QAAmB,EAH3DK,aAG2D,IAF3DuP,YAE2D,EAGxE,IAAMA,EAASC,SAASC,cAAc,UAHkC,OAIxEF,EAAO9H,MAAQ9H,EAAM8H,MACrB8H,EAAO5H,OAAShI,EAAMgI,OACtB2H,EAAYI,YAAYH,GAExB,EAAKvP,QAAUuP,EAAOI,WAAW,MACjC,EAAKJ,OAASA,EAT0D,E,0DAaxE9P,KAAKO,QAAQ4P,SAAS,EAAG,EAAGnQ,KAAK8P,OAAO9H,MAAOhI,KAAK8P,OAAO5H,U,6BAGxDiD,EAA2BnG,GAAoB,IAAD,OACjDhF,KAAKoQ,cACLjF,EAAYvH,SAAQ,SAAAzC,GAAG,OAAIA,EAAIQ,OAAO,EAAKpB,YAC3CyE,EAAQpB,SAAQ,SAAAqB,GAAM,OAAIA,EAAOtD,OAAO,EAAKpB,c,gCAGvC6K,GACNpL,KAAKO,QAAQE,UAAY,UACzBT,KAAKO,QAAQ8P,KAAO,oBACpBrQ,KAAKO,QAAQ+P,SAASvP,KAAKwP,MAAMnF,GAAO,OAAQ,EAAG,IAEnDpL,KAAKO,QAAQE,UAAY,Y,qCAGd4K,GAA4B,IAAD,OACtCrL,KAAKoQ,cACLpQ,KAAKO,QAAQE,UAAY,UACzBT,KAAKO,QAAQ8P,KAAO,oBACpBrQ,KAAKO,QAAQiQ,aAAe,SAE5B,IAAI5P,EAAI,GACJC,EAAI,GAERb,KAAKO,QAAQ+P,SAAS,aAAc1P,EAAGC,GAEvCA,GAAK,GAELb,KAAKO,QAAQE,UAAY,UACzBT,KAAKO,QAAQ+P,SAAS,MAAO1P,EAAGC,GAChCb,KAAKO,QAAQ+P,SAAS,QAAS1P,IAASC,GACxCb,KAAKO,QAAQ+P,SAAS,QAAS1P,IAASC,GACxCb,KAAKO,QAAQ+P,SAAS,UAAW1P,IAASC,GAE1CA,GAAK,GAELwK,EAAYzH,SAAQ,SAAA0H,GAEhB,IAAImF,EAAO7P,EAAI,EAAKV,MAAMY,UAC1BwK,EAAGnK,IAAIQ,OAAO,EAAKpB,QAAS,IAAIuB,IAAM2O,EAAM5P,IAE5C4P,GAAQ,EAAKvQ,MAAMY,UAAY,EAC/B,EAAKP,QAAQE,UAAY,UACzB,IAAMiQ,EAAWpF,EAAGnL,GACpB,EAAKI,QAAQ+P,SAASI,EAAUD,EAAM5P,GAEtC4P,EAAO7P,IACP,EAAKL,QAAQE,UAAY,UACzB,EAAKF,QAAQ+P,SAASK,OAAOrF,EAAG9J,OAAQiP,EAAM5P,GAE9C4P,EAAO7P,IACP,EAAKL,QAAQE,UAAY,UACzB,EAAKF,QAAQ+P,SAASK,OAAOnG,OAAOgF,KAAKlE,EAAGlK,OAAOkH,QAASmI,EAAM5P,GAElE4P,EAAO7P,IACP,EAAKL,QAAQE,UAAY,UACzB,EAAKF,QAAQ+P,SAAShF,EAAGjK,KAAO,IAAM,IAAKoP,EAAM5P,GAEjDA,GAAK,Q,+BAIJ4K,GACLmF,sBAAsBnF,O,GAlFe3B,M,u9CCO7C,SAAS+G,QAAQC,GACbA,EAAKC,aAAa,IAAIC,+DAASF,EAAK5Q,QACpC4Q,EAAKC,aAAa,IAAI9O,8DAAS6O,EAAK5Q,QACpC4Q,EAAKC,aAAa,IAAI9O,8DAAS6O,EAAK5Q,QACpC4Q,EAAKC,aAAa,IAAI9O,8DAAS6O,EAAK5Q,QACpC4Q,EAAKC,aAAa,IAAI9O,8DAAS6O,EAAK5Q,QACpC4Q,EAAKC,aAAa,IAAI9O,8DAAS6O,EAAK5Q,QACpC4Q,EAAKC,aAAa,IAAIpH,+DAAUmH,EAAK5Q,QACrC4Q,EAAKC,aAAa,IAAIpH,+DAAUmH,EAAK5Q,QACrC4Q,EAAKC,aAAa,IAAIpH,+DAAUmH,EAAK5Q,QACrC4Q,EAAKC,aAAa,IAAIlK,uEAAkBiK,EAAK5Q,QAC7C4Q,EAAKC,aAAa,IAAIlK,uEAAkBiK,EAAK5Q,QAC7C4Q,EAAKC,aAAa,IAAIlK,uEAAkBiK,EAAK5Q,QAC7C4Q,EAAKC,aAAa,IAAIlK,uEAAkBiK,EAAK5Q,QAC7C4Q,EAAKC,aAAa,IAAIlK,uEAAkBiK,EAAK5Q,QAGjD,SAAS+Q,MACL,IAAMC,YAAcC,6CAAMC,OAAuB,MAC3CC,WAAaF,6CAAMC,OAAoB,MACvCE,QAAUH,6CAAMC,OAAa,IAAIzF,sDAWvC,SAAS4F,gBACDL,YAAYnJ,UACZuJ,QAAQvJ,QAAU,IAAI4D,qDACtB2F,QAAQvJ,QAAQyJ,YAAY,IAAI5B,8EAAgBsB,YAAYnJ,QAASuJ,QAAQvJ,QAAQ7H,QACrF2Q,QAAQS,QAAQvJ,UAIxB,SAAS0J,eAAgB,IAAD,oBAEdC,MAAQC,KAAK,IAAD,mCAAKN,WAAWtJ,eAAhB,+BAAK,oBAAoB6J,UAAzB,MACZzQ,IAAM,IAAIuQ,MAAMjQ,6DAAc,SAAU6P,QAAQvJ,QAAQ7H,MAAM0N,SACpE0D,QAAQvJ,QAAQgJ,aAAa5P,KAGjC,SAAS0Q,kBACL,IAAK,IAAIzE,EAAI,EAAGA,EAAI,GAAIA,IACpBkE,QAAQvJ,QAAQgJ,aAAa,IAAIpH,+DAAU2H,QAAQvJ,QAAQ7H,MAAM0N,UAErE,IAAK,IAAIR,EAAI,EAAGA,EAAI,EAAGA,IACnBkE,QAAQvJ,QAAQgJ,aAAa,IAAI9O,8DAASqP,QAAQvJ,QAAQ7H,MAAM0N,UAEpE0D,QAAQvJ,QAAQ+J,QAGpB,OAlCAX,6CAAMY,WAAU,WACRb,YAAYnJ,UACZuJ,QAAQvJ,QAAU,IAAI4D,qDACtB2F,QAAQvJ,QAAQyJ,YAAY,IAAI5B,8EAAgBsB,YAAYnJ,QAASuJ,QAAQvJ,QAAQ7H,QACrF2Q,QAAQS,QAAQvJ,SAChBuJ,QAAQvJ,QAAQ+J,WAErB,IA4BC,mEAAKE,UAAU,MAAf,UACI,2FACA,kEAAKC,IAAKf,cAEV,wFACA,mLACA,uMAKA,2FAEA,qEAAQgB,KAAK,SAASC,QAASZ,cAA/B,sBACA,qEAAQW,KAAK,SAASC,QAASV,aAA/B,qBACA,qEAAQS,KAAK,SAASC,QAASN,gBAA/B,wBAEA,4EACI,yQAIA,mHACA,gLAEA,oIAGJ,kEAAKG,UAAWI,mEAAOC,OAAvB,SACX,mEAAMJ,IAAKZ,WAAYW,UAAU,+BAA+BM,iBAAe,EAACC,gCAAgC,EAAhH,qrFAuFctB,2B,gBCzLfuB,EAAOC,QAAU,CAAC,KAAO,sBAAsB,OAAS,0B,8GCSlDC,EAAW,CAAC,UAAW,YAAa,YAAa,aAAc,OAAQ,OAAQ,OAAQ,QAExE1B,E,kDAIjB,WAAsB9Q,GAAoB,IAAD,8BACrC,cAAMA,IADYA,QAAmB,EAHjCqB,eAGiC,IAFxBiO,UAEwB,EAErC,EAAKjO,UAAY,EACjB,EAAKiO,KAAO,GAEZO,SAAS4C,iBAAiB,WAAW,SAACC,GAAD,OAAW,EAAKC,WAAWD,MAChE7C,SAAS4C,iBAAiB,SAAS,SAACC,GAAD,OAAW,EAAKE,WAAWF,MANzB,E,uDAStBA,GACXF,EAASK,QAAQH,EAAMI,OAAS,GAChCJ,EAAMK,iBAEVjT,KAAKwP,KAAKoD,EAAMI,OAAQ,I,iCAGTJ,GACXF,EAASK,QAAQH,EAAMI,OAAS,GAChCJ,EAAMK,iBAEVjT,KAAKwP,KAAKoD,EAAMI,OAAQ,I,8BAIxB,OAAOhT,KAAKG,K,iCAIZH,KAAKuB,YACL,IAAIzB,EAASH,IAAO+B,KAChBwR,EAAiBtT,IAAU8B,KAsB/B,GApBI1B,KAAKwP,KAAL,MACA0D,EAAiBtT,IAAU+E,GACvB3E,KAAKwP,KAAL,KACA0D,EAAiBtT,IAAU4E,QACpBxE,KAAKwP,KAAL,OACP0D,EAAiBtT,IAAU2E,SAExBvE,KAAKwP,KAAL,MACP0D,EAAiBtT,IAAUgF,KACvB5E,KAAKwP,KAAL,KACA0D,EAAiBtT,IAAU8E,UACpB1E,KAAKwP,KAAL,OACP0D,EAAiBtT,IAAU6E,WAExBzE,KAAKwP,KAAL,KACP0D,EAAiBtT,IAAUkF,MACpB9E,KAAKwP,KAAL,OACP0D,EAAiBtT,IAAUiF,WAGR+D,IAAnBsK,GACIlT,KAAKuB,UAAYvB,KAAKE,MAAMyC,iBAAmB,EAE/C,OADA3C,KAAKuB,UAAY,EACV,IAAI1B,IAAUF,IAAOiD,MAAOsQ,GAI3C,IAAIC,EAAgBvT,IAAU8B,KA0B9B,OAxBI1B,KAAKwP,KAAL,SACA2D,EAAgBvT,IAAU+E,GACtB3E,KAAKwP,KAAL,WACA2D,EAAgBvT,IAAU4E,QACnBxE,KAAKwP,KAAL,YACP2D,EAAgBvT,IAAU2E,SAEvBvE,KAAKwP,KAAL,WACP2D,EAAgBvT,IAAUgF,KACtB5E,KAAKwP,KAAL,WACA2D,EAAgBvT,IAAU8E,UACnB1E,KAAKwP,KAAL,YACP2D,EAAgBvT,IAAU6E,WAEvBzE,KAAKwP,KAAL,WACP2D,EAAgBvT,IAAUkF,MACnB9E,KAAKwP,KAAL,YACP2D,EAAgBvT,IAAUiF,MAG1B/E,IAAWH,IAAO+B,WAA0BkH,IAAlBuK,IAC1BrT,EAASH,IAAOmD,MAGb,IAAIjD,IAAUC,EAAQqT,K,6BAG1B5S,EAAmCC,GACtCD,EAAQE,UAAY,UACpBF,EAAQG,YACRH,EAAQI,IAAIH,EAAMI,EAAGJ,EAAMK,EAAGb,KAAKE,MAAMY,UAAW,EAAG,EAAIC,KAAKC,IAChET,EAAQU,OACRV,EAAQE,UAAY,Y,oCAIpB,MAAO,a,iCAIP,OAAOmB,KAAKC,UAAU,CAClB1B,GAAIH,KAAKG,IACV,KAAM,O,GA3GqBF,M,sECZtC,uCAOAmT,IAAMC,QAAQC,oBAAoBC,YAAY,CAC1C,mBAAmB,EACnB,iBAAiB,EACjB,aAAa,EACb,cAAc,K,qHCCEC,QACW,cAA7BC,OAAOC,SAASC,UAEe,UAA7BF,OAAOC,SAASC,UAEhBF,OAAOC,SAASC,SAASC,MACvB,2DCZNC,IAASlS,OACP,cAAC,IAAMmS,WAAP,UACE,cAAC7C,EAAA,EAAD,MAEFlB,SAASgE,eAAe,SDiIpB,kBAAmBC,WACrBA,UAAUC,cAAcC,MACrBC,MAAK,SAAAC,GACJA,EAAaC,gBAEdC,OAAM,SAAA7G,GACLlC,QAAQkC,MAAMA,EAAM8G,c","file":"static/js/main.f3b9905d.chunk.js","sourcesContent":["export enum Action {\n None = 'None',\n Move = 'Move',\n Shoot = 'Shoot'\n}\n\nexport enum Direction {\n None = 'None',\n Up = 'Up',\n Right = 'Right',\n Down = 'Down',\n Left = 'Left',\n UpRight = 'UpRight',\n DownRight = 'DownRight',\n DownLeft = 'DownLeft',\n UpLeft = 'UpLeft'\n}\n\nexport default class BotAction {\n constructor(public readonly action: Action, public readonly direction: Direction) {\n this.action = action;\n this.direction = direction;\n }\n}\n","import Point from './Point';\nimport BotAction, {Action, Direction} from './BotAction';\nimport State, {WorldState} from './State';\nimport {generateId} from './ID';\n\nexport abstract class Bot {\n protected id: string;\n protected teammates: Set;\n\n constructor(protected world: WorldState) {\n this.id = '';\n this.teammates = new Set();\n }\n\n public setId(id: string): void {\n this.id = id;\n }\n\n public getId(): string {\n return this.id;\n }\n\n public registerTeammate(id: string): void {\n this.teammates.add(id);\n }\n\n public abstract takeTurn(state: State): BotAction;\n\n /**\n *\n * @param context\n * @param point\n */\n public render(context: CanvasRenderingContext2D, point: Point): void {\n context.fillStyle = '#FF0000';\n context.beginPath();\n context.arc(point.x, point.y, this.world.botRadius, 0, 2 * Math.PI);\n context.fill();\n context.fillStyle = '#000000';\n }\n\n public abstract getTeamName(): string;\n}\n\nclass BotManager {\n public id: string;\n public bot: Bot;\n public point: Point;\n public kills: { [key: string]: boolean };\n public dead: boolean;\n public currentAction: BotAction;\n public shotClock: number;\n public score: number;\n\n constructor(bot: Bot, point: Point) {\n this.id = generateId();\n this.currentAction = new BotAction(Action.None, Direction.None)\n this.bot = bot;\n this.point = point;\n this.kills = {};\n this.dead = false;\n this.shotClock = 0;\n this.score = 0;\n }\n\n render(context: CanvasRenderingContext2D) {\n this.bot.render(context, this.point);\n }\n\n toString() {\n return JSON.stringify({\n bot: this.bot,\n point: this.point,\n kills: this.kills,\n dead: this.dead\n }, null, 4);\n }\n}\n\nexport default BotManager;\n","export default class Point {\n public x: number;\n public y: number\n\n constructor(x: number, y: number) {\n this.x = x;\n this.y = y;\n }\n\n distance(point: Point) {\n return Math.hypot(point.x - this.x, point.y - this.y);\n }\n\n clone() {\n return new Point(this.x, this.y);\n }\n\n toString() {\n return `(${this.x}, ${this.y})`;\n }\n}\n","import BotAction, {Action, Direction} from '../BotAction';\nimport State, {BotState, BulletState, WorldState} from '../State';\nimport Point from '../Point';\nimport {Bot} from '../BotManager';\n\nconst eighthPi = Math.PI / 8;\n\nconst mushroomPng = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH4wUSAiMdsN8cPgAAAEJ0RVh0Q29tbWVudABDUkVBVE9SOiBnZC1qcGVnIHYxLjAgKHVzaW5nIElKRyBKUEVHIHY2MiksIHF1YWxpdHkgPSAxMDAKu3x3owAAAMNJREFUOMulUzEOgzAM9IWO5QOVGPuA/I29YudveQBjJT4AK3KnK4lrQqveFMydc7HOEAeqql4dAGwtWCHFqW1kHTtZx05S23z8Jy72VpItWI/LJqqqdBNy8Tp2b8H9cXPP5FADfqS2KYg1TMMscdkEAGCts8k0zIXI1uOy7UO077Zir0ZNkD8RPJs1WA5UVfPpH1n2ml/7554DFojYH7vIL4RNFgOSh8Weixnk+fayXtuHgn+0QGfLFr7ZwhoHvzaw1l+S7oHlEqbYjAAAAABJRU5ErkJggg==';\n\nexport class Mushroom extends Bot {\n private frameCounter: number;\n private win: boolean;\n private readonly image: HTMLImageElement;\n private readonly botBulletThresh: number;\n private readonly botBotThresh: number;\n\n constructor(protected world: WorldState) {\n super(world);\n this.frameCounter = 0;\n\n this.win = false;\n\n this.image = new Image();\n this.image.src = mushroomPng;\n\n this.botBulletThresh = (world.botRadius + world.bulletRadius) + 20;\n this.botBotThresh = 2 * world.botRadius + 10;\n }\n\n getId(): string {\n return this.id;\n }\n\n takeTurn(state: State): BotAction {\n this.frameCounter++;\n let action: Action;\n let direction: Direction;\n\n if (this.frameCounter % this.world.shootFrequency === 0) {\n action = Action.Shoot;\n direction = this.getShootDirection(state);\n } else {\n action = Action.Move;\n direction = this.getDodgeDirection(state);\n\n if (direction === Direction.None) {\n const friendlyBot = this.getClosestFriendlyBot(state);\n if (friendlyBot !== null) {\n direction = this.getAvoidBotDirection(state.myBot.point, friendlyBot.point);\n }\n }\n\n if (direction === Direction.None) {\n const targetBot = this.getClosestEnemyBot(state);\n if (targetBot === null) {\n this.win = true;\n } else {\n direction = this.getGoToBotDirection(state.myBot.point, targetBot.point);\n }\n }\n }\n\n return new BotAction(action, direction);\n }\n\n getClosestFriendlyBot(state: State): BotState | null {\n let closestBot = null;\n let closestDistance = Number.MAX_VALUE;\n state.bots.forEach(bot => {\n if (this.getTeamName() === bot.team\n && state.myBot.point.distance(bot.point) < 60\n && state.myBot.point.distance(bot.point) <= closestDistance) {\n closestBot = bot;\n closestDistance = state.myBot.point.distance(bot.point);\n }\n });\n return closestBot;\n }\n\n getClosestEnemyBot(state: State): BotState | null {\n let closestBot = null;\n let closestDistance = Number.MAX_VALUE;\n state.bots.forEach(bot => {\n if (this.getTeamName() !== bot.team\n && state.myBot.point.distance(bot.point) <= closestDistance) {\n closestBot = bot;\n closestDistance = state.myBot.point.distance(bot.point);\n }\n });\n return closestBot;\n }\n\n getAvoidBotDirection(me: Point, bot: Point): Direction {\n if (!bot) {\n return Direction.None;\n }\n let botDistance = this.botBotThresh;\n\n let down = me.y > Math.abs(bot.y - botDistance);\n let up = me.y < Math.abs(bot.y - botDistance);\n let right = me.x > Math.abs(bot.x - botDistance);\n let left = me.x < Math.abs(bot.x - botDistance);\n\n if (up && left) {\n return Direction.UpLeft;\n } else if (up && right) {\n return Direction.UpRight;\n } else if (down && left) {\n return Direction.DownLeft;\n } else if (down && right) {\n return Direction.DownRight;\n } else if (up) {\n return Direction.Up;\n } else if (down) {\n return Direction.Down;\n } else if (left) {\n return Direction.Left;\n } else if (right) {\n return Direction.Right;\n }\n\n return Direction.None;\n }\n\n getGoToBotDirection(me: Point, bot: Point): Direction {\n if (!bot) {\n return Direction.None;\n }\n let botDistance = 50;\n\n let up = me.y > Math.abs(bot.y - botDistance);\n let down = me.y < Math.abs(bot.y - botDistance);\n let left = me.x > Math.abs(bot.x - botDistance);\n let right = me.x < Math.abs(bot.x - botDistance);\n\n if (up && left) {\n return Direction.UpLeft;\n } else if (up && right) {\n return Direction.UpRight;\n } else if (down && left) {\n return Direction.DownLeft;\n } else if (down && right) {\n return Direction.DownRight;\n } else if (up) {\n return Direction.Up;\n } else if (down) {\n return Direction.Down;\n } else if (left) {\n return Direction.Left;\n } else if (right) {\n return Direction.Right;\n }\n\n return Direction.None;\n }\n\n getDodgeDirection(state: State): Direction {\n let closestBullet = null;\n let closestDistance = Number.MAX_VALUE;\n let bulletDistance = 50;\n\n state.bullets.forEach((bullet: BulletState) => {\n if (state.myBot.point.distance(bullet.point) < bulletDistance\n && state.myBot.point.distance(bullet.point) <= closestDistance\n && this.bulletWillHit(state.myBot.point, bullet)) {\n closestBullet = bullet;\n closestDistance = state.myBot.point.distance(bullet.point);\n }\n });\n\n if (closestBullet === null) {\n return Direction.None;\n } else {\n return this.getAvoidBulletDirection(state.myBot.point, closestBullet);\n }\n }\n\n getXPlus(bullet: BulletState): number {\n if (bullet.direction === Direction.Left) {\n return -this.world.bulletStepDistance;\n } else {\n return this.world.bulletStepDistance;\n }\n }\n\n getYPlus(bullet: BulletState): number {\n if (bullet.direction === Direction.Up) {\n return -this.world.bulletStepDistance;\n } else {\n return this.world.bulletStepDistance;\n }\n }\n\n bulletWillHit(me: Point, bullet: BulletState) {\n if ((this.getXPlus(bullet) > 0 && bullet.point.x > me.x)\n || (this.getXPlus(bullet) < 0 && bullet.point.x < me.x)\n || (this.getYPlus(bullet) < 0 && bullet.point.y < me.y)\n || (this.getYPlus(bullet) > 0 && bullet.point.y > me.y)) {\n\n if (me.distance(bullet.point) < this.botBulletThresh * this.world.bulletStepDistance * ((this.getXPlus(bullet) !== 0)\n && (this.getYPlus(bullet) !== 0) ? 2 : 1)) {\n return true;\n }\n }\n return false;\n }\n\n getAvoidBulletDirection(me: Point, bullet: BulletState): Direction {\n let direction = Direction.None;\n if (this.getXPlus(bullet) !== 0 && this.getYPlus(bullet) !== 0) {\n let c = ((me.x - bullet.point.x) * this.getXPlus(bullet) + (me.y - bullet.point.y) * this.getYPlus(bullet)) / (Math.pow(this.getXPlus(bullet), 2) + Math.pow(this.getYPlus(bullet), 2));\n let distance = Math.sqrt(Math.pow((me.x - bullet.point.x - c * this.getXPlus(bullet)), 2) + Math.pow((me.y - bullet.point.y - c * this.getYPlus(bullet)), 2));\n // bullet is going in this direction: \\\n if (this.getXPlus(bullet) * this.getYPlus(bullet) > 0 && Math.ceil(Math.abs(distance)) <= this.botBulletThresh) {\n if (me.y - me.x - bullet.point.y + bullet.point.x >= 0) {\n direction = Direction.DownLeft;\n } else {\n direction = Direction.UpRight;\n }\n } // bullet is going in this direction: /\n else if (this.getXPlus(bullet) * this.getYPlus(bullet) <= 0 && Math.ceil(Math.abs(distance)) <= this.botBulletThresh) {\n if (me.y + me.x - bullet.point.y - bullet.point.x > 0) {\n direction = Direction.DownRight;\n } else {\n direction = Direction.UpLeft;\n }\n }\n } else {\n if (this.shouldMoveVertical(me, bullet)) {\n if (me.y < bullet.point.y) {\n direction = Direction.Up;\n } else {\n direction = Direction.Down;\n }\n }\n if (this.shouldMoveHorizontal(me, bullet)) {\n if (me.x < bullet.point.x) {\n if (direction === Direction.Up) {\n direction = Direction.UpLeft;\n } else if (direction === Direction.Down) {\n direction = Direction.DownLeft;\n } else {\n direction = Direction.Left;\n }\n } else {\n if (direction === Direction.Up) {\n direction = Direction.UpRight;\n } else if (direction === Direction.Down) {\n direction = Direction.DownRight;\n } else {\n direction = Direction.Right;\n }\n }\n }\n }\n return direction;\n }\n\n shouldMoveVertical(me: Point, bullet: BulletState): boolean {\n let thresh = Math.abs(Math.abs(me.y - bullet.point.y) - this.botBulletThresh);\n return ((this.getXPlus(bullet) > 0 && bullet.point.x < me.x)\n || (this.getXPlus(bullet) < 0 && bullet.point.x > me.x))\n && (thresh <= this.botBulletThresh && thresh > 0);\n }\n\n shouldMoveHorizontal(me: Point, bullet: BulletState): boolean {\n let thresh = Math.abs(Math.abs(me.x - bullet.point.x) - this.botBulletThresh);\n return ((this.getYPlus(bullet) < 0 && bullet.point.y > me.y)\n || (this.getYPlus(bullet) > 0 && bullet.point.y < me.y))\n && (thresh <= this.botBulletThresh && thresh > 0);\n }\n\n getShootDirection(state: State): Direction {\n let closestBot: BotState | undefined;\n let closestDistance = Number.MAX_VALUE;\n state.bots.forEach(bot => {\n if (this.getTeamName() !== bot.team\n && state.myBot.point.distance(bot.point) < closestDistance) {\n closestBot = bot;\n closestDistance = state.myBot.point.distance(bot.point);\n }\n });\n\n if (closestBot) {\n return this.shootTargetDirection(state.myBot.point, closestBot.point);\n }\n return Direction.None;\n }\n\n shootTargetDirection(me: Point, target: Point): Direction {\n let directionX = Direction.None;\n let directionY = Direction.None;\n let direction = Direction.None;\n let deltaX = target.x - me.x;\n let deltaY = target.y - me.y;\n\n if (deltaX > 0) {\n directionX = Direction.Right;\n } else if (deltaX < 0) {\n directionX = Direction.Left;\n }\n\n if (deltaY > 0) {\n directionY = Direction.Down;\n } else if (deltaY < 0) {\n directionY = Direction.Up;\n }\n\n if (deltaX === 0) {\n direction = directionY;\n } else if (deltaY === 0) {\n direction = directionX;\n } else {\n let ratio1 = Math.abs(deltaY) / Math.abs(deltaX);\n let ratio2 = Math.abs(deltaX) / Math.abs(deltaY);\n if (ratio1 <= Math.tan(eighthPi)) {\n direction = directionX;\n } else if (ratio2 <= Math.tan(eighthPi)) {\n direction = directionY;\n } else {\n if (directionY === Direction.Up && directionX === Direction.Left) {\n direction = Direction.UpLeft;\n } else if (directionY === Direction.Up && directionX === Direction.Right) {\n direction = Direction.UpRight;\n } else if (directionY === Direction.Down && directionX === Direction.Left) {\n direction = Direction.DownLeft;\n } else if (directionY === Direction.Down && directionX === Direction.Right) {\n direction = Direction.DownRight;\n }\n }\n }\n return direction;\n }\n\n render(context: CanvasRenderingContext2D, point: Point): void {\n context.drawImage(this.image,\n point.x - this.world.botRadius,\n point.y - this.world.botRadius,\n this.world.botRadius * 2,\n this.world.botRadius * 2);\n\n // context.fillStyle = \"#ffff00\";\n // context.beginPath();\n // context.arc(point.x, point.y, this.world.botRadius, 0, 2 * Math.PI);\n // context.fill();\n // context.fillStyle = \"#000\";\n }\n\n getTeamName(): string {\n return 'Mushroom';\n }\n}","import BotAction, {Action, Direction} from '../BotAction';\nimport Point from '../Point';\nimport State, {BotState, WorldState} from '../State';\nimport {Bot} from '../BotManager';\n\n/*\n\nThis is a bot based on potential field behaviors. The computations used in this bot are taken from\nhttp://cs.boisestate.edu/~tim/potentialfields/PfieldsTutorial.pdf\n\n */\n\nconst imageStar = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QAAAAAAAD5Q7t/AAAACXBIWXMAAC4jAAAuIwF4pT92AAAAB3RJTUUH4wUWFzorcerWcwAAABl0RVh0Q29tbWVudABDcmVhdGVkIHdpdGggR0lNUFeBDhcAAACKSURBVDjLpVNBDsAgCCviB3bk/4/bkRcs7rC5GAV0sYmJidqWgkCM8i4XFD2+zgMAwKLu3YRN0Ey9wnORZzUamQwOBrVVsCgSAHrtWZaHfV9SDdEkiZRrHm0Xlkj6MN02trlEGVHUur8lbA2SqV4zCc4oBxY/ARYtHhEZnyYc8ZaIRR+Cxd9pjvQN2HI2aP9iF0cAAAAASUVORK5CYII=';\nconst imageRainbowStar = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QAAAAAAAD5Q7t/AAAACXBIWXMAAC4jAAAuIwF4pT92AAAAB3RJTUUH4wUXAAQbtyMvMgAAABl0RVh0Q29tbWVudABDcmVhdGVkIHdpdGggR0lNUFeBDhcAAAEDSURBVDjLpZMxTsQwEEWfzUKfSAiqvcAWKTgBXAFOgcQRtqLbNueIxA1WHIDVcoU06WjoQMmnSGyt7cQgMVKkKNabP/l/DPnS9CyWzcHfXNBv1+SaWP5ZNqdut9cA9F2xOMVq6cDBPH7GngRlnFoCnsAvV2v/6eH1OHa6PQA3WMCc85XAZ8+tf7+37+H/TTBgnAe+STx2rH4Kz5rXd4X6rhCgZqjUDJUAmb0Eb8lerJbiaYbKq5o96O7wa4ya4vJjB4ZNurtNG6SR7MEsjIMvFydQM1QzsHGGBbDKOtkJb9hoVnKJBGi3aaWylsranxunHuVtMrfTTYD5eBobRJv5l/LMD8dIfXZ8EpdLAAAAAElFTkSuQmCC';\n\nclass FieldObject {\n /**\n * @param point {Point} - The position of the object generating the field.\n * @param radius {Number} - The radius of the object generating the field. Can be a goal or obstacle.\n * @param spread {Number} - The radius around the field that applies varying strengths.\n * @param strength {Number} - The strength of the field. Stronger when closer for repulsive fields. Stronger when farther away for attractive\n * fields.\n * @param tangentialModifier {Number} - Should be + or - 90 degrees or Math.PI / 2.\n */\n constructor(\n public point: Point,\n public radius: number,\n public spread: number,\n public strength: number,\n public tangentialModifier: number = 1\n ) {\n }\n}\n\nexport default class PotentialFieldBot extends Bot {\n private shotClock: number;\n private history: Point[]\n private historyClock: number;\n private readonly historyInterval: number;\n private readonly maxHistory: number;\n private readonly image: HTMLImageElement;\n private readonly shootImage: HTMLImageElement;\n private shooting: boolean;\n\n constructor(protected world: WorldState) {\n super(world);\n this.shotClock = 0;\n\n this.history = [];\n this.historyClock = 0;\n this.historyInterval = 10;\n this.maxHistory = 100;\n\n this.image = new Image();\n this.image.src = imageStar;\n this.shootImage = new Image();\n this.shootImage.src = imageRainbowStar;\n this.shooting = false;\n }\n\n getId() {\n return this.id;\n }\n\n takeTurn(state: State) {\n this.shotClock++;\n this.shooting = false;\n\n let action = Action.None;\n let direction: Direction;\n\n const closestEnemy = this.getClosestEnemy(state);\n\n if (this.shotClock % this.world.shootFrequency === 0 && closestEnemy) {\n // TODO: Do we want to shoot whenever we can? What if we need to move out of the way of a bullet with this one spare turn?\n this.shotClock = 0;\n action = Action.Shoot;\n direction = this.getShootDirection(state, closestEnemy);\n this.shooting = true;\n\n return new BotAction(action, direction);\n }\n\n\n let fields = [\n // TODO: Experiment with field strength\n // avoid lethal objects\n ...this.getAvoidEnemyFields(state),\n ...this.getAvoidBulletFields(state),\n // avoid getting cornered\n this.getAvoidWallField(state),\n // avoid cyclic behavior\n ...this.getPastBehaviorFields(state),\n ]\n // move toward the closest enemy\n if (closestEnemy) {\n fields.push(this.getAttractiveField(state, new FieldObject(closestEnemy.point, this.world.botRadius, 1, 1)))\n }\n let [deltaX, deltaY] = fields.reduce(\n (delta: number[], current: number[]) => [delta[0] + current[0], delta[1] + current[1]]\n );\n\n if (\n state.myBot.point.x < this.world.botRadius * 8\n || state.myBot.point.x > this.world.width - this.world.botRadius * 8\n || state.myBot.point.y < this.world.botRadius * 8\n || state.myBot.point.y > this.world.width - this.world.botRadius * 8\n ) {\n // prefer the center\n const centerField = new FieldObject(new Point(this.world.width / 2, this.world.height / 2), 1, 10, 5, Math.PI / 2);\n [deltaX, deltaY] = this.addDelta([deltaX, deltaY], this.getAttractiveField(state, centerField));\n }\n\n direction = this.getDirection(deltaX, deltaY);\n\n if (direction !== null) {\n action = Action.Move;\n }\n\n this.updateHistory(state);\n\n return new BotAction(action, direction);\n }\n\n updateHistory(state: State) {\n if (this.history.length >= this.maxHistory) {\n this.history.pop();\n }\n\n this.historyClock++;\n if (this.historyClock % this.historyInterval === 0) {\n this.historyClock = 0;\n this.history.push(state.myBot.point);\n }\n }\n\n addDelta(current: number[], addition: number[]): number[] {\n return [current[0] + addition[0], current[1] + addition[1]];\n }\n\n getShootDirection(state: State, enemy: BotState): Direction {\n // TODO: Is there a way we can combine this with the getDirection function?\n const deltaX = enemy.point.x - state.myBot.point.x;\n const deltaY = state.myBot.point.y - enemy.point.y;\n const angle = Math.atan2(deltaY, deltaX) * 180 / Math.PI;\n\n if (angle >= -22 && angle < 23) {\n return Direction.Right;\n }\n if (angle >= 23 && angle < 68) {\n return Direction.UpRight;\n }\n if (angle >= 68 && angle < 113) {\n return Direction.Up;\n }\n if (angle >= 113 && angle < 158) {\n return Direction.UpLeft;\n }\n if (angle >= 158 || angle < -157) {\n return Direction.Left;\n }\n if (angle >= -157 && angle < -112) {\n return Direction.DownLeft;\n }\n if (angle >= -112 && angle < -67) {\n return Direction.Down;\n }\n if (angle >= -67 && angle < -22) {\n return Direction.DownRight;\n }\n\n return Direction.None;\n }\n\n getDirection(deltaX: number, deltaY: number): Direction {\n let direction = Direction.None;\n\n if (deltaY !== 0) {\n if (deltaY < 0) {\n direction = Direction.Up;\n } else {\n direction = Direction.Down;\n }\n }\n\n if (deltaX !== 0) {\n if (deltaX < 0) {\n if (direction === Direction.Up) {\n direction = Direction.UpLeft;\n } else if (direction === Direction.Down) {\n direction = Direction.DownLeft;\n } else {\n direction = Direction.Left;\n }\n } else {\n if (direction === Direction.Up) {\n direction = Direction.UpRight;\n } else if (direction === Direction.Down) {\n direction = Direction.DownRight;\n } else {\n direction = Direction.Right;\n }\n }\n }\n\n return direction;\n }\n\n getClosestEnemy(state: State): BotState | undefined {\n let closestBot: BotState | undefined = undefined;\n let closestDistance = Number.MAX_VALUE;\n\n state.bots.forEach(bot => {\n if (this.getTeamName() !== bot.team\n && state.myBot.point.distance(bot.point) <= closestDistance) {\n closestBot = bot;\n closestDistance = state.myBot.point.distance(bot.point);\n }\n });\n\n return closestBot;\n }\n\n getAvoidEnemyFields(state: State): number[][] {\n return state.bots.map(bot => {\n const safeBulletDistance = 60; // TODO: calculate based on how fast I can get out of bullet speed range\n const spread = 2 * this.world.botRadius + safeBulletDistance;\n return this.getRepulsiveField(state, new FieldObject(bot.point, this.world.botRadius, spread, 2));\n });\n }\n\n getAvoidBulletFields(state: State) {\n return state.bullets.map(bullet => {\n const safeBulletDistance = 40; // TODO: calculate based on how fast I can get out of bullet speed range\n const spread = 2 * this.world.bulletRadius + safeBulletDistance;\n const fieldObject = new FieldObject(bullet.point, this.world.bulletRadius, spread, 5);\n return this.getRepulsiveField(state, fieldObject);\n });\n }\n\n getAvoidWallField(state: State) {\n // TODO: Calculate a Perpendicular Field\n const distanceFromWall = this.world.botRadius * 3;\n let deltaX = 0;\n let deltaY = 0;\n\n if (state.myBot.point.x < distanceFromWall) {\n deltaX = distanceFromWall;\n } else if (state.myBot.point.x > this.world.width - distanceFromWall) {\n deltaX = -distanceFromWall;\n }\n\n if (state.myBot.point.y < distanceFromWall) {\n deltaY = distanceFromWall;\n } else if (state.myBot.point.y > this.world.width - distanceFromWall) {\n deltaY = -distanceFromWall;\n }\n\n return [\n this.getRepulsiveField(state, new FieldObject(new Point(0, 0), this.world.botRadius, this.world.botRadius * 4, 5)),\n this.getRepulsiveField(state, new FieldObject(new Point(0, 0), this.world.botRadius, this.world.botRadius * 4, 5)),\n this.getRepulsiveField(state, new FieldObject(new Point(0, 0), this.world.botRadius, this.world.botRadius * 4, 5)),\n this.getRepulsiveField(state, new FieldObject(new Point(0, 0), this.world.botRadius, this.world.botRadius * 4, 5)),\n ].reduce((delta, current) => [delta[0] + current[0], delta[1] + current[1]], [deltaX, deltaY]);\n }\n\n getPastBehaviorFields(state: State) {\n return this.history.map((point, index, history) => {\n const strength = (history.length - index) * (1 / this.maxHistory);\n const spread = this.world.bulletRadius;\n const fieldObject = new FieldObject(point, spread, spread, strength, Math.PI / 2);\n return this.getRepulsiveField(state, fieldObject);\n });\n }\n\n getAttractiveField(state: State, field: FieldObject): number[] {\n // TODO: Explore tangential field\n const distance = state.myBot.point.distance(field.point);\n const angle = Math.atan2((field.point.y - state.myBot.point.y), (field.point.x - state.myBot.point.x)) + field.tangentialModifier;\n\n let deltaX = 0;\n let deltaY = 0;\n\n if (distance < field.radius) {\n deltaX = 0;\n deltaY = 0;\n } else if (field.radius <= distance && distance <= field.spread + field.radius) {\n deltaX = field.strength * (distance - field.radius) * Math.cos(angle);\n deltaY = field.strength * (distance - field.radius) * Math.sin(angle);\n } else if (distance > field.spread + field.radius) {\n deltaX = field.strength * field.spread * Math.cos(angle);\n deltaY = field.strength * field.spread * Math.sin(angle);\n }\n\n return [deltaX, deltaY];\n }\n\n getRepulsiveField(state: State, field: FieldObject): number[] {\n // TODO: Explore tangential field\n const distance = state.myBot.point.distance(field.point);\n const angle = Math.atan2((field.point.y - state.myBot.point.y), (field.point.x - state.myBot.point.x)) + field.tangentialModifier;\n\n let deltaX = 0;\n let deltaY = 0;\n\n if (distance < field.radius) {\n deltaX = -Math.sign(Math.cos(angle)) * Number.MAX_VALUE;\n deltaY = -Math.sign(Math.sin(angle)) * Number.MAX_VALUE;\n } else if (field.radius <= distance && distance <= field.spread + field.radius) {\n deltaX = -field.strength * (field.spread + field.radius - distance) * Math.cos(angle);\n deltaY = -field.strength * (field.spread + field.radius - distance) * Math.sin(angle);\n } else if (distance > field.spread + field.radius) {\n deltaX = 0;\n deltaY = 0;\n }\n\n return [deltaX, deltaY];\n }\n\n render(context: CanvasRenderingContext2D, point: Point) {\n // TODO: Add animations for moving in various directions and shooting\n if (this.shooting || this.shotClock < 10) {\n context.drawImage(this.shootImage,\n point.x - this.world.botRadius,\n point.y - this.world.botRadius,\n this.world.botRadius * 2,\n this.world.botRadius * 2);\n } else {\n context.drawImage(this.image,\n point.x - this.world.botRadius,\n point.y - this.world.botRadius,\n this.world.botRadius * 2,\n this.world.botRadius * 2);\n }\n }\n\n public getTeamName(): string {\n return 'PotentialFieldBot';\n }\n\n toString() {\n return JSON.stringify({\n id: this.id,\n }, null, 4);\n }\n}\n","import * as images from './images';\nimport BotAction, {Action, Direction} from '../BotAction';\nimport {WorldState} from '../State';\nimport Point from '../Point';\nimport {Bot} from '../BotManager';\n\nconst IMAGES = [images.bird, images.duck, images.fish, images.frog, images.owl];\n\nfunction getRandomImage(): string {\n return IMAGES[Math.floor(Math.random() * IMAGES.length)];\n}\n\nfunction getRandomInt(max: number): number {\n return Math.floor(Math.random() * Math.floor(max));\n}\n\nexport default class RandomBot extends Bot {\n private readonly image: HTMLImageElement;\n\n constructor(protected world: WorldState) {\n super(world);\n this.image = new Image();\n this.image.src = getRandomImage();\n }\n\n getId(): string {\n return this.id;\n }\n\n takeTurn(): BotAction {\n let action: Action;\n let direction: Direction;\n\n switch(getRandomInt(2))\n {\n case 0:\n action = Action.Move;\n break;\n case 1:\n action = Action.Shoot;\n break;\n default:\n action = Action.None;\n }\n\n switch(getRandomInt(7))\n {\n case 0:\n direction = Direction.Up;\n break;\n case 1:\n direction = Direction.Down;\n break;\n case 2:\n direction = Direction.Left;\n break;\n case 3:\n direction = Direction.Right;\n break;\n case 4:\n direction = Direction.UpLeft;\n break;\n case 5:\n direction = Direction.UpRight;\n break;\n case 6:\n direction = Direction.DownRight;\n break;\n default:\n direction = Direction.None;\n }\n\n return new BotAction(action, direction);\n }\n\n render(context: CanvasRenderingContext2D, point: Point): void {\n // TODO: Don't allow bots to draw outside their allotted 16x16 space.\n\n context.drawImage(this.image, point.x - this.world.botRadius, point.y - this.world.botRadius, this.world.botRadius * 2, this.world.botRadius * 2);\n }\n\n public getTeamName(): string {\n return 'RandomBot';\n }\n\n toString(): string {\n return JSON.stringify({\n id: this.id,\n }, null, 4);\n }\n}\n","const bird = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQBAMAAADt3eJSAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAJFBMVEUAAAAIPGEynO1LtP+HNRQQdrcAAADUfAD/xk3vrExaGQD///8qJMamAAAAAXRSTlMAQObYZgAAAAFiS0dECx/XxMAAAAAHdElNRQfjBQ4RBiOd4iR8AAAAeklEQVQI12NgYGAQBAEgzahsbGysyMDi4uISlKaUwODe0dHpZKwkwOAxc+bMQlEgw3NGR0dHIYhR4uLiPhHEcAfJARlLVgHBUiCDgYEtNDQIzGBUUjQWApuspCoYpAhiBKmGBoUBGQxpQJAAYrCUsEwB0Qxcq7hWMQAArnIdm1hi3VAAAAAldEVYdGRhdGU6Y3JlYXRlADIwMTktMDUtMTRUMTc6MDY6MzUrMDM6MDCabweyAAAAJXRFWHRkYXRlOm1vZGlmeQAyMDE5LTA1LTE0VDE3OjA2OjM1KzAzOjAw6zK/DgAAAABJRU5ErkJggg==';\nconst duck = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAB1WlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNS40LjAiPgogICA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyI+CiAgICAgICAgIDx0aWZmOkNvbXByZXNzaW9uPjE8L3RpZmY6Q29tcHJlc3Npb24+CiAgICAgICAgIDx0aWZmOk9yaWVudGF0aW9uPjE8L3RpZmY6T3JpZW50YXRpb24+CiAgICAgICAgIDx0aWZmOlBob3RvbWV0cmljSW50ZXJwcmV0YXRpb24+MjwvdGlmZjpQaG90b21ldHJpY0ludGVycHJldGF0aW9uPgogICAgICA8L3JkZjpEZXNjcmlwdGlvbj4KICAgPC9yZGY6UkRGPgo8L3g6eG1wbWV0YT4KAtiABQAAAT5JREFUOBGNU7tOxDAQnKD7A/IFJ9EAuoLoJOiJUIqroeAD6FICJ0p00NLzAXQUkbB49BQoDcqVtFThFzA3a9mKHRNYKdbuZGd2s+skCKy5gA4gbF8iCbFoTLK+L/T3B9zDOCbaE3g69Mk7t1+eCN/3SCtgrQvqieqGzv8NZ8KIh1V/mTMC8qMC9fpxvKRJcefoaprqvNxzwPKhwvOdwv7CQYOOdHBdvkpSNm6RlzN83lSDJDtUbkcETk53HYEdxIyf1WwoGeRWVoBzaQAt++VnWJJ0sUoeGpzN5cykAwJn86nFhZy890VimBMIKy5rhc2J0xQnhnn3wE8HWNFa1ydmY9dB2B4HxXXWi1Q0srGS+6FhLhu7ESEeHOLsoAVJ/zGSq8cU52+tWSMdgJtQf4p0ySzm/abddQ51YgqajB/xFpV4hFEPQgAAAABJRU5ErkJggg==';\nconst fish = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAB1WlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNS40LjAiPgogICA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyI+CiAgICAgICAgIDx0aWZmOkNvbXByZXNzaW9uPjE8L3RpZmY6Q29tcHJlc3Npb24+CiAgICAgICAgIDx0aWZmOk9yaWVudGF0aW9uPjE8L3RpZmY6T3JpZW50YXRpb24+CiAgICAgICAgIDx0aWZmOlBob3RvbWV0cmljSW50ZXJwcmV0YXRpb24+MjwvdGlmZjpQaG90b21ldHJpY0ludGVycHJldGF0aW9uPgogICAgICA8L3JkZjpEZXNjcmlwdGlvbj4KICAgPC9yZGY6UkRGPgo8L3g6eG1wbWV0YT4KAtiABQAAAbhJREFUOBGVUz0sQ1EYPa8xGJsSYa34CUkXPwuRCI0+P5tEEwwYJJZu0lglVTaTqe1gICFhQEVjERY/C4kU0ZUITUeT6577+r12IOokr9937z3nvO/r+64FDe9iWjH+F4U126oSUU9fCFPNQLhJdoDtp1IuWbYAXL8BF2fHZsuSg+reGTUQTeLlNS9bP8aGeh9O47P4PE8ZrYcstpBKJnEwAtzM+X4VygG50rbFJDIeMmXRgOhI5BHP1DgL/RsNfrg5E1bRWQes7x7DVCCno4dAJmwZ8WAMCIZtMJabCVeiMWj1wvQe2bRckXVrQwXShlduQjP+gdQYsIWtR6VOJqDUnq2+nnXUkCg51+QwrnTVGg21bgs3uVrzRk+jY8wKBJZfgQ9buk/JrhNdAy4p0m+Ayjlfl1Ee7A8jHrtCe0fJmBp3kHbmH4BYC6JLJUJ5FZMLaWSX9dfQnNX+TWxQreEacCEmzMeG3tE2A1NyLAFHXOT4SSjCGHA8c4+X8Dd3GxPmdwldiRYSR9NplIt4ng2EzJlpVoaJg1EJOHjk8jI5/5ZW0YQXqhJwDigm1zXggheK8S/IRSLvGwQByCeh5mgHAAAAAElFTkSuQmCC';\nconst frog = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAB1WlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNS40LjAiPgogICA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyI+CiAgICAgICAgIDx0aWZmOkNvbXByZXNzaW9uPjE8L3RpZmY6Q29tcHJlc3Npb24+CiAgICAgICAgIDx0aWZmOk9yaWVudGF0aW9uPjE8L3RpZmY6T3JpZW50YXRpb24+CiAgICAgICAgIDx0aWZmOlBob3RvbWV0cmljSW50ZXJwcmV0YXRpb24+MjwvdGlmZjpQaG90b21ldHJpY0ludGVycHJldGF0aW9uPgogICAgICA8L3JkZjpEZXNjcmlwdGlvbj4KICAgPC9yZGY6UkRGPgo8L3g6eG1wbWV0YT4KAtiABQAAAXNJREFUOBGVUjFIA0EQnAuPjUREhBSiYGOlKVLZW0S0sbIPBDsFQTuxkHQKAe2CYCeSykZMCutYpdCYQhDBxyIgilopj+/Nnfvehzfqwmf3Zmcmv/unoGN6JRMyS5zvdpTUbk7ieQQ3iu+Yn3o03JPLIZQQNxSTJJ7H5lz/E5Syf/pxAxqAZDdK+32JPGPQPgAoZLD+KZJ4MmtsB/ntMYxODsd8/NYD6ut3MUwflBqYyYaLax4qE83uZs/z0nUO1Z0AZgSK+Xpb5UxPkTQ3VzuoFJqoImsN6EbgqNARzq+ZGiCwPI6hq389Xxr7BrTh4iS4QC7NjW6sUbPdFJ0Oyz6W868GefbfsDByC2aK+LgYSeRSQ22KwPH9OJMht6/sIiUTl5qZhgzReC9nF0pvM/Rn07ppetHP9xiDEcZir55GoxaA2mgEd0ZebQYvjlwewWhKroxg5tDc6AvoKx0WT3NyNkbsE2PP5XIHcpUjohQ6/6n3CctUvwwyewsLAAAAAElFTkSuQmCC';\nconst owl = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAB1WlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNS40LjAiPgogICA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyI+CiAgICAgICAgIDx0aWZmOkNvbXByZXNzaW9uPjE8L3RpZmY6Q29tcHJlc3Npb24+CiAgICAgICAgIDx0aWZmOk9yaWVudGF0aW9uPjE8L3RpZmY6T3JpZW50YXRpb24+CiAgICAgICAgIDx0aWZmOlBob3RvbWV0cmljSW50ZXJwcmV0YXRpb24+MjwvdGlmZjpQaG90b21ldHJpY0ludGVycHJldGF0aW9uPgogICAgICA8L3JkZjpEZXNjcmlwdGlvbj4KICAgPC9yZGY6UkRGPgo8L3g6eG1wbWV0YT4KAtiABQAAAaFJREFUOBGNUzFIA0EQnFc7wSoQCyshjdoEsRYFiQS/SJNoIRZa2YhYCWIhgpWks9LKIiZNipfgp1BstBAJgloYtLIw8JVgGc+b/dzlo0GycH9zu7Nze3v3gLbsYFxxEPdiUf6ASdjdbAD53kTI3cvHJdUKeH4M6dSI0ft39vymjTsGsazkWBO1537jsoIV/936DKf01ZBcWwEZTM7UPrCYAM7qQDk5LIndfFaR4GAqph53oDjTtMvOUUxnlMtcW8F42tVLD0fFKxRelMyaTE6Hz00FINfzbyUmH56fFagbV1VzYSXc6fsVMog5GCOHXHOVfaZ5E/vAU8XD3IYL7kJzHlZkENPHGDnkspk0xwhwsX0XsB+EGNoq4Xp1SfD0SQGfh1nBUQ6bLgKMGEUSaGEvBcrHccIbNxuY66aX52kzNVpbaKCeucR6bkb8bGyiPIvj806efgsIZSMirGRyNMD9W8w+qm4+Jmtr1SX7SPf/JDH0W6x1TNncvgOq6XOp4um8yOWWL/QcNpTn7fTbytuAWbwRydafVolmyZjF5j+g4wejQ+UbF5mK2AAAAABJRU5ErkJggg==';\n\nexport { bird, duck, fish, frog, owl };\n","export function generateId(): string {\n return Math.random().toString(36).substr(2, 9);\n}\n","import BotManager from '../BotManager';\nimport Bullet from '../Bullet';\n\nexport default abstract class GameRenderer {\n public abstract gameLoop(callback: () => void): void;\n\n public abstract render(botManagers: BotManager[], bullets: Bullet[]): void;\n\n public abstract renderFPS(fps: number): void;\n\n public abstract renderGameOver(orderedBots: BotManager[]): void;\n}\n\n","import Point from './Point';\nimport {Direction} from './BotAction';\n\nexport default class State {\n constructor(\n public readonly myBot: BotState,\n public readonly bots: BotState[],\n public readonly myBullets: BulletState[],\n public readonly bullets: BulletState[]\n ) {\n }\n\n toString(): string {\n return JSON.stringify(this);\n }\n}\n\nexport class BotState {\n constructor(\n public readonly id: string,\n public readonly team: string,\n public readonly point: Point\n ) {\n }\n\n toString(): string {\n return JSON.stringify(this);\n }\n}\n\nexport class BulletState {\n constructor(\n public readonly point: Point,\n public readonly direction: Direction\n ) {\n }\n\n toString(): string {\n return JSON.stringify(this);\n }\n}\n\nexport class WorldState {\n public readonly width: number;\n public readonly height: number;\n public readonly botRadius: number;\n public readonly bulletRadius: number;\n public readonly botStepDistance: number;\n public readonly bulletStepDistance: number;\n public readonly shootFrequency: number;\n public readonly minStartDistance: number;\n public readonly killPoints: number;\n public readonly survivePoints: number;\n\n constructor() {\n this.width = 800;\n this.height = 800;\n\n this.botRadius = 8;\n this.bulletRadius = 2;\n\n this.botStepDistance = 2;\n this.bulletStepDistance = 4;\n\n this.shootFrequency = 20;\n\n this.minStartDistance = 4 * this.botRadius;\n\n this.killPoints = 10;\n this.survivePoints = 40;\n }\n\n clone(): WorldState {\n return Object.assign(Object.create(Object.getPrototypeOf(this)), this);\n }\n}\n","import Point from './Point';\nimport {Direction} from './BotAction';\nimport BotManager from './BotManager';\nimport {WorldState} from './State';\n\nexport default class Bullet {\n constructor(\n public owner: BotManager,\n public point: Point,\n public direction: Direction,\n private readonly world: WorldState\n ) {\n }\n\n render(context: CanvasRenderingContext2D) {\n context.fillStyle = '#00FF00';\n context.beginPath();\n context.arc(this.point.x, this.point.y, this.world.bulletRadius, 0, 2 * Math.PI);\n context.fill();\n\n context.fillStyle = '#000';\n }\n}\n","export class Circle {\n public radius: number;\n public x: number;\n public y: number;\n\n constructor(radius: number, x: number, y: number) {\n this.radius = radius;\n this.x = x;\n this.y = y;\n }\n}\n\nexport class Rectangle {\n public width: number;\n public height: number;\n public x: number;\n public y: number;\n\n constructor(width: number, height: number, x: number, y: number) {\n this.width = width;\n this.height = height;\n this.x = x;\n this.y = y;\n }\n}\n\nexport function circlesOverlap(circle1: Circle, circle2: Circle): boolean {\n const distance = Math.hypot(circle2.x - circle1.x, circle2.y - circle1.y);\n return distance < circle1.radius + circle2.radius;\n}\n\nexport function rectangleAndCircleOverlap(rectangle: Rectangle, circle: Circle): boolean {\n const distX = Math.abs(circle.x - rectangle.x);\n const distY = Math.abs(circle.y - rectangle.y);\n\n if (distX > (rectangle.width / 2 + circle.radius)) {\n return false;\n }\n if (distY > (rectangle.height / 2 + circle.radius)) {\n return false;\n }\n\n if (distX <= (rectangle.width / 2)) {\n return true;\n }\n if (distY <= (rectangle.height / 2)) {\n return true;\n }\n\n const dx = distX - rectangle.width;\n const dy = distY - rectangle.height;\n return (dx * dx + dy * dy < (circle.radius * circle.radius));\n}\n\nexport function rectanglesOverlap(rectangle1: Rectangle, rectangle2: Rectangle): boolean {\n const left1 = rectangle1.x - rectangle1.width / 2;\n const right1 = rectangle1.x - rectangle1.width / 2;\n const top1 = rectangle1.y - rectangle1.height / 2;\n const bottom1 = rectangle1.y - rectangle1.height / 2;\n\n const left2 = rectangle2.x - rectangle2.width / 2;\n const right2 = rectangle2.x - rectangle2.width / 2;\n const top2 = rectangle2.y - rectangle2.height / 2;\n const bottom2 = rectangle2.y - rectangle2.height / 2;\n\n return left1 < right2\n && right1 > left2\n && top1 < bottom2\n && bottom1 > top2;\n}","import GameRenderer from './GameRenderer';\nimport BotManager from '../BotManager';\nimport Bullet from '../Bullet';\n\nexport default class ConsoleGameRenderer extends GameRenderer {\n render(botManagers: BotManager[], bullets: Bullet[]) {\n }\n\n renderFPS(fps: number) {\n }\n\n renderGameOver(orderedBots: BotManager[]) {\n orderedBots.forEach(bm => {\n console.log(`Bot: ${bm.id}, Team: ${bm.bot.getTeamName()}, Score: ${bm.score}`)\n });\n }\n\n gameLoop(callback: () => void): void {\n callback();\n }\n}\n","import Point from './Point';\nimport State, {BotState, BulletState, WorldState} from './State';\nimport BotManager, {Bot} from './BotManager';\nimport {Action, Direction} from './BotAction';\nimport Bullet from './Bullet';\nimport {Circle, circlesOverlap} from './Shapes';\nimport GameRenderer from './GameRenderer/GameRenderer';\nimport ConsoleGameRenderer from './GameRenderer/ConsoleGameRenderer';\n\nfunction getRandomInt(min: number, max: number): number {\n min = Math.ceil(min);\n max = Math.floor(max);\n return Math.floor(Math.random() * (max - min + 1)) + min;\n}\n\nexport default class Game {\n public readonly world: WorldState;\n private botManagers: BotManager[];\n private deadBots: BotManager[];\n private readonly bullets: Bullet[];\n\n private gameOver: boolean;\n\n private fps: number;\n private readonly fpsTimes: number[];\n\n private renderer: GameRenderer;\n\n constructor() {\n this.world = new WorldState();\n this.renderer = new ConsoleGameRenderer();\n\n this.botManagers = [];\n this.deadBots = [];\n this.bullets = [];\n\n this.gameOver = false;\n\n this.fps = 0;\n this.fpsTimes = [];\n }\n\n public setRenderer(renderer: GameRenderer) {\n this.renderer = renderer;\n }\n\n public start(): void {\n // TODO: This is done to allow images to load. Fix this?\n setTimeout(() => this.gameLoop(), 10);\n }\n\n private getMinDistanceToBot(point: Point): number {\n return this.botManagers.reduce(\n (min, otherBot) => Math.min(min, point.distance(otherBot.point)),\n Number.MAX_VALUE\n );\n }\n\n public addBotToGame(bot: Bot): void {\n let numTries = 0;\n let minDistance = 0;\n let point;\n do {\n numTries++;\n if (numTries > 1000) {\n throw new Error('Failed to find a spot to place the bot.');\n }\n point = new Point(\n getRandomInt(this.world.botRadius, this.world.width - this.world.botRadius),\n getRandomInt(this.world.botRadius, this.world.height - this.world.botRadius));\n minDistance = this.getMinDistanceToBot(point);\n } while (minDistance < this.world.minStartDistance);\n const botManager = new BotManager(bot, point);\n this.botManagers.push(botManager);\n }\n\n private gameLoop(): void {\n if (this.gameOver) {\n this.renderer.renderGameOver(this.compileResults());\n } else {\n this.collectBotActions();\n this.performBotActions();\n this.updateBulletPositions();\n\n this.renderer.render(this.botManagers, this.bullets);\n this.renderer.renderFPS(this.fps);\n\n this.handleCollisions();\n this.checkGameOver();\n\n this.renderer.gameLoop(() => {\n const now = performance.now();\n while (this.fpsTimes.length > 0 && this.fpsTimes[0] <= now - 1000) {\n this.fpsTimes.shift();\n }\n this.fpsTimes.push(now);\n this.fps = this.fpsTimes.length;\n // setTimeout(() => this.gameLoop(), 50);\n this.gameLoop();\n });\n }\n }\n\n private checkGameOver() {\n const oneTeamStanding = this.botManagers.every(\n (bm, i, managers) => bm.bot.getTeamName() === managers[0].bot.getTeamName()\n );\n\n if (oneTeamStanding) {\n this.gameOver = true;\n }\n }\n\n private collectBotActions() {\n this.botManagers.forEach((botManager, index, botManagers) => {\n const state = this.generateState(botManager);\n try {\n botManager.currentAction = botManager.bot.takeTurn(state);\n } catch (err) {\n console.error(`Bot ${botManager} returned error when taking a turn.`, err);\n // TODO: Fix all the places I'm removing items in a forEach. This won't work for consecutive items that need to be removed. It will work if we use a regular loop and start from the end of the array and go backwards.\n botManager.dead = true;\n this.deadBots.push(botManager);\n botManagers.splice(index, 1);\n }\n });\n }\n\n private generateState(botManager: BotManager): State {\n const myBot = new BotState(\n Game.cloneString(botManager.id),\n Game.cloneString(botManager.bot.getTeamName()),\n botManager.point.clone());\n\n const bots = this.botManagers\n .filter(bm => bm.id !== botManager.id)\n .map(bm => new BotState(\n Game.cloneString(bm.id),\n Game.cloneString(bm.bot.getTeamName()),\n bm.point.clone()));\n\n const myBullets = this.bullets\n .filter(bulletManager => bulletManager.owner.id === botManager.id)\n .map(bullet => new BulletState(bullet.point, bullet.direction));\n\n const bullets = this.bullets\n .filter(bullet => bullet.owner.id !== botManager.id)\n .map(bullet => new BulletState(bullet.point, bullet.direction));\n\n return new State(myBot, bots, myBullets, bullets);\n }\n\n private static cloneString(str: string): string {\n return (' ' + str).slice(1);\n }\n\n private performBotActions() {\n this.botManagers.forEach(botManager => {\n if (botManager.shotClock + this.world.shootFrequency > 0) {\n botManager.shotClock--;\n }\n const action = botManager.currentAction.action;\n const direction = botManager.currentAction.direction;\n\n if (action === Action.None || direction === Direction.None) {\n return;\n }\n\n if (action === Action.Move) {\n const point = this.getUpdatedPoint(direction, botManager.point, this.world.botStepDistance);\n if (point.x < this.world.botRadius) {\n point.x = this.world.botRadius;\n }\n if (point.y < this.world.botRadius) {\n point.y = this.world.botRadius;\n }\n if (point.x >= this.world.width - this.world.botRadius) {\n point.x = this.world.width - this.world.botRadius;\n }\n if (point.y >= this.world.height - this.world.botRadius) {\n point.y = this.world.height - this.world.botRadius;\n }\n botManager.point = point;\n } else if (action === Action.Shoot && botManager.shotClock + this.world.shootFrequency === 0) {\n botManager.shotClock = 0;\n const bullet = new Bullet(\n botManager,\n // TODO: Fix bullet starting position. Should start from the edge of the bot. It doesn't render that way because the bullet positions are updated once before getting rendered.\n this.getUpdatedPoint(direction, botManager.point, this.world.botRadius),\n direction,\n this.world.clone()\n );\n this.bullets.push(bullet);\n }\n });\n }\n\n private updateBulletPositions() {\n this.bullets.forEach((bullet, index, bullets) => {\n bullet.point = this.getUpdatedPoint(bullet.direction, bullet.point, this.world.bulletStepDistance);\n if (bullet.point.x <= -this.world.bulletRadius\n || bullet.point.y <= -this.world.bulletRadius\n || bullet.point.x >= this.world.width + this.world.bulletRadius\n || bullet.point.y >= this.world.height + this.world.bulletRadius) {\n bullets.splice(index, 1);\n }\n });\n }\n\n private getUpdatedPoint(direction: Direction, point: Point, stepDistance: number): Point {\n let updatedPoint = point.clone();\n\n if (direction === Direction.Up) {\n updatedPoint.y -= stepDistance;\n } else if (direction === Direction.Right) {\n updatedPoint.x += stepDistance;\n } else if (direction === Direction.Down) {\n updatedPoint.y += stepDistance;\n } else if (direction === Direction.Left) {\n updatedPoint.x -= stepDistance;\n } else if (direction === Direction.UpRight) {\n updatedPoint.y -= stepDistance;\n updatedPoint.x += stepDistance;\n } else if (direction === Direction.DownRight) {\n updatedPoint.y += stepDistance;\n updatedPoint.x += stepDistance;\n } else if (direction === Direction.DownLeft) {\n updatedPoint.y += stepDistance;\n updatedPoint.x -= stepDistance;\n } else if (direction === Direction.UpLeft) {\n updatedPoint.y -= stepDistance;\n updatedPoint.x -= stepDistance;\n }\n\n return updatedPoint;\n }\n\n private handleCollisions() {\n this.botManagers = this.botManagers.filter(\n bm1 => !this.botManagers.some(bm2 => {\n const collide = this.botsCollide(bm1, bm2);\n\n if (collide) {\n this.deadBots.push(bm1, bm2);\n bm1.dead = true;\n bm2.dead = true;\n // TODO: Can push more dead bots if more than 2 collide at a time. Clean up the lame way of doing this. Probably just use a Set to begin with.\n this.deadBots = Array.from(new Set(this.deadBots));\n this.addBotCollideKill(bm1.id, bm2.id);\n this.addBotCollideKill(bm2.id, bm1.id);\n }\n\n return collide;\n })\n );\n\n this.bullets.forEach((bullet, bulletIndex, bullets) => {\n this.botManagers.forEach((botManager, botIndex, botManagers) => {\n if (botManager.id !== bullet.owner.id\n && this.botAndBulletCollide(bullet.point, botManager.point)) {\n\n botManager.dead = true;\n this.deadBots.push(botManager);\n this.addBulletKill(bullet.owner.id, botManager.id);\n\n bullets.splice(bulletIndex, 1);\n botManagers.splice(botIndex, 1);\n }\n });\n });\n }\n\n private addBotCollideKill(killerId: string, killedId: string): void {\n const bot = this.botManagers\n .find(botManager => botManager.id === killerId);\n if (bot) {\n bot.kills[killedId] = true;\n }\n }\n\n private addBulletKill(killerId: string, killedId: string): void {\n let bot = this.botManagers\n .find(bm => bm.id === killerId);\n if (!bot) {\n bot = this.deadBots.find(bm => bm.id === killerId);\n }\n if (bot) {\n bot.kills[killedId] = true;\n }\n }\n\n private botAndBulletCollide(bulletPoint: Point, botPoint: Point): boolean {\n const botCircle = new Circle(this.world.botRadius, botPoint.x, botPoint.y);\n const bulletCircle = new Circle(this.world.bulletRadius, bulletPoint.x, bulletPoint.y);\n return circlesOverlap(botCircle, bulletCircle);\n }\n\n private botsCollide(bot1: BotManager, bot2: BotManager): boolean {\n const botCircle1 = new Circle(this.world.botRadius, bot1.point.x, bot1.point.y);\n const botCircle2 = new Circle(this.world.botRadius, bot2.point.x, bot2.point.y);\n return bot1 !== bot2\n && circlesOverlap(botCircle1, botCircle2);\n }\n\n private compileResults(): BotManager[] {\n return this.botManagers.concat(this.deadBots)\n .map(bm => {\n bm.score = Object.keys(bm.kills).length * this.world.killPoints\n + (!bm.dead ? this.world.survivePoints : 0);\n return bm;\n }).sort((bm1, bm2) => {\n return bm2.score - bm1.score;\n });\n }\n}\n","import GameRenderer from './GameRenderer';\nimport {WorldState} from '../State';\nimport BotManager from '../BotManager';\nimport Bullet from '../Bullet';\nimport Point from '../Point';\n\nexport default class GuiGameRenderer extends GameRenderer {\n private readonly context: CanvasRenderingContext2D;\n private readonly canvas: HTMLCanvasElement;\n\n constructor(private rootElement: HTMLDivElement, private world: WorldState) {\n super();\n\n const canvas = document.createElement('canvas');\n canvas.width = world.width;\n canvas.height = world.height;\n rootElement.appendChild(canvas);\n\n this.context = canvas.getContext('2d')!;\n this.canvas = canvas;\n }\n\n private clearScreen() {\n this.context.fillRect(0, 0, this.canvas.width, this.canvas.height);\n }\n\n render(botManagers: BotManager[], bullets: Bullet[]) {\n this.clearScreen();\n botManagers.forEach(bot => bot.render(this.context));\n bullets.forEach(bullet => bullet.render(this.context));\n }\n\n renderFPS(fps: number) {\n this.context.fillStyle = '#FF00FF';\n this.context.font = 'normal 12pt Arial';\n this.context.fillText(Math.round(fps) + ' fps', 4, 18);\n\n this.context.fillStyle = '#000000';\n }\n\n renderGameOver(orderedBots: BotManager[]) {\n this.clearScreen();\n this.context.fillStyle = '#0000FF';\n this.context.font = 'normal 12pt Arial';\n this.context.textBaseline = 'middle';\n\n let x = 12;\n let y = 20;\n\n this.context.fillText('Scoreboard', x, y);\n\n y += 20;\n\n this.context.fillStyle = '#00FF00';\n this.context.fillText('Bot', x, y);\n this.context.fillText('Score', x + 150, y);\n this.context.fillText('Kills', x + 225, y);\n this.context.fillText('Survive', x + 300, y);\n\n y += 20;\n\n orderedBots.forEach(bm => {\n // TODO: Bots that are larger than 16x16 based on world config do not render well here. Need to account for bot size here.\n let colX = x + this.world.botRadius;\n bm.bot.render(this.context, new Point(colX, y));\n\n colX += this.world.botRadius + 8;\n this.context.fillStyle = '#FF0000';\n const winnerId = bm.id;\n this.context.fillText(winnerId, colX, y);\n\n colX = x + 150;\n this.context.fillStyle = '#FFFF00';\n this.context.fillText(String(bm.score), colX, y);\n\n colX = x + 225;\n this.context.fillStyle = '#FFFF00';\n this.context.fillText(String(Object.keys(bm.kills).length), colX, y);\n\n colX = x + 300;\n this.context.fillStyle = '#FFFF00';\n this.context.fillText(bm.dead ? '0' : '1', colX, y);\n\n y += 20;\n });\n }\n\n gameLoop(callback: () => void): void {\n requestAnimationFrame(callback);\n }\n}","import React from 'react';\nimport './App.css';\n\nimport styles from './clobber/clobber.module.css';\nimport './clobber/prismjs/prism';\nimport Game from './clobber/clobber/Game';\nimport {generateId} from './clobber/clobber/ID';\nimport RandomBot from './clobber/clobber/bots/RandomBot';\nimport {Mushroom} from './clobber/clobber/bots/Mushroom';\nimport PotentialFieldBot from './clobber/clobber/bots/PotentialFieldBot';\nimport HumanBot from './clobber/clobber/bots/HumanBot';\nimport GuiGameRenderer from './clobber/clobber/GameRenderer/GuiGameRenderer';\n\nfunction addBots(game: Game) {\n game.addBotToGame(new HumanBot(game.world));\n game.addBotToGame(new Mushroom(game.world));\n game.addBotToGame(new Mushroom(game.world));\n game.addBotToGame(new Mushroom(game.world));\n game.addBotToGame(new Mushroom(game.world));\n game.addBotToGame(new Mushroom(game.world));\n game.addBotToGame(new RandomBot(game.world));\n game.addBotToGame(new RandomBot(game.world));\n game.addBotToGame(new RandomBot(game.world));\n game.addBotToGame(new PotentialFieldBot(game.world));\n game.addBotToGame(new PotentialFieldBot(game.world));\n game.addBotToGame(new PotentialFieldBot(game.world));\n game.addBotToGame(new PotentialFieldBot(game.world));\n game.addBotToGame(new PotentialFieldBot(game.world));\n}\n\nfunction App() {\n const gameRootRef = React.useRef(null);\n const botCodeRef = React.useRef(null);\n const gameRef = React.useRef(new Game());\n\n React.useEffect(() => {\n if (gameRootRef.current) {\n gameRef.current = new Game();\n gameRef.current.setRenderer(new GuiGameRenderer(gameRootRef.current, gameRef.current.world))\n addBots(gameRef.current);\n gameRef.current.start();\n }\n }, [])\n\n function handleNewGame() {\n if (gameRootRef.current) {\n gameRef.current = new Game();\n gameRef.current.setRenderer(new GuiGameRenderer(gameRootRef.current, gameRef.current.world))\n addBots(gameRef.current);\n }\n }\n\n function handleAddBot() {\n // eslint-disable-next-line no-eval\n const MyBot = eval(`(${botCodeRef.current?.innerText})`);\n const bot = new MyBot(generateId(), 'my-bot', gameRef.current.world.clone());\n gameRef.current.addBotToGame(bot);\n }\n\n function handleStartGame() {\n for (let i = 0; i < 10; i++) {\n gameRef.current.addBotToGame(new RandomBot(gameRef.current.world.clone()));\n }\n for (let i = 0; i < 3; i++) {\n gameRef.current.addBotToGame(new Mushroom(gameRef.current.world.clone()));\n }\n gameRef.current.start();\n }\n\n return (\n
\n

Clobber Bots

\n
\n\n

Human Bot

\n

Take the challenge and contest as the human-controlled bot, currently represented as a yellow circle.

\n

Use the arrow keys to move and the the E (up), D (down), S (left), and F (right) keys to shoot in the\n provided\n direction.

\n\n\n

Custom Bots!

\n\n \n \n \n\n
    \n
  1. Click New Game to setup a new game. You want to do this if you want to test modifications to your\n bot. If\n you click Start Game between games, all bots from previous games will get added.\n
  2. \n
  3. Add your bot in the code area below.
  4. \n
  5. Click Add Bot to add your bot to the game. For each click, a new copy of your bot will get added.\n
  6. \n
  7. Click Start Game to start the game and see it run!
  8. \n
\n\n
\n\t\n        {\n            `\n            class MyBot {\n                constructor(id, team, world) {\n                    this.id = id;\n                    this.team = team;\n                    this.world = world;\n                }\n            \n                getId() {\n                    return this.id;\n                }\n            \n                takeTurn(state) {\n                    let action = \"None\";\n                    let direction = \"Up\";\n            \n                    switch(MyBot.getRandomInt(2))\n                    {\n                        case 0:\n                            action = \"Move\";\n                            break;\n                        case 1:\n                            action = \"Shoot\";\n                            break;\n                        default:\n                            action = \"None\";\n                    }\n            \n                    switch(MyBot.getRandomInt(8))\n                    {\n                        case 0:\n                            direction = \"Up\";\n                            break;\n                        case 1:\n                            direction = \"Down\";\n                            break;\n                        case 2:\n                            direction = \"Left\";\n                            break;\n                        case 3:\n                            direction = \"Right\";\n                            break;\n                        case 4:\n                            direction = \"UpLeft\";\n                            break;\n                        case 5:\n                            direction = \"UpRight\";\n                            break;\n                        case 6:\n                            direction = \"DownRight\";\n                            break;\n                        default:\n                            direction = \"DownLeft\";\n                    }\n            \n                    return {action, direction};\n                }\n            \n                render(context, point) {\n                    context.fillStyle = \"#ffff00\";\n                    context.beginPath();\n                    context.arc(point.x, point.y, this.world.botRadius, 0, 2 * Math.PI);\n                    context.fill();\n                    context.fillStyle = \"#000\";\n                }\n            \n                toString() {\n                    return JSON.stringify({\n                        id: this.id,\n                        name: this.team\n                    }, null, 4);\n                }\n            \n                static getRandomInt(max) {\n                    return Math.floor(Math.random() * Math.floor(max));\n                }\n            }\n            `\n        }\n\t\n
\n
\n );\n}\n\nexport default App;\n","// extracted by mini-css-extract-plugin\nmodule.exports = {\"game\":\"clobber_game__6syM3\",\"botPre\":\"clobber_botPre__1uNm_\"};","import BotAction, {Action, Direction} from '../BotAction';\nimport Point from '../Point';\nimport {WorldState} from '../State';\nimport {Bot} from '../BotManager';\n\n/*\nThis bot can be controlled by a human. Use the arrow keys to move and the the E (up), D (down), S (left), and F (right) keys to shoot in the provided\ndirection.\n */\n\nconst KeyCodes = ['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'KeyE', 'KeyD', 'KeyS', 'KeyF'];\n\nexport default class HumanBot extends Bot {\n private shotClock: number;\n private readonly keys: { [key: string]: boolean };\n\n constructor(protected world: WorldState) {\n super(world);\n this.shotClock = 0;\n this.keys = {};\n\n document.addEventListener('keydown', (event) => this.captureKey(event));\n document.addEventListener('keyup', (event) => this.releaseKey(event));\n }\n\n private captureKey(event: KeyboardEvent): void {\n if (KeyCodes.indexOf(event.code) > -1) {\n event.preventDefault();\n }\n this.keys[event.code] = true;\n }\n\n private releaseKey(event: KeyboardEvent): void {\n if (KeyCodes.indexOf(event.code) > -1) {\n event.preventDefault();\n }\n this.keys[event.code] = false;\n }\n\n getId(): string {\n return this.id;\n }\n\n takeTurn(): BotAction {\n this.shotClock++;\n let action = Action.None;\n let shootDirection = Direction.None;\n\n if (this.keys['KeyE']) {\n shootDirection = Direction.Up;\n if (this.keys['KeyF']) {\n shootDirection = Direction.UpRight;\n } else if (this.keys['KeyS']) {\n shootDirection = Direction.UpLeft;\n }\n } else if (this.keys['KeyD']) {\n shootDirection = Direction.Down;\n if (this.keys['KeyF']) {\n shootDirection = Direction.DownRight;\n } else if (this.keys['KeyS']) {\n shootDirection = Direction.DownLeft;\n }\n } else if (this.keys['KeyF']) {\n shootDirection = Direction.Right;\n } else if (this.keys['KeyS']) {\n shootDirection = Direction.Left;\n }\n\n if (shootDirection !== undefined) {\n if (this.shotClock % this.world.shootFrequency === 0) {\n this.shotClock = 0;\n return new BotAction(Action.Shoot, shootDirection);\n }\n }\n\n let moveDirection = Direction.None;\n\n if (this.keys['ArrowUp']) {\n moveDirection = Direction.Up;\n if (this.keys['ArrowRight']) {\n moveDirection = Direction.UpRight;\n } else if (this.keys['ArrowLeft']) {\n moveDirection = Direction.UpLeft;\n }\n } else if (this.keys['ArrowDown']) {\n moveDirection = Direction.Down;\n if (this.keys['ArrowRight']) {\n moveDirection = Direction.DownRight;\n } else if (this.keys['ArrowLeft']) {\n moveDirection = Direction.DownLeft;\n }\n } else if (this.keys['ArrowRight']) {\n moveDirection = Direction.Right;\n } else if (this.keys['ArrowLeft']) {\n moveDirection = Direction.Left;\n }\n\n if (action === Action.None && moveDirection !== undefined) {\n action = Action.Move;\n }\n\n return new BotAction(action, moveDirection);\n }\n\n render(context: CanvasRenderingContext2D, point: Point): void {\n context.fillStyle = '#FFFF00';\n context.beginPath();\n context.arc(point.x, point.y, this.world.botRadius, 0, 2 * Math.PI);\n context.fill();\n context.fillStyle = '#000000';\n }\n\n public getTeamName(): string {\n return 'HumanBot';\n }\n\n toString(): string {\n return JSON.stringify({\n id: this.id,\n }, null, 4);\n }\n}\n","import Prism from 'prismjs';\n\nimport 'prismjs/components/prism-javascript';\nimport './prismjs-theme-darcula.css';\n\nimport 'prismjs/plugins/normalize-whitespace/prism-normalize-whitespace';\n\nPrism.plugins.NormalizeWhitespace.setDefaults({\n 'remove-trailing': true,\n 'remove-indent': true,\n 'left-trim': true,\n 'right-trim': true\n});\n","// This optional code is used to register a service worker.\n// register() is not called by default.\n\n// This lets the app load faster on subsequent visits in production, and gives\n// it offline capabilities. However, it also means that developers (and users)\n// will only see deployed updates on subsequent visits to a page, after all the\n// existing tabs open on the page have been closed, since previously cached\n// resources are updated in the background.\n\n// To learn more about the benefits of this model and instructions on how to\n// opt-in, read https://bit.ly/CRA-PWA\n\nconst isLocalhost = Boolean(\n window.location.hostname === 'localhost' ||\n // [::1] is the IPv6 localhost address.\n window.location.hostname === '[::1]' ||\n // 127.0.0.0/8 are considered localhost for IPv4.\n window.location.hostname.match(\n /^127(?:\\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/\n )\n);\n\ntype Config = {\n onSuccess?: (registration: ServiceWorkerRegistration) => void;\n onUpdate?: (registration: ServiceWorkerRegistration) => void;\n};\n\nexport function register(config?: Config) {\n if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {\n // The URL constructor is available in all browsers that support SW.\n const publicUrl = new URL(\n process.env.PUBLIC_URL,\n window.location.href\n );\n if (publicUrl.origin !== window.location.origin) {\n // Our service worker won't work if PUBLIC_URL is on a different origin\n // from what our page is served on. This might happen if a CDN is used to\n // serve assets; see https://github.com/facebook/create-react-app/issues/2374\n return;\n }\n\n window.addEventListener('load', () => {\n const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;\n\n if (isLocalhost) {\n // This is running on localhost. Let's check if a service worker still exists or not.\n checkValidServiceWorker(swUrl, config);\n\n // Add some additional logging to localhost, pointing developers to the\n // service worker/PWA documentation.\n navigator.serviceWorker.ready.then(() => {\n console.log(\n 'This web app is being served cache-first by a service ' +\n 'worker. To learn more, visit https://bit.ly/CRA-PWA'\n );\n });\n } else {\n // Is not localhost. Just register service worker\n registerValidSW(swUrl, config);\n }\n });\n }\n}\n\nfunction registerValidSW(swUrl: string, config?: Config) {\n navigator.serviceWorker\n .register(swUrl)\n .then(registration => {\n registration.onupdatefound = () => {\n const installingWorker = registration.installing;\n if (installingWorker == null) {\n return;\n }\n installingWorker.onstatechange = () => {\n if (installingWorker.state === 'installed') {\n if (navigator.serviceWorker.controller) {\n // At this point, the updated precached content has been fetched,\n // but the previous service worker will still serve the older\n // content until all client tabs are closed.\n console.log(\n 'New content is available and will be used when all ' +\n 'tabs for this page are closed. See https://bit.ly/CRA-PWA.'\n );\n\n // Execute callback\n if (config && config.onUpdate) {\n config.onUpdate(registration);\n }\n } else {\n // At this point, everything has been precached.\n // It's the perfect time to display a\n // \"Content is cached for offline use.\" message.\n console.log('Content is cached for offline use.');\n\n // Execute callback\n if (config && config.onSuccess) {\n config.onSuccess(registration);\n }\n }\n }\n };\n };\n })\n .catch(error => {\n console.error('Error during service worker registration:', error);\n });\n}\n\nfunction checkValidServiceWorker(swUrl: string, config?: Config) {\n // Check if the service worker can be found. If it can't reload the page.\n fetch(swUrl, {\n headers: { 'Service-Worker': 'script' }\n })\n .then(response => {\n // Ensure service worker exists, and that we really are getting a JS file.\n const contentType = response.headers.get('content-type');\n if (\n response.status === 404 ||\n (contentType != null && contentType.indexOf('javascript') === -1)\n ) {\n // No service worker found. Probably a different app. Reload the page.\n navigator.serviceWorker.ready.then(registration => {\n registration.unregister().then(() => {\n window.location.reload();\n });\n });\n } else {\n // Service worker found. Proceed as normal.\n registerValidSW(swUrl, config);\n }\n })\n .catch(() => {\n console.log(\n 'No internet connection found. App is running in offline mode.'\n );\n });\n}\n\nexport function unregister() {\n if ('serviceWorker' in navigator) {\n navigator.serviceWorker.ready\n .then(registration => {\n registration.unregister();\n })\n .catch(error => {\n console.error(error.message);\n });\n }\n}\n","import React from 'react';\nimport ReactDOM from 'react-dom';\nimport './index.css';\nimport App from './App';\nimport * as serviceWorker from './serviceWorker';\n\nReactDOM.render(\n \n \n ,\n document.getElementById('root')\n);\n\n// If you want your app to work offline and load faster, you can change\n// unregister() to register() below. Note this comes with some pitfalls.\n// Learn more about service workers: https://bit.ly/CRA-PWA\nserviceWorker.unregister();\n"],"sourceRoot":""}