diff --git a/.gitignore b/.gitignore index 485dee6..c75370f 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,7 @@ .idea +.git +user_interface/.DS_Store +user_interface/dist +user_interface/node_modules +backend/tests/.* +__pycache__ diff --git a/Bandit.py b/Bandit.py deleted file mode 100644 index 089e6db..0000000 --- a/Bandit.py +++ /dev/null @@ -1,299 +0,0 @@ -from __future__ import annotations - -from typing import Dict, Tuple, List, Optional, Union -import numpy as np -from CustomExceptions import CombinationLengthException, CombinationValuesException, WinningMoneyException, \ - ProbabilitiesSumException, ProbabilitiesArrayLengthException, SettingStateException - - -class OneHandBandit: - """ - There are m*n slots(columns,row) - and k pictures for each slot(column) - Pictures represented as integers 0..k-1 - (You can create visuals by setting them to real image files) - """ - players: List[OneHandBandit] = [] - - def __init__(self, m: int, n: int, k: int, probs: Optional[List[float, ...]] = None): - """ - m: number of columns - n: number of rows - k: number of pictures - k(number of pictures) must be greater or equal to n(number of rows) - """ - - self.drumsColumns = [[i for i in range(k)] for _ in range(m)] - self.columnsNumb = m - self.rowsNumb = n - self.picturesNumb = k - - self.probabilities: List[float, ...] = list() - self.setProbabilities(probs) - - self.combinations: Dict[Tuple[int, ...], int] = dict() - self.price: int = 0 - - self.money: int = 0 - self.moneySpent = 0 - self.moneyWon: int = 0 - - # maybe not in constructor??? - # old code string: self.state = self.displayCurrentTurn() - self.state: List[List[int,],] = list() - - OneHandBandit.players.append(self) - - # price of the 1 turn of game??? - - # winning combinations input - # combinations like (7,7,7):100$, (3,5,7):200$, ('king','king','king'):500$ etc - def setWinningCombs(self, combs: Dict[Tuple[int, ...], int]): - """ - combs: dict of winning combinations - example: { (7, 7, 7): 100, (3, 5, 7): 200, (4, 3, 0): 500 } - """ - combs = combs.copy() - for comb in combs: - if len(comb) != self.columnsNumb: - raise CombinationLengthException - elif max(comb) > (self.picturesNumb - 1) or min(comb) < 0: - raise CombinationValuesException - elif combs[comb] < 0: - raise WinningMoneyException - - self.combinations = combs - - def addWinningComb(self, comb: Tuple[int, ...], win: int): - self.combinations[comb] = win - - # the price of 1 turn - def setPriceOfGame(self, price): - self.price = price - - # money player gives to bandit - def startGame(self, money: int, modeling: bool = False) -> None: - if money > 0: - self.money = money - if not modeling: - print(f"you deposited {money} money") - - # methods to good display of games... - - # Here is the trickiest part - # we want to differ probabilities of images - # on each column and row - def setProbabilities(self, probs: Optional[List[float, ...]] = None): - f""" - set probabilities({self.probabilities}) for corresponding pictures(with indices from 0 till {self.picturesNumb}) - , where sum of probabilities have to equal 1 - """ - if probs is None: - # uniform distribution - probs = [1 / self.picturesNumb for i in range(self.picturesNumb)] - - if 0.001 < abs(sum(probs) - 1.0): - # sum of probs dont equal 1 - raise ProbabilitiesSumException - - if len(probs) != self.picturesNumb: - raise ProbabilitiesArrayLengthException - - self.probabilities = probs.copy() - - def setProbabilitiesFromTxtFile(self, filename: str = "probabilities.txt"): - f""" - read probabilities from .txt file, where probabilities values delimit with one space, - then use {self.setProbabilities} function - raise: - ValueError if probabilities values are not float - """ - with open(filename, "r") as f: - probsLine = f.readline() - probs = list(map(lambda x: float(x), probsLine.split(" "))) - - self.setProbabilities(probs) - - def genTurnProbabilities(self, rolledPicturesIdxs: List[int, ...]) -> List: - newProbs = np.array(self.probabilities, dtype=float) - divideCoeff = 1 - sum([self.probabilities[idx] for idx in rolledPicturesIdxs]) - newProbs[rolledPicturesIdxs] = 0.0 - newProbs = newProbs / divideCoeff - return list(newProbs) - - def genBaseProbabilities(self, coef: float): - # todo: now - # не дуже розумію що мається на увазі під "рандомно генерувати ймовірності", - # не розумію як їх генерувати, в залежності від коефіцієнту віддачі ігрового автомата, - # не розумію, що означає (Монте-Карло) цилк гравця - pass - - def getOneHandBanditReturn(self, gamesCount: int = 10 ** 4, probs: Optional[List[float, ...]] = None) \ - -> Tuple[int, float]: - oldProbs = self.probabilities - oldMoneySpent = self.moneySpent - oldMoneyWon = self.moneyWon - if probs is not None: - self.setProbabilities(probs) - self.moneyWon = 0 - self.moneySpent = 0 - - money = self.price * gamesCount - - modeling = True - self.startGame(money, modeling=modeling) - self.play(gamesCount, modeling=modeling) - gameReturn = self.moneyWon - gameReturnPercentage = gameReturn / self.moneySpent - if probs is not None: - self.setProbabilities(oldProbs) - self.moneyWon = oldMoneyWon - self.moneySpent = oldMoneySpent - - return gameReturn, gameReturnPercentage - - # randomly display n*m result of turn - def currentTurn(self, modeling: bool = False) -> int: - """ - return: np.array([np.array(dtype=int), ]) - example: - np.array([np.array([0, 1, ... , self.picturesNumb-1]), ]) - """ - self.setState([self.turnColumn(i) for i in range(self.columnsNumb)]) - self.money -= self.price - self.moneySpent += self.price - won = self.currentWon() - - if not modeling: - self.printState() - - return won - - # this methods could be with further changes.... - # our probalities are not necessary uniform!!!! - # get random i-th column - # TODO: generalize and modify this!!! - # !!! U should implement different options for this method... - def turnColumn(self, i) -> List[int,]: - # get n(rowsNumb) images from k(picturesNumb) without duplicates - # easy uniform probability form - # could be kept for testing - # but more complex form also implemented - downIdx = self.rowsNumb - 1 - upIdx = 0 - rolledPicturesIdx = [] - resColumn = [-1, ] * self.rowsNumb - for idx in range(self.rowsNumb): - newProbs = self.genTurnProbabilities(rolledPicturesIdxs=rolledPicturesIdx) - - assert abs(sum(newProbs) - 1) < 0.001 - - rolledPicIdx = np.random.choice(self.drumsColumns[i], size=1, p=newProbs)[0] - rolledPicturesIdx.append(rolledPicIdx) - - if (idx % 2) == 0: - resColumn[downIdx] = rolledPicIdx - downIdx -= 1 - else: - resColumn[upIdx] = rolledPicIdx - upIdx += 1 - - return resColumn - - def getRow(self, i) -> np.array: - return np.array([self.state[j][i] for j in range(self.columnsNumb)]) - - def getColumn(self, j): - return self.state[j] - - def printState(self): - print(f"current state: \n{np.array(self.state).T}") - # print("money spent:", self.moneySpent) - print("money won:", self.moneyWon) - print("money remained:", self.money) - - # getters ..setters - def getState(self) -> np.array: - return self.state - - def setState(self, state: List[List[int, ], ]) -> None: - if all([len(col) == len(set(col)) for col in state]): - self.state = state - else: - raise SettingStateException - - def currentWon(self) -> int: - won = 0 - for i in range(self.rowsNumb): - comb = tuple(self.getRow(i)) - if comb in self.combinations: - # it is possible to have several winning combinations.... - won += self.combinations[comb] - - self.moneyWon += won # ???? not sure if it is needed - return won - - # number of turns - def play(self, n, modeling: bool = False): - if self.money < self.price * n: - print("Not enough money") - return None - # play game n times - for _ in range(n): - won = self.currentTurn(modeling=modeling) - if not modeling: - print(f"you won {won} money for one turn") - print(f"you spent {self.price} money for one turn") - print() - - # winning sum in fact could be (and often is) negative ;-)) - @property - def currentPlayerWon(self): - return self.moneyWon - self.moneySpent # not sure - - # todo: мається на увазі загальний виграш всіх гравців? - @property - def totalPlayersWon(self): - totalWin = 0 - for p in OneHandBandit.players: - totalWin += p.currentPlayerWon - return totalWin - - # Our main goal - # !! modification needed - def getWinCoef(self): - return self.moneyWon / self.moneySpent - - -if __name__ == "__main__": - p1 = OneHandBandit(3, 3, 4, probs=[0.5, 0.2, 0.2, 0.1]) - p1.setPriceOfGame(10) - p1.setWinningCombs({(0, 0, 0): 15, (1, 1, 1): 15, (2, 2, 2): 15, (1, 2, 3): 20, }) - p1.addWinningComb((3, 3, 3), 15) - modelingGamesCount = 10 ** 4 - returnMoney, returnCoef = p1.getOneHandBanditReturn(gamesCount=modelingGamesCount) - print(f"this one hand bandit return: {returnMoney} with return coefficient: {returnCoef} " - f"for {modelingGamesCount} games") - p1.startGame(100) - p1.play(10) - - print(f"{p1.currentPlayerWon=}\n") - - # p2 = OneHandBandit(4, 3, 5, probs=[0.1, 0.1, 0.1, 0.1, 0.6]) - p2 = OneHandBandit(4, 3, 5) - p2.setProbabilitiesFromTxtFile() - p2.setPriceOfGame(10) - p2.setWinningCombs({(0, 0, 0, 0): 15, (1, 1, 1, 1): 15, (2, 2, 2, 2): 15, (3, 3, 3, 3): 15, (1, 2, 3, 4): 20, }) - p2.addWinningComb((4, 4, 4, 4), 15) - - modelingGamesCount = 10 ** 4 - returnMoney, returnCoef = p1.getOneHandBanditReturn(gamesCount=modelingGamesCount) - print(f"this one hand bandit return: {returnMoney} with return coefficient: {returnCoef} " - f"for {modelingGamesCount} games") - - p2.startGame(100) - p2.play(10) - - print(f"{p2.currentPlayerWon=}") - - print(f"{p2.totalPlayersWon=}") diff --git a/Pipfile b/Pipfile index c52bbfe..d8c303b 100644 --- a/Pipfile +++ b/Pipfile @@ -4,11 +4,18 @@ verify_ssl = true name = "pypi" [packages] -numpy = "*" jupyter = "*" -coverage = "*" +numpy = "*" +pipfile = "*" +flask = "*" +tqdm = "*" +scipy = "*" +matplotlib = "*" [dev-packages] +coverage = "*" +pyment = "*" +pdoc3 = "*" [requires] python_version = "3.8" diff --git a/Pipfile.lock b/Pipfile.lock index 8de89fb..da1b91c 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "1a4782ac8359026ac65fa6f2bb8cadbb28adcd196024854cad66b7b8e6b661d8" + "sha256": "6aedd154b55287cd81c4ddc32f38e3137cca7cd7b2269d982f4837be8ee92202" }, "pipfile-spec": 6, "requires": { @@ -144,60 +144,29 @@ ], "version": "==1.15.0" }, + "click": { + "hashes": [ + "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e", + "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48" + ], + "markers": "python_version >= '3.7'", + "version": "==8.1.3" + }, "colorama": { "hashes": [ "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b", "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2" ], - "markers": "sys_platform == 'win32'", + "markers": "platform_system == 'Windows' and sys_platform == 'win32' and platform_system == 'Windows'", "version": "==0.4.4" }, - "coverage": { + "cycler": { "hashes": [ - "sha256:03e2a7826086b91ef345ff18742ee9fc47a6839ccd517061ef8fa1976e652ce9", - "sha256:07e6db90cd9686c767dcc593dff16c8c09f9814f5e9c51034066cad3373b914d", - "sha256:18d520c6860515a771708937d2f78f63cc47ab3b80cb78e86573b0a760161faf", - "sha256:1ebf730d2381158ecf3dfd4453fbca0613e16eaa547b4170e2450c9707665ce7", - "sha256:21b7745788866028adeb1e0eca3bf1101109e2dc58456cb49d2d9b99a8c516e6", - "sha256:26e2deacd414fc2f97dd9f7676ee3eaecd299ca751412d89f40bc01557a6b1b4", - "sha256:2c6dbb42f3ad25760010c45191e9757e7dce981cbfb90e42feef301d71540059", - "sha256:2fea046bfb455510e05be95e879f0e768d45c10c11509e20e06d8fcaa31d9e39", - "sha256:34626a7eee2a3da12af0507780bb51eb52dca0e1751fd1471d0810539cefb536", - "sha256:37d1141ad6b2466a7b53a22e08fe76994c2d35a5b6b469590424a9953155afac", - "sha256:46191097ebc381fbf89bdce207a6c107ac4ec0890d8d20f3360345ff5976155c", - "sha256:4dd8bafa458b5c7d061540f1ee9f18025a68e2d8471b3e858a9dad47c8d41903", - "sha256:4e21876082ed887baed0146fe222f861b5815455ada3b33b890f4105d806128d", - "sha256:58303469e9a272b4abdb9e302a780072c0633cdcc0165db7eec0f9e32f901e05", - "sha256:5ca5aeb4344b30d0bec47481536b8ba1181d50dbe783b0e4ad03c95dc1296684", - "sha256:68353fe7cdf91f109fc7d474461b46e7f1f14e533e911a2a2cbb8b0fc8613cf1", - "sha256:6f89d05e028d274ce4fa1a86887b071ae1755082ef94a6740238cd7a8178804f", - "sha256:7a15dc0a14008f1da3d1ebd44bdda3e357dbabdf5a0b5034d38fcde0b5c234b7", - "sha256:8bdde1177f2311ee552f47ae6e5aa7750c0e3291ca6b75f71f7ffe1f1dab3dca", - "sha256:8ce257cac556cb03be4a248d92ed36904a59a4a5ff55a994e92214cde15c5bad", - "sha256:8cf5cfcb1521dc3255d845d9dca3ff204b3229401994ef8d1984b32746bb45ca", - "sha256:8fbbdc8d55990eac1b0919ca69eb5a988a802b854488c34b8f37f3e2025fa90d", - "sha256:9548f10d8be799551eb3a9c74bbf2b4934ddb330e08a73320123c07f95cc2d92", - "sha256:96f8a1cb43ca1422f36492bebe63312d396491a9165ed3b9231e778d43a7fca4", - "sha256:9b27d894748475fa858f9597c0ee1d4829f44683f3813633aaf94b19cb5453cf", - "sha256:9baff2a45ae1f17c8078452e9e5962e518eab705e50a0aa8083733ea7d45f3a6", - "sha256:a2a8b8bcc399edb4347a5ca8b9b87e7524c0967b335fbb08a83c8421489ddee1", - "sha256:acf53bc2cf7282ab9b8ba346746afe703474004d9e566ad164c91a7a59f188a4", - "sha256:b0be84e5a6209858a1d3e8d1806c46214e867ce1b0fd32e4ea03f4bd8b2e3359", - "sha256:b31651d018b23ec463e95cf10070d0b2c548aa950a03d0b559eaa11c7e5a6fa3", - "sha256:b78e5afb39941572209f71866aa0b206c12f0109835aa0d601e41552f9b3e620", - "sha256:c76aeef1b95aff3905fb2ae2d96e319caca5b76fa41d3470b19d4e4a3a313512", - "sha256:dd035edafefee4d573140a76fdc785dc38829fe5a455c4bb12bac8c20cfc3d69", - "sha256:dd6fe30bd519694b356cbfcaca9bd5c1737cddd20778c6a581ae20dc8c04def2", - "sha256:e5f4e1edcf57ce94e5475fe09e5afa3e3145081318e5fd1a43a6b4539a97e518", - "sha256:ec6bc7fe73a938933d4178c9b23c4e0568e43e220aef9472c4f6044bfc6dd0f0", - "sha256:f1555ea6d6da108e1999b2463ea1003fe03f29213e459145e70edbaf3e004aaa", - "sha256:f5fa5803f47e095d7ad8443d28b01d48c0359484fec1b9d8606d0e3282084bc4", - "sha256:f7331dbf301b7289013175087636bbaf5b2405e57259dd2c42fdcc9fcc47325e", - "sha256:f9987b0354b06d4df0f4d3e0ec1ae76d7ce7cbca9a2f98c25041eb79eec766f1", - "sha256:fd9e830e9d8d89b20ab1e5af09b32d33e1a08ef4c4e14411e559556fd788e6b2" + "sha256:3a27e95f763a428a739d2add979fa7494c912a32c17c4c38c4d5f082cad165a3", + "sha256:9c87405839a19696e837b3b818fed3f5f69f16f1eec1a1ad77e043dcea9c772f" ], - "index": "pypi", - "version": "==6.3.2" + "markers": "python_version >= '3.6'", + "version": "==0.11.0" }, "debugpy": { "hashes": [ @@ -261,6 +230,30 @@ ], "version": "==2.15.3" }, + "flask": { + "hashes": [ + "sha256:315ded2ddf8a6281567edb27393010fe3406188bafbfe65a3339d5787d89e477", + "sha256:fad5b446feb0d6db6aec0c3184d16a8c1f6c3e464b511649c8918a9be100b4fe" + ], + "index": "pypi", + "version": "==2.1.2" + }, + "fonttools": { + "hashes": [ + "sha256:c0fdcfa8ceebd7c1b2021240bd46ef77aa8e7408cf10434be55df52384865f8e", + "sha256:f829c579a8678fa939a1d9e9894d01941db869de44390adb49ce67055a06cc2a" + ], + "markers": "python_version >= '3.7'", + "version": "==4.33.3" + }, + "importlib-metadata": { + "hashes": [ + "sha256:5d26852efe48c0a32b0509ffbc583fda1a2266545a78d104a6f4aff3db17d700", + "sha256:c58c8eb8a762858f49e18436ff552e83914778e50e9d2f1660535ffb364552ec" + ], + "markers": "python_version < '3.10'", + "version": "==4.11.4" + }, "importlib-resources": { "hashes": [ "sha256:b6062987dfc51f0fcb809187cffbd60f35df7acb4589091f154214af6d0d49d3", @@ -271,19 +264,19 @@ }, "ipykernel": { "hashes": [ - "sha256:0e28273e290858393e86e152b104e5506a79c13d25b951ac6eca220051b4be60", - "sha256:2b0987af43c0d4b62cecb13c592755f599f96f29aafe36c01731aaa96df30d39" + "sha256:720f2a07aadf70ca05b034f2cdd0590a8bd56581ddf3b7ec4a37075366270e89", + "sha256:cbf50ed3dc9998d4077d8cd263275f232cbe67dbd2e79cd6d2644f3a4418148b" ], "markers": "python_version >= '3.7'", - "version": "==6.13.0" + "version": "==6.14.0" }, "ipython": { "hashes": [ - "sha256:341456643a764c28f670409bbd5d2518f9b82c013441084ff2c2fc999698f83b", - "sha256:807ae3cf43b84693c9272f70368440a9a7eaa2e7e6882dad943c32fbf7e51402" + "sha256:7ca74052a38fa25fe9bedf52da0be7d3fdd2fb027c3b778ea78dfe8c212937d1", + "sha256:f2db3a10254241d9b447232cec8b424847f338d9d36f9a577a6192c332a46abd" ], "markers": "python_version >= '3.3'", - "version": "==8.3.0" + "version": "==8.4.0" }, "ipython-genutils": { "hashes": [ @@ -299,6 +292,14 @@ ], "version": "==7.7.0" }, + "itsdangerous": { + "hashes": [ + "sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44", + "sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a" + ], + "markers": "python_version >= '3.7'", + "version": "==2.1.2" + }, "jedi": { "hashes": [ "sha256:637c9635fcf47945ceb91cd7f320234a7be540ded6f3e99a50cb6febdfd1ba8d", @@ -317,11 +318,11 @@ }, "jsonschema": { "hashes": [ - "sha256:636694eb41b3535ed608fe04129f26542b59ed99808b4f688aa32dcf55317a83", - "sha256:77281a1f71684953ee8b3d488371b162419767973789272434bbc3f29d9c8823" + "sha256:1c92d2db1900b668201f1797887d66453ab1fbfea51df8e4b46236689c427baf", + "sha256:9d6397ba4a6c0bf0300736057f649e3e12ecbc07d3e81a0dacb72de4e9801957" ], "markers": "python_version >= '3.7'", - "version": "==4.4.0" + "version": "==4.6.0" }, "jupyter": { "hashes": [ @@ -334,11 +335,11 @@ }, "jupyter-client": { "hashes": [ - "sha256:3bcc8e08a294d0fa9406e48cfe17e11ef0efdb7c504fe8cc335128e3ef8f3dac", - "sha256:671dd2d90d03f41716b09627a4eb06bb37875f92bf6563cc2ce4fe71c61c5cda" + "sha256:17d74b0d0a7b24f1c8c527b24fcf4607c56bee542ffe8e3418e50b21e514b621", + "sha256:aa9a6c32054b290374f95f73bb0cae91455c58dfb84f65c8591912b8f65e6d56" ], "markers": "python_version >= '3.7'", - "version": "==7.3.0" + "version": "==7.3.4" }, "jupyter-console": { "hashes": [ @@ -372,6 +373,55 @@ "markers": "python_version >= '3.6'", "version": "==1.1.0" }, + "kiwisolver": { + "hashes": [ + "sha256:007799c7fa934646318fc128b033bb6e6baabe7fbad521bfb2279aac26225cd7", + "sha256:130c6c35eded399d3967cf8a542c20b671f5ba85bd6f210f8b939f868360e9eb", + "sha256:1858ad3cb686eccc7c6b7c5eac846a1cfd45aacb5811b2cf575e80b208f5622a", + "sha256:1ae7aa0784aeadfbd693c27993727792fbe1455b84d49970bad5886b42976b18", + "sha256:1d2c744aeedce22c122bb42d176b4aa6d063202a05a4abdacb3e413c214b3694", + "sha256:21a3a98f0a21fc602663ca9bce2b12a4114891bdeba2dea1e9ad84db59892fca", + "sha256:22ccba48abae827a0f952a78a7b1a7ff01866131e5bbe1f826ce9bda406bf051", + "sha256:26b5a70bdab09e6a2f40babc4f8f992e3771751e144bda1938084c70d3001c09", + "sha256:2d76780d9c65c7529cedd49fa4802d713e60798d8dc3b0d5b12a0a8f38cca51c", + "sha256:325fa1b15098e44fe4590a6c5c09a212ca10c6ebb5d96f7447d675f6c8340e4e", + "sha256:3a297d77b3d6979693f5948df02b89431ae3645ec95865e351fb45578031bdae", + "sha256:3b1dcbc49923ac3c973184a82832e1f018dec643b1e054867d04a3a22255ec6a", + "sha256:40240da438c0ebfe2aa76dd04b844effac6679423df61adbe3437d32f23468d9", + "sha256:46c6e5018ba31d5ee7582f323d8661498a154dea1117486a571db4c244531f24", + "sha256:46fb56fde006b7ef5f8eaa3698299b0ea47444238b869ff3ced1426aa9fedcb5", + "sha256:4dc350cb65fe4e3f737d50f0465fa6ea0dcae0e5722b7edf5d5b0a0e3cd2c3c7", + "sha256:51078855a16b7a4984ed2067b54e35803d18bca9861cb60c60f6234b50869a56", + "sha256:547111ef7cf13d73546c2de97ce434935626c897bdec96a578ca100b5fcd694b", + "sha256:5fb73cc8a34baba1dfa546ae83b9c248ef6150c238b06fc53d2773685b67ec67", + "sha256:654280c5f41831ddcc5a331c0e3ce2e480bbc3d7c93c18ecf6236313aae2d61a", + "sha256:6b3136eecf7e1b4a4d23e4b19d6c4e7a8e0b42d55f30444e3c529700cdacaa0d", + "sha256:7118ca592d25b2957ff7b662bc0fe4f4c2b5d5b27814b9b1bc9f2fb249a970e7", + "sha256:71af5b43e4fa286a35110fc5bb740fdeae2b36ca79fbcf0a54237485baeee8be", + "sha256:747190fcdadc377263223f8f72b038381b3b549a8a3df5baf4d067da4749b046", + "sha256:8395064d63b26947fa2c9faeea9c3eee35e52148c5339c37987e1d96fbf009b3", + "sha256:84f85adfebd7d3c3db649efdf73659e1677a2cf3fa6e2556a3f373578af14bf7", + "sha256:86bcf0009f2012847a688f2f4f9b16203ca4c835979a02549aa0595d9f457cc8", + "sha256:ab8a15c2750ae8d53e31f77a94f846d0a00772240f1c12817411fa2344351f86", + "sha256:af24b21c2283ca69c416a8a42cde9764dc36c63d3389645d28c69b0e93db3cd7", + "sha256:afe173ac2646c2636305ab820cc0380b22a00a7bca4290452e7166b4f4fa49d0", + "sha256:b9eb88593159a53a5ee0b0159daee531ff7dd9c87fa78f5d807ca059c7eb1b2b", + "sha256:c16635f8dddbeb1b827977d0b00d07b644b040aeb9ff8607a9fc0997afa3e567", + "sha256:ca3eefb02ef17257fae8b8555c85e7c1efdfd777f671384b0e4ef27409b02720", + "sha256:caa59e2cae0e23b1e225447d7a9ddb0f982f42a6a22d497a484dfe62a06f7c0e", + "sha256:cb55258931448d61e2d50187de4ee66fc9d9f34908b524949b8b2b93d0c57136", + "sha256:d248c46c0aa406695bda2abf99632db991f8b3a6d46018721a2892312a99f069", + "sha256:d2578e5149ff49878934debfacf5c743fab49eca5ecdb983d0b218e1e554c498", + "sha256:dd22085446f3eca990d12a0878eeb5199dc9553b2e71716bfe7bed9915a472ab", + "sha256:e7cf940af5fee00a92e281eb157abe8770227a5255207818ea9a34e54a29f5b2", + "sha256:f70f3d028794e31cf9d1a822914efc935aadb2438ec4e8d4871d95eb1ce032d6", + "sha256:fd2842a0faed9ab9aba0922c951906132d9384be89690570f0ed18cd4f20e658", + "sha256:fd628e63ffdba0112e3ddf1b1e9f3db29dd8262345138e08f4938acbc6d0805a", + "sha256:ffd7cf165ff71afb202b3f36daafbf298932bee325aac9f58e1c9cd55838bef0" + ], + "markers": "python_version >= '3.7'", + "version": "==1.4.3" + }, "markupsafe": { "hashes": [ "sha256:0212a68688482dc52b2d45013df70d169f542b7394fc744c02a57374a4207003", @@ -418,6 +468,47 @@ "markers": "python_version >= '3.7'", "version": "==2.1.1" }, + "matplotlib": { + "hashes": [ + "sha256:03bbb3f5f78836855e127b5dab228d99551ad0642918ccbf3067fcd52ac7ac5e", + "sha256:24173c23d1bcbaed5bf47b8785d27933a1ac26a5d772200a0f3e0e38f471b001", + "sha256:2a0967d4156adbd0d46db06bc1a877f0370bce28d10206a5071f9ecd6dc60b79", + "sha256:2e8bda1088b941ead50caabd682601bece983cadb2283cafff56e8fcddbf7d7f", + "sha256:31fbc2af27ebb820763f077ec7adc79b5a031c2f3f7af446bd7909674cd59460", + "sha256:364e6bca34edc10a96aa3b1d7cd76eb2eea19a4097198c1b19e89bee47ed5781", + "sha256:3d8e129af95b156b41cb3be0d9a7512cc6d73e2b2109f82108f566dbabdbf377", + "sha256:44c6436868186564450df8fd2fc20ed9daaef5caad699aa04069e87099f9b5a8", + "sha256:48cf850ce14fa18067f2d9e0d646763681948487a8080ec0af2686468b4607a2", + "sha256:49a5938ed6ef9dda560f26ea930a2baae11ea99e1c2080c8714341ecfda72a89", + "sha256:4a05f2b37222319753a5d43c0a4fd97ed4ff15ab502113e3f2625c26728040cf", + "sha256:4a44cdfdb9d1b2f18b1e7d315eb3843abb097869cd1ef89cfce6a488cd1b5182", + "sha256:4fa28ca76ac5c2b2d54bc058b3dad8e22ee85d26d1ee1b116a6fd4d2277b6a04", + "sha256:5844cea45d804174bf0fac219b4ab50774e504bef477fc10f8f730ce2d623441", + "sha256:5a32ea6e12e80dedaca2d4795d9ed40f97bfa56e6011e14f31502fdd528b9c89", + "sha256:6c623b355d605a81c661546af7f24414165a8a2022cddbe7380a31a4170fa2e9", + "sha256:751d3815b555dcd6187ad35b21736dc12ce6925fc3fa363bbc6dc0f86f16484f", + "sha256:75c406c527a3aa07638689586343f4b344fcc7ab1f79c396699eb550cd2b91f7", + "sha256:77157be0fc4469cbfb901270c205e7d8adb3607af23cef8bd11419600647ceed", + "sha256:7d7705022df2c42bb02937a2a824f4ec3cca915700dd80dc23916af47ff05f1a", + "sha256:7f409716119fa39b03da3d9602bd9b41142fab7a0568758cd136cd80b1bf36c8", + "sha256:9480842d5aadb6e754f0b8f4ebeb73065ac8be1855baa93cd082e46e770591e9", + "sha256:9776e1a10636ee5f06ca8efe0122c6de57ffe7e8c843e0fb6e001e9d9256ec95", + "sha256:a91426ae910819383d337ba0dc7971c7cefdaa38599868476d94389a329e599b", + "sha256:b4fedaa5a9aa9ce14001541812849ed1713112651295fdddd640ea6620e6cf98", + "sha256:b6c63cd01cad0ea8704f1fd586e9dc5777ccedcd42f63cbbaa3eae8dd41172a1", + "sha256:b8d3f4e71e26307e8c120b72c16671d70c5cd08ae412355c11254aa8254fb87f", + "sha256:c4b82c2ae6d305fcbeb0eb9c93df2602ebd2f174f6e8c8a5d92f9445baa0c1d3", + "sha256:c772264631e5ae61f0bd41313bbe48e1b9bcc95b974033e1118c9caa1a84d5c6", + "sha256:c87973ddec10812bddc6c286b88fdd654a666080fbe846a1f7a3b4ba7b11ab78", + "sha256:e2b696699386766ef171a259d72b203a3c75d99d03ec383b97fc2054f52e15cf", + "sha256:ea75df8e567743207e2b479ba3d8843537be1c146d4b1e3e395319a4e1a77fe9", + "sha256:ebc27ad11df3c1661f4677a7762e57a8a91dd41b466c3605e90717c9a5f90c82", + "sha256:ee0b8e586ac07f83bb2950717e66cb305e2859baf6f00a9c39cc576e0ce9629c", + "sha256:ee175a571e692fc8ae8e41ac353c0e07259113f4cb063b0ec769eff9717e84bb" + ], + "index": "pypi", + "version": "==3.5.2" + }, "matplotlib-inline": { "hashes": [ "sha256:a04bfba22e0d1395479f866853ec1ee28eea1485c1d69a6faf00dc3e24ff34ee", @@ -435,11 +526,11 @@ }, "nbclient": { "hashes": [ - "sha256:2eed35fc954716cdf0a01ea8cbdd9f9316761479008570059e2f5de29e139423", - "sha256:3f89a403c6badf24d2855a455b69a80985b3b27e04111243fdb6a88a28d27031" + "sha256:cdef7757cead1735d2c70cc66095b072dced8a1e6d1c7639ef90cd3e04a11f2e", + "sha256:f251bba200a2b401a061dfd700a7a70b5772f664fb49d4a2d3e5536ec0e98c76" ], "markers": "python_version >= '3.7'", - "version": "==0.6.0" + "version": "==0.6.4" }, "nbconvert": { "hashes": [ @@ -451,11 +542,11 @@ }, "nbformat": { "hashes": [ - "sha256:38856d97de49e8292e2d5d8f595e9d26f02abfd87e075d450af4511870b40538", - "sha256:fcc5ab8cb74e20b19570b5be809e2dba9b82836fd2761a89066ad43394ba29f5" + "sha256:0d6072aaec95dddc39735c144ee8bbc6589c383fb462e4058abc855348152dad", + "sha256:44ba5ca6acb80c5d5a500f1e5b83ede8cbe364d5a495c4c8cf60aaf1ba656501" ], "markers": "python_version >= '3.7'", - "version": "==5.3.0" + "version": "==5.4.0" }, "nest-asyncio": { "hashes": [ @@ -467,37 +558,39 @@ }, "notebook": { "hashes": [ - "sha256:709b1856a564fe53054796c80e17a67262071c86bfbdfa6b96aaa346113c555a", - "sha256:b4a6baf2eba21ce67a0ca11a793d1781b06b8078f34d06c710742e55f3eee505" + "sha256:6268c9ec9048cff7a45405c990c29ac9ca40b0bc3ec29263d218c5e01f2b4e86", + "sha256:8c07a3bb7640e371f8a609bdbb2366a1976c6a2589da8ef917f761a61e3ad8b1" ], "markers": "python_version >= '3.7'", - "version": "==6.4.11" + "version": "==6.4.12" }, "numpy": { "hashes": [ - "sha256:07a8c89a04997625236c5ecb7afe35a02af3896c8aa01890a849913a2309c676", - "sha256:08d9b008d0156c70dc392bb3ab3abb6e7a711383c3247b410b39962263576cd4", - "sha256:201b4d0552831f7250a08d3b38de0d989d6f6e4658b709a02a73c524ccc6ffce", - "sha256:2c10a93606e0b4b95c9b04b77dc349b398fdfbda382d2a39ba5a822f669a0123", - "sha256:3ca688e1b9b95d80250bca34b11a05e389b1420d00e87a0d12dc45f131f704a1", - "sha256:48a3aecd3b997bf452a2dedb11f4e79bc5bfd21a1d4cc760e703c31d57c84b3e", - "sha256:568dfd16224abddafb1cbcce2ff14f522abe037268514dd7e42c6776a1c3f8e5", - "sha256:5bfb1bb598e8229c2d5d48db1860bcf4311337864ea3efdbe1171fb0c5da515d", - "sha256:639b54cdf6aa4f82fe37ebf70401bbb74b8508fddcf4797f9fe59615b8c5813a", - "sha256:8251ed96f38b47b4295b1ae51631de7ffa8260b5b087808ef09a39a9d66c97ab", - "sha256:92bfa69cfbdf7dfc3040978ad09a48091143cffb778ec3b03fa170c494118d75", - "sha256:97098b95aa4e418529099c26558eeb8486e66bd1e53a6b606d684d0c3616b168", - "sha256:a3bae1a2ed00e90b3ba5f7bd0a7c7999b55d609e0c54ceb2b076a25e345fa9f4", - "sha256:c34ea7e9d13a70bf2ab64a2532fe149a9aced424cd05a2c4ba662fd989e3e45f", - "sha256:dbc7601a3b7472d559dc7b933b18b4b66f9aa7452c120e87dfb33d02008c8a18", - "sha256:e7927a589df200c5e23c57970bafbd0cd322459aa7b1ff73b7c2e84d6e3eae62", - "sha256:f8c1f39caad2c896bc0018f699882b345b2a63708008be29b1f355ebf6f933fe", - "sha256:f950f8845b480cffe522913d35567e29dd381b0dc7e4ce6a4a9f9156417d2430", - "sha256:fade0d4f4d292b6f39951b6836d7a3c7ef5b2347f3c420cd9820a1d90d794802", - "sha256:fdf3c08bce27132395d3c3ba1503cac12e17282358cb4bddc25cc46b0aca07aa" + "sha256:0791fbd1e43bf74b3502133207e378901272f3c156c4df4954cad833b1380207", + "sha256:1ce7ab2053e36c0a71e7a13a7475bd3b1f54750b4b433adc96313e127b870887", + "sha256:2d487e06ecbf1dc2f18e7efce82ded4f705f4bd0cd02677ffccfb39e5c284c7e", + "sha256:37431a77ceb9307c28382c9773da9f306435135fae6b80b62a11c53cfedd8802", + "sha256:3e1ffa4748168e1cc8d3cde93f006fe92b5421396221a02f2274aab6ac83b077", + "sha256:425b390e4619f58d8526b3dcf656dde069133ae5c240229821f01b5f44ea07af", + "sha256:43a8ca7391b626b4c4fe20aefe79fec683279e31e7c79716863b4b25021e0e74", + "sha256:4c6036521f11a731ce0648f10c18ae66d7143865f19f7299943c985cdc95afb5", + "sha256:59d55e634968b8f77d3fd674a3cf0b96e85147cd6556ec64ade018f27e9479e1", + "sha256:64f56fc53a2d18b1924abd15745e30d82a5782b2cab3429aceecc6875bd5add0", + "sha256:7228ad13744f63575b3a972d7ee4fd61815b2879998e70930d4ccf9ec721dce0", + "sha256:9ce7df0abeabe7fbd8ccbf343dc0db72f68549856b863ae3dd580255d009648e", + "sha256:a911e317e8c826ea632205e63ed8507e0dc877dcdc49744584dfc363df9ca08c", + "sha256:b89bf9b94b3d624e7bb480344e91f68c1c6c75f026ed6755955117de00917a7c", + "sha256:ba9ead61dfb5d971d77b6c131a9dbee62294a932bf6a356e48c75ae684e635b3", + "sha256:c1d937820db6e43bec43e8d016b9b3165dcb42892ea9f106c70fb13d430ffe72", + "sha256:cc7f00008eb7d3f2489fca6f334ec19ca63e31371be28fd5dad955b16ec285bd", + "sha256:d4c5d5eb2ec8da0b4f50c9a843393971f31f1d60be87e0fb0917a49133d257d6", + "sha256:e96d7f3096a36c8754207ab89d4b3282ba7b49ea140e4973591852c77d09eb76", + "sha256:f0725df166cf4785c0bc4cbfb320203182b1ecd30fee6e541c8752a92df6aa32", + "sha256:f3eb268dbd5cfaffd9448113539e44e2dd1c5ca9ce25576f7c04a5453edc26fa", + "sha256:fb7a980c81dd932381f8228a426df8aeb70d59bbcda2af075b627bbc50207cba" ], "index": "pypi", - "version": "==1.22.3" + "version": "==1.22.4" }, "packaging": { "hashes": [ @@ -530,6 +623,57 @@ ], "version": "==0.7.5" }, + "pillow": { + "hashes": [ + "sha256:088df396b047477dd1bbc7de6e22f58400dae2f21310d9e2ec2933b2ef7dfa4f", + "sha256:09e67ef6e430f90caa093528bd758b0616f8165e57ed8d8ce014ae32df6a831d", + "sha256:0b4d5ad2cd3a1f0d1df882d926b37dbb2ab6c823ae21d041b46910c8f8cd844b", + "sha256:0b525a356680022b0af53385944026d3486fc8c013638cf9900eb87c866afb4c", + "sha256:1d4331aeb12f6b3791911a6da82de72257a99ad99726ed6b63f481c0184b6fb9", + "sha256:20d514c989fa28e73a5adbddd7a171afa5824710d0ab06d4e1234195d2a2e546", + "sha256:2b291cab8a888658d72b575a03e340509b6b050b62db1f5539dd5cd18fd50578", + "sha256:3f6c1716c473ebd1649663bf3b42702d0d53e27af8b64642be0dd3598c761fb1", + "sha256:42dfefbef90eb67c10c45a73a9bc1599d4dac920f7dfcbf4ec6b80cb620757fe", + "sha256:488f3383cf5159907d48d32957ac6f9ea85ccdcc296c14eca1a4e396ecc32098", + "sha256:4d45dbe4b21a9679c3e8b3f7f4f42a45a7d3ddff8a4a16109dff0e1da30a35b2", + "sha256:53c27bd452e0f1bc4bfed07ceb235663a1df7c74df08e37fd6b03eb89454946a", + "sha256:55e74faf8359ddda43fee01bffbc5bd99d96ea508d8a08c527099e84eb708f45", + "sha256:59789a7d06c742e9d13b883d5e3569188c16acb02eeed2510fd3bfdbc1bd1530", + "sha256:5b650dbbc0969a4e226d98a0b440c2f07a850896aed9266b6fedc0f7e7834108", + "sha256:66daa16952d5bf0c9d5389c5e9df562922a59bd16d77e2a276e575d32e38afd1", + "sha256:6e760cf01259a1c0a50f3c845f9cad1af30577fd8b670339b1659c6d0e7a41dd", + "sha256:7502539939b53d7565f3d11d87c78e7ec900d3c72945d4ee0e2f250d598309a0", + "sha256:769a7f131a2f43752455cc72f9f7a093c3ff3856bf976c5fb53a59d0ccc704f6", + "sha256:7c150dbbb4a94ea4825d1e5f2c5501af7141ea95825fadd7829f9b11c97aaf6c", + "sha256:8844217cdf66eabe39567118f229e275f0727e9195635a15e0e4b9227458daaf", + "sha256:8a66fe50386162df2da701b3722781cbe90ce043e7d53c1fd6bd801bca6b48d4", + "sha256:9370d6744d379f2de5d7fa95cdbd3a4d92f0b0ef29609b4b1687f16bc197063d", + "sha256:937a54e5694684f74dcbf6e24cc453bfc5b33940216ddd8f4cd8f0f79167f765", + "sha256:9c857532c719fb30fafabd2371ce9b7031812ff3889d75273827633bca0c4602", + "sha256:a4165205a13b16a29e1ac57efeee6be2dfd5b5408122d59ef2145bc3239fa340", + "sha256:b3fe2ff1e1715d4475d7e2c3e8dabd7c025f4410f79513b4ff2de3d51ce0fa9c", + "sha256:b6617221ff08fbd3b7a811950b5c3f9367f6e941b86259843eab77c8e3d2b56b", + "sha256:b761727ed7d593e49671d1827044b942dd2f4caae6e51bab144d4accf8244a84", + "sha256:baf3be0b9446a4083cc0c5bb9f9c964034be5374b5bc09757be89f5d2fa247b8", + "sha256:c17770a62a71718a74b7548098a74cd6880be16bcfff5f937f900ead90ca8e92", + "sha256:c67db410508b9de9c4694c57ed754b65a460e4812126e87f5052ecf23a011a54", + "sha256:d78ca526a559fb84faaaf84da2dd4addef5edb109db8b81677c0bb1aad342601", + "sha256:e9ed59d1b6ee837f4515b9584f3d26cf0388b742a11ecdae0d9237a94505d03a", + "sha256:f054b020c4d7e9786ae0404278ea318768eb123403b18453e28e47cdb7a0a4bf", + "sha256:f372d0f08eff1475ef426344efe42493f71f377ec52237bf153c5713de987251", + "sha256:f3f6a6034140e9e17e9abc175fc7a266a6e63652028e157750bd98e804a8ed9a", + "sha256:ffde4c6fabb52891d81606411cbfaf77756e3b561b566efd270b3ed3791fde4e" + ], + "markers": "python_version >= '3.7'", + "version": "==9.1.1" + }, + "pipfile": { + "hashes": [ + "sha256:f7d9f15de8b660986557eb3cc5391aa1a16207ac41bc378d03f414762d36c984" + ], + "index": "pypi", + "version": "==0.0.2" + }, "prometheus-client": { "hashes": [ "sha256:522fded625282822a89e2773452f42df14b5a8e84a86433e3f8a189c1d54dc01", @@ -548,41 +692,41 @@ }, "psutil": { "hashes": [ - "sha256:072664401ae6e7c1bfb878c65d7282d4b4391f1bc9a56d5e03b5a490403271b5", - "sha256:1070a9b287846a21a5d572d6dddd369517510b68710fca56b0e9e02fd24bed9a", - "sha256:1d7b433519b9a38192dfda962dd8f44446668c009833e1429a52424624f408b4", - "sha256:3151a58f0fbd8942ba94f7c31c7e6b310d2989f4da74fcbf28b934374e9bf841", - "sha256:32acf55cb9a8cbfb29167cd005951df81b567099295291bcfd1027365b36591d", - "sha256:3611e87eea393f779a35b192b46a164b1d01167c9d323dda9b1e527ea69d697d", - "sha256:3d00a664e31921009a84367266b35ba0aac04a2a6cad09c550a89041034d19a0", - "sha256:4e2fb92e3aeae3ec3b7b66c528981fd327fb93fd906a77215200404444ec1845", - "sha256:539e429da49c5d27d5a58e3563886057f8fc3868a5547b4f1876d9c0f007bccf", - "sha256:55ce319452e3d139e25d6c3f85a1acf12d1607ddedea5e35fb47a552c051161b", - "sha256:58c7d923dc209225600aec73aa2c4ae8ea33b1ab31bc11ef8a5933b027476f07", - "sha256:7336292a13a80eb93c21f36bde4328aa748a04b68c13d01dfddd67fc13fd0618", - "sha256:742c34fff804f34f62659279ed5c5b723bb0195e9d7bd9907591de9f8f6558e2", - "sha256:7641300de73e4909e5d148e90cc3142fb890079e1525a840cf0dfd39195239fd", - "sha256:76cebf84aac1d6da5b63df11fe0d377b46b7b500d892284068bacccf12f20666", - "sha256:7779be4025c540d1d65a2de3f30caeacc49ae7a2152108adeaf42c7534a115ce", - "sha256:7d190ee2eaef7831163f254dc58f6d2e2a22e27382b936aab51c835fc080c3d3", - "sha256:8293942e4ce0c5689821f65ce6522ce4786d02af57f13c0195b40e1edb1db61d", - "sha256:869842dbd66bb80c3217158e629d6fceaecc3a3166d3d1faee515b05dd26ca25", - "sha256:90a58b9fcae2dbfe4ba852b57bd4a1dded6b990a33d6428c7614b7d48eccb492", - "sha256:9b51917c1af3fa35a3f2dabd7ba96a2a4f19df3dec911da73875e1edaf22a40b", - "sha256:b2237f35c4bbae932ee98902a08050a27821f8f6dfa880a47195e5993af4702d", - "sha256:c3400cae15bdb449d518545cbd5b649117de54e3596ded84aacabfbb3297ead2", - "sha256:c51f1af02334e4b516ec221ee26b8fdf105032418ca5a5ab9737e8c87dafe203", - "sha256:cb8d10461c1ceee0c25a64f2dd54872b70b89c26419e147a05a10b753ad36ec2", - "sha256:d62a2796e08dd024b8179bd441cb714e0f81226c352c802fca0fd3f89eeacd94", - "sha256:df2c8bd48fb83a8408c8390b143c6a6fa10cb1a674ca664954de193fdcab36a9", - "sha256:e5c783d0b1ad6ca8a5d3e7b680468c9c926b804be83a3a8e95141b05c39c9f64", - "sha256:e9805fed4f2a81de98ae5fe38b75a74c6e6ad2df8a5c479594c7629a1fe35f56", - "sha256:ea42d747c5f71b5ccaa6897b216a7dadb9f52c72a0fe2b872ef7d3e1eacf3ba3", - "sha256:ef216cc9feb60634bda2f341a9559ac594e2eeaadd0ba187a4c2eb5b5d40b91c", - "sha256:ff0d41f8b3e9ebb6b6110057e40019a432e96aae2008951121ba4e56040b84f3" + "sha256:068935df39055bf27a29824b95c801c7a5130f118b806eee663cad28dca97685", + "sha256:0904727e0b0a038830b019551cf3204dd48ef5c6868adc776e06e93d615fc5fc", + "sha256:0f15a19a05f39a09327345bc279c1ba4a8cfb0172cc0d3c7f7d16c813b2e7d36", + "sha256:19f36c16012ba9cfc742604df189f2f28d2720e23ff7d1e81602dbe066be9fd1", + "sha256:20b27771b077dcaa0de1de3ad52d22538fe101f9946d6dc7869e6f694f079329", + "sha256:28976df6c64ddd6320d281128817f32c29b539a52bdae5e192537bc338a9ec81", + "sha256:29a442e25fab1f4d05e2655bb1b8ab6887981838d22effa2396d584b740194de", + "sha256:3054e923204b8e9c23a55b23b6df73a8089ae1d075cb0bf711d3e9da1724ded4", + "sha256:32c52611756096ae91f5d1499fe6c53b86f4a9ada147ee42db4991ba1520e574", + "sha256:3a76ad658641172d9c6e593de6fe248ddde825b5866464c3b2ee26c35da9d237", + "sha256:44d1826150d49ffd62035785a9e2c56afcea66e55b43b8b630d7706276e87f22", + "sha256:4b6750a73a9c4a4e689490ccb862d53c7b976a2a35c4e1846d049dcc3f17d83b", + "sha256:56960b9e8edcca1456f8c86a196f0c3d8e3e361320071c93378d41445ffd28b0", + "sha256:57f1819b5d9e95cdfb0c881a8a5b7d542ed0b7c522d575706a80bedc848c8954", + "sha256:58678bbadae12e0db55186dc58f2888839228ac9f41cc7848853539b70490021", + "sha256:645bd4f7bb5b8633803e0b6746ff1628724668681a434482546887d22c7a9537", + "sha256:799759d809c31aab5fe4579e50addf84565e71c1dc9f1c31258f159ff70d3f87", + "sha256:79c9108d9aa7fa6fba6e668b61b82facc067a6b81517cab34d07a84aa89f3df0", + "sha256:91c7ff2a40c373d0cc9121d54bc5f31c4fa09c346528e6a08d1845bce5771ffc", + "sha256:9272167b5f5fbfe16945be3db475b3ce8d792386907e673a209da686176552af", + "sha256:944c4b4b82dc4a1b805329c980f270f170fdc9945464223f2ec8e57563139cf4", + "sha256:a6a11e48cb93a5fa606306493f439b4aa7c56cb03fc9ace7f6bfa21aaf07c453", + "sha256:a8746bfe4e8f659528c5c7e9af5090c5a7d252f32b2e859c584ef7d8efb1e689", + "sha256:abd9246e4cdd5b554a2ddd97c157e292ac11ef3e7af25ac56b08b455c829dca8", + "sha256:b14ee12da9338f5e5b3a3ef7ca58b3cba30f5b66f7662159762932e6d0b8f680", + "sha256:b88f75005586131276634027f4219d06e0561292be8bd6bc7f2f00bdabd63c4e", + "sha256:c7be9d7f5b0d206f0bbc3794b8e16fb7dbc53ec9e40bbe8787c6f2d38efcf6c9", + "sha256:d2d006286fbcb60f0b391741f520862e9b69f4019b4d738a2a45728c7e952f1b", + "sha256:db417f0865f90bdc07fa30e1aadc69b6f4cad7f86324b02aa842034efe8d8c4d", + "sha256:e7e10454cb1ab62cc6ce776e1c135a64045a11ec4c6d254d3f7689c16eb3efd2", + "sha256:f65f9a46d984b8cd9b3750c2bdb419b2996895b005aefa6cbaba9a143b1ce2c5", + "sha256:fea896b54f3a4ae6f790ac1d017101252c93f6fe075d0e7571543510f11d2676" ], - "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==5.9.0" + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==5.9.1" }, "pure-eval": { "hashes": [ @@ -608,11 +752,11 @@ }, "pyparsing": { "hashes": [ - "sha256:7bf433498c016c4314268d95df76c81b842a4cb2b276fa3312cfb1e1d85f6954", - "sha256:ef7b523f6356f763771559412c0d7134753f037822dad1b16945b7b846f7ad06" + "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb", + "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc" ], "markers": "python_full_version >= '3.6.8'", - "version": "==3.0.8" + "version": "==3.0.9" }, "pyrsistent": { "hashes": [ @@ -682,72 +826,111 @@ }, "pyzmq": { "hashes": [ - "sha256:08c4e315a76ef26eb833511ebf3fa87d182152adf43dedee8d79f998a2162a0b", - "sha256:0ca6cd58f62a2751728016d40082008d3b3412a7f28ddfb4a2f0d3c130f69e74", - "sha256:1621e7a2af72cced1f6ec8ca8ca91d0f76ac236ab2e8828ac8fe909512d566cb", - "sha256:18cd854b423fce44951c3a4d3e686bac8f1243d954f579e120a1714096637cc0", - "sha256:2841997a0d85b998cbafecb4183caf51fd19c4357075dfd33eb7efea57e4c149", - "sha256:2b97502c16a5ec611cd52410bdfaab264997c627a46b0f98d3f666227fd1ea2d", - "sha256:3a4c9886d61d386b2b493377d980f502186cd71d501fffdba52bd2a0880cef4f", - "sha256:3c1895c95be92600233e476fe283f042e71cf8f0b938aabf21b7aafa62a8dac9", - "sha256:42abddebe2c6a35180ca549fadc7228d23c1e1f76167c5ebc8a936b5804ea2df", - "sha256:468bd59a588e276961a918a3060948ae68f6ff5a7fa10bb2f9160c18fe341067", - "sha256:480b9931bfb08bf8b094edd4836271d4d6b44150da051547d8c7113bf947a8b0", - "sha256:53f4fd13976789ffafedd4d46f954c7bb01146121812b72b4ddca286034df966", - "sha256:62bcade20813796c426409a3e7423862d50ff0639f5a2a95be4b85b09a618666", - "sha256:67db33bea0a29d03e6eeec55a8190e033318cee3cbc732ba8fd939617cbf762d", - "sha256:6b217b8f9dfb6628f74b94bdaf9f7408708cb02167d644edca33f38746ca12dd", - "sha256:7661fc1d5cb73481cf710a1418a4e1e301ed7d5d924f91c67ba84b2a1b89defd", - "sha256:76c532fd68b93998aab92356be280deec5de8f8fe59cd28763d2cc8a58747b7f", - "sha256:79244b9e97948eaf38695f4b8e6fc63b14b78cc37f403c6642ba555517ac1268", - "sha256:7c58f598d9fcc52772b89a92d72bf8829c12d09746a6d2c724c5b30076c1f11d", - "sha256:7dc09198e4073e6015d9a8ea093fc348d4e59de49382476940c3dd9ae156fba8", - "sha256:80e043a89c6cadefd3a0712f8a1322038e819ebe9dbac7eca3bce1721bcb63bf", - "sha256:851977788b9caa8ed011f5f643d3ee8653af02c5fc723fa350db5125abf2be7b", - "sha256:8eddc033e716f8c91c6a2112f0a8ebc5e00532b4a6ae1eb0ccc48e027f9c671c", - "sha256:902319cfe23366595d3fa769b5b751e6ee6750a0a64c5d9f757d624b2ac3519e", - "sha256:954e73c9cd4d6ae319f1c936ad159072b6d356a92dcbbabfd6e6204b9a79d356", - "sha256:ab888624ed68930442a3f3b0b921ad7439c51ba122dbc8c386e6487a658e4a4e", - "sha256:acebba1a23fb9d72b42471c3771b6f2f18dcd46df77482612054bd45c07dfa36", - "sha256:b4ebed0977f92320f6686c96e9e8dd29eed199eb8d066936bac991afc37cbb70", - "sha256:badb868fff14cfd0e200eaa845887b1011146a7d26d579aaa7f966c203736b92", - "sha256:be4e0f229cf3a71f9ecd633566bd6f80d9fa6afaaff5489492be63fe459ef98c", - "sha256:c0f84360dcca3481e8674393bdf931f9f10470988f87311b19d23cda869bb6b7", - "sha256:c1e41b32d6f7f9c26bc731a8b529ff592f31fc8b6ef2be9fa74abd05c8a342d7", - "sha256:c88fa7410e9fc471e0858638f403739ee869924dd8e4ae26748496466e27ac59", - "sha256:cf98fd7a6c8aaa08dbc699ffae33fd71175696d78028281bc7b832b26f00ca57", - "sha256:d072f7dfbdb184f0786d63bda26e8a0882041b1e393fbe98940395f7fab4c5e2", - "sha256:d1b5d457acbadcf8b27561deeaa386b0217f47626b29672fa7bd31deb6e91e1b", - "sha256:d3dcb5548ead4f1123851a5ced467791f6986d68c656bc63bfff1bf9e36671e2", - "sha256:d6157793719de168b199194f6b6173f0ccd3bf3499e6870fac17086072e39115", - "sha256:d728b08448e5ac3e4d886b165385a262883c34b84a7fe1166277fe675e1c197a", - "sha256:de8df0684398bd74ad160afdc2a118ca28384ac6f5e234eb0508858d8d2d9364", - "sha256:e6a02cf7271ee94674a44f4e62aa061d2d049001c844657740e156596298b70b", - "sha256:ea12133df25e3a6918718fbb9a510c6ee5d3fdd5a346320421aac3882f4feeea", - "sha256:ea5a79e808baef98c48c884effce05c31a0698c1057de8fc1c688891043c1ce1", - "sha256:f43b4a2e6218371dd4f41e547bd919ceeb6ebf4abf31a7a0669cd11cd91ea973", - "sha256:f762442bab706fd874064ca218b33a1d8e40d4938e96c24dafd9b12e28017f45", - "sha256:f89468059ebc519a7acde1ee50b779019535db8dcf9b8c162ef669257fef7a93", - "sha256:f907c7359ce8bf7f7e63c82f75ad0223384105f5126f313400b7e8004d9b33c3" + "sha256:057176dd3f5ccf5aad4abd662d76b6a39bbf799baaf2f39cd4fdaf2eab326e43", + "sha256:05ec90a8da618f2398f9d1aa20b18a9ef332992c6ac23e8c866099faad6ef0d6", + "sha256:154de02b15422af28b53d29a02de72121ba503634955017255573fc1f995143d", + "sha256:16b832adb5d8716f46051da5533c480250bf126984ce86804db6137a3a7f931b", + "sha256:1df26aa854bdd3a8341bf199064dd6aa6e240f2eaa3c9fa8d217e5d8b868c73e", + "sha256:28f9164fb2658b7b414fa0894c75b1a9c61375774cdc1bdb7298beb042a2cd87", + "sha256:2951c29b8649f3672af9dca8ff61d86310d3664d9629788b1c66422fb13b1239", + "sha256:2b08774057ae7ce8a2eb4e7d54db05358234440706ce43a85814500c5d7bd22e", + "sha256:2e2ac40f7a91c740ec68d6db07ae19ea9259c959333c68bee56ab2c799a67d66", + "sha256:312e56799410c34797417a4060a8bd37d4db1f06d1ec0c54f7c8fd81e0d90376", + "sha256:38f778a74e3889392e949326cfd0e9b2eb37dcbb2980d98fad2c51703d523db2", + "sha256:3955dd5bbbe02f454655296ee36a66c334c7102a29b8458223d168c0380edfd5", + "sha256:425ba851a6f9892bde1da2024d82e2fe6796bd77e3391fb96665c50fe9d4c6a5", + "sha256:48bbc2db041ab28eeee4a3e8ada0ed336640946dd5a8e53dbd3805f9dbdcf0dc", + "sha256:4fbcd657cda75574fd1315a4c44bd322bc2e219039fb09f146bbe6f8aef039e9", + "sha256:523ba7fd4d8fe75ad09c1e574a648892b75a97d0cfc8005727681053ac19555b", + "sha256:53b2c1326c2e484d450932d2be739f064b7cb572faabec38386098a28516a529", + "sha256:540d7146c3cdc9bbffab039ea067f494eba24d1abe5bd33eb9f963c01e3305d4", + "sha256:563d4281c4dbdf647d93114420151d33f895afc4c46b7115a67a0aa5347e6624", + "sha256:67a049bcf967a39993858beed873ed3405536019820922d4efacfe35ab3da51a", + "sha256:67ec63ae3c9c1fa2e077fcb42e77035e2121a04f987464bdf9945a28535d30ad", + "sha256:68e22c5d3be451e87d47f956b397a7823bfbde2176341bc902fba30f96831d7e", + "sha256:6ab4b6108e69f63c917cd7ef7217c5727955b1ac90600e44a13ed5312019a014", + "sha256:6bd7f18bd4cf51ea8d7e54825902cf36f9d2f35cc51ef618373988d5398b8dd0", + "sha256:6cd53e861bccc0bdc4620f68fb4a91d5bcfe9f4213cf8e200fa498044d33a6dc", + "sha256:6d346e551fa64b89d57a4ac74b9bc66703413f02f50093e089e861999ec5cccc", + "sha256:6ff8708fabc9f9bc2949f457d39b4088c9656c4c9ac15fbbbbaafce8f6d07833", + "sha256:7626e8384275a7dea6f3d1f749fb5e00299042e9c895fc3dbe24cb154909c242", + "sha256:7e7346b2b33dcd4a2171dd8a9870ae283eec8f6231dcbcf237a0f41e74751a50", + "sha256:81623c67cb71b93b5f7e06c9107f3781738ae86866db830c950223d87af2a235", + "sha256:83f1c76068faf62c32a36dd62dc4db642c2027bbbd960f8f6345b59e9d4dc472", + "sha256:8679bb1dd723ecbea03b1f96c98972815775fd8ec756c440a14f289c436c472e", + "sha256:86fb683cb9a9c0bb7476988b7957393ecdd22777d87d804442c66e62c99197f9", + "sha256:8757c62f7960cd26122f7aaaf86eda1e016fa85734c3777b8054dd334d7dea4d", + "sha256:894be7d17228e7328cc188096c0162697211ec91761f6812fff12790cbe11c66", + "sha256:8a0f240bf43c29be1bd82d77e602a61c798e9de02e5f8bb7bb414cb814f43236", + "sha256:8c3abf7eab5b76ae162c4fbb16d514a947fc57fd995b64e5ea8ef8ba3b888a69", + "sha256:93332c6972e4c91522c4810e907f3aea067424338071161b39cacded022559df", + "sha256:97d6c676dc97d593625d9fc48154f2ffeabb619a1e6fe8d2a5b53f97e3e9bdee", + "sha256:99dd85f0ca1db8d17a01a25c2bbb7784d25a2d39497c6beddbe96bff74194e04", + "sha256:9c7fb691fb07ec7ab99fd173bb0e7e0248d31bf83d484a87b917a342f63812c9", + "sha256:b3bc3cf200aab74f3d758586ac50295214eda496ac6a6636e0c881c5958d9123", + "sha256:bba54f97578943f48f621b4a7afb8eb022370da26a88b88ccc9fee9f3ef7ce45", + "sha256:bd2a13a0f8367e50347cbac87ae230ae1953935443240238f956bf10668bead6", + "sha256:cbc1184349ca6e5112898aa7fc3efa1b1bbae24ab1edc774cfd09cbfd3b091d7", + "sha256:cd82cca9c489e441574804dbda2dd8e114cf3be7935b03de11dade2c9478aea6", + "sha256:ce8ba5ed8b0a7a203922d61cff45ee6001a41a9359f04f00d055a4e988755569", + "sha256:cfee22e072a382b92ee0709dbb8203dabd52d54258051e770d9d2a81b162530b", + "sha256:d977df6f7c4109ed1d96ffb6795f6af77114be606ae4556efbfc9cac725db65d", + "sha256:da72a384a1d7e87490ca71182f3ab469ed21d847adc16b70c34faac5a3b12801", + "sha256:ddf4ad1d651e6c9234945061e1a31fe27a4be0dea21c498b87b186fadf8f5919", + "sha256:eb0ae5dfda83bbce660179d7b41c1c38fd833a54d2e6d9b258c644f3b75ef94d", + "sha256:f4c7d370badc60ac94a554bc571a46d03e39d8aacfba8006b334512e184aed59", + "sha256:f6c378b435a26fda8996579c0e324b108d2ca0d01b4661503a75634e5155559f", + "sha256:f6c9d30888503f2f5f87d6d41f016301352dd98da4a861bd10663c3a2d99d3b5", + "sha256:fab8a7877275060f7b303e1f91c218069a2814a616b6a5ee2d8a3737deb15915", + "sha256:fc32e7d7f98cac3d8d5153ed2cb583158ae3d446a6efb8e28ccb1c54a09f4169" ], "markers": "python_version >= '3.6'", - "version": "==22.3.0" + "version": "==23.1.0" }, "qtconsole": { "hashes": [ - "sha256:75f2ded876444454edcb5a53262149e33b53db3a4a53116b7c3df52830905b0f", - "sha256:8e3520fdc75e46abc4cc6cffeca16fa2652754109b8ae839fa28e27d1eba5625" + "sha256:b73723fac43938b684dcb237a88510dc7721c43a726cea8ade179a2927c0a2f3", + "sha256:d364592d7ede3257f1e17fcdbfd339c26e2cc638ca4fa4ee56a724e26ea13c81" ], "markers": "python_version >= '3.7'", - "version": "==5.3.0" + "version": "==5.3.1" }, "qtpy": { "hashes": [ - "sha256:adfd073ffbd2de81dc7aaa0b983499ef5c59c96adcfdcc9dea60d42ca885eb8f", - "sha256:d93f2c98e97387fcc9d623d509772af5b6c15ab9d8f9f4c5dfbad9a73ad34812" + "sha256:aee0586081f943029312becece9f63977b0a9e3788f77a6ac8cc74802bb173d6", + "sha256:ca8cd4217175186344299ee4c0f7e7adcf362c70852ba35b255a534077025c06" ], - "markers": "python_version >= '3.6'", - "version": "==2.0.1" + "markers": "python_version >= '3.7'", + "version": "==2.1.0" + }, + "scipy": { + "hashes": [ + "sha256:02b567e722d62bddd4ac253dafb01ce7ed8742cf8031aea030a41414b86c1125", + "sha256:1166514aa3bbf04cb5941027c6e294a000bba0cf00f5cdac6c77f2dad479b434", + "sha256:1da52b45ce1a24a4a22db6c157c38b39885a990a566748fc904ec9f03ed8c6ba", + "sha256:23b22fbeef3807966ea42d8163322366dd89da9bebdc075da7034cee3a1441ca", + "sha256:28d2cab0c6ac5aa131cc5071a3a1d8e1366dad82288d9ec2ca44df78fb50e649", + "sha256:2ef0fbc8bcf102c1998c1f16f15befe7cffba90895d6e84861cd6c6a33fb54f6", + "sha256:3b69b90c9419884efeffaac2c38376d6ef566e6e730a231e15722b0ab58f0328", + "sha256:4b93ec6f4c3c4d041b26b5f179a6aab8f5045423117ae7a45ba9710301d7e462", + "sha256:4e53a55f6a4f22de01ffe1d2f016e30adedb67a699a310cdcac312806807ca81", + "sha256:6311e3ae9cc75f77c33076cb2794fb0606f14c8f1b1c9ff8ce6005ba2c283621", + "sha256:65b77f20202599c51eb2771d11a6b899b97989159b7975e9b5259594f1d35ef4", + "sha256:6cc6b33139eb63f30725d5f7fa175763dc2df6a8f38ddf8df971f7c345b652dc", + "sha256:70de2f11bf64ca9921fda018864c78af7147025e467ce9f4a11bc877266900a6", + "sha256:70ebc84134cf0c504ce6a5f12d6db92cb2a8a53a49437a6bb4edca0bc101f11c", + "sha256:83606129247e7610b58d0e1e93d2c5133959e9cf93555d3c27e536892f1ba1f2", + "sha256:93d07494a8900d55492401917a119948ed330b8c3f1d700e0b904a578f10ead4", + "sha256:9c4e3ae8a716c8b3151e16c05edb1daf4cb4d866caa385e861556aff41300c14", + "sha256:9dd4012ac599a1e7eb63c114d1eee1bcfc6dc75a29b589ff0ad0bb3d9412034f", + "sha256:9e3fb1b0e896f14a85aa9a28d5f755daaeeb54c897b746df7a55ccb02b340f33", + "sha256:a0aa8220b89b2e3748a2836fbfa116194378910f1a6e78e4675a095bcd2c762d", + "sha256:d3b3c8924252caaffc54d4a99f1360aeec001e61267595561089f8b5900821bb", + "sha256:e013aed00ed776d790be4cb32826adb72799c61e318676172495383ba4570aa4", + "sha256:f3e7a8867f307e3359cc0ed2c63b61a1e33a19080f92fe377bc7d49f646f2ec1" + ], + "index": "pypi", + "version": "==1.8.1" }, "send2trash": { "hashes": [ @@ -781,11 +964,11 @@ }, "terminado": { "hashes": [ - "sha256:874d4ea3183536c1782d13c7c91342ef0cf4e5ee1d53633029cbc972c8760bd8", - "sha256:94d1cfab63525993f7d5c9b469a50a18d0cdf39435b59785715539dd41e36c0d" + "sha256:0d5f126fbfdb5887b25ae7d9d07b0d716b1cc0ccaacc71c1f3c14d228e065197", + "sha256:ab4eeedccfcc1e6134bfee86106af90852c69d602884ea3a1e8ca6d4486e9bfe" ], "markers": "python_version >= '3.7'", - "version": "==0.13.3" + "version": "==0.15.0" }, "tinycss2": { "hashes": [ @@ -795,6 +978,14 @@ "markers": "python_version >= '3.6'", "version": "==1.1.1" }, + "toml": { + "hashes": [ + "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", + "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f" + ], + "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==0.10.2" + }, "tornado": { "hashes": [ "sha256:0a00ff4561e2929a2c37ce706cb8233b7907e0cdc22eab98888aca5dd3775feb", @@ -842,13 +1033,21 @@ "markers": "python_version >= '3.5'", "version": "==6.1" }, + "tqdm": { + "hashes": [ + "sha256:40be55d30e200777a307a7585aee69e4eabb46b4ec6a4b4a5f2d9f11e7d5408d", + "sha256:74a2cdefe14d11442cedf3ba4e21a3b84ff9a2dbdc6cfae2c34addb2a14a5ea6" + ], + "index": "pypi", + "version": "==4.64.0" + }, "traitlets": { "hashes": [ - "sha256:059f456c5a7c1c82b98c2e8c799f39c9b8128f6d0d46941ee118daace9eb70c7", - "sha256:2d313cc50a42cd6c277e7d7dc8d4d7fedd06a2c215f78766ae7b1a66277e0033" + "sha256:1530d04badddc6a73d50b7ee34667d4b96914da352109117b4280cb56523a51b", + "sha256:74803a1baa59af70f023671d86d5c7a834c931186df26d50d362ee6a1ff021fd" ], "markers": "python_version >= '3.7'", - "version": "==5.1.1" + "version": "==5.2.2.post1" }, "wcwidth": { "hashes": [ @@ -864,6 +1063,14 @@ ], "version": "==0.5.1" }, + "werkzeug": { + "hashes": [ + "sha256:1ce08e8093ed67d638d63879fd1ba3735817f7a80de3674d293f5984f25fb6e6", + "sha256:72a4b735692dd3135217911cbeaa1be5fa3f62bffb8745c5215420a03dc55255" + ], + "markers": "python_version >= '3.7'", + "version": "==2.1.2" + }, "widgetsnbextension": { "hashes": [ "sha256:4fd321cad39fdcf8a8e248a657202d42917ada8e8ed5dd3f60f073e0d54ceabd", @@ -880,5 +1087,147 @@ "version": "==3.8.0" } }, - "develop": {} + "develop": { + "coverage": { + "hashes": [ + "sha256:01c5615d13f3dd3aa8543afc069e5319cfa0c7d712f6e04b920431e5c564a749", + "sha256:106c16dfe494de3193ec55cac9640dd039b66e196e4641fa8ac396181578b982", + "sha256:129cd05ba6f0d08a766d942a9ed4b29283aff7b2cccf5b7ce279d50796860bb3", + "sha256:145f296d00441ca703a659e8f3eb48ae39fb083baba2d7ce4482fb2723e050d9", + "sha256:1480ff858b4113db2718848d7b2d1b75bc79895a9c22e76a221b9d8d62496428", + "sha256:269eaa2c20a13a5bf17558d4dc91a8d078c4fa1872f25303dddcbba3a813085e", + "sha256:26dff09fb0d82693ba9e6231248641d60ba606150d02ed45110f9ec26404ed1c", + "sha256:2bd9a6fc18aab8d2e18f89b7ff91c0f34ff4d5e0ba0b33e989b3cd4194c81fd9", + "sha256:309ce4a522ed5fca432af4ebe0f32b21d6d7ccbb0f5fcc99290e71feba67c264", + "sha256:3384f2a3652cef289e38100f2d037956194a837221edd520a7ee5b42d00cc605", + "sha256:342d4aefd1c3e7f620a13f4fe563154d808b69cccef415415aece4c786665397", + "sha256:39ee53946bf009788108b4dd2894bf1349b4e0ca18c2016ffa7d26ce46b8f10d", + "sha256:4321f075095a096e70aff1d002030ee612b65a205a0a0f5b815280d5dc58100c", + "sha256:4803e7ccf93230accb928f3a68f00ffa80a88213af98ed338a57ad021ef06815", + "sha256:4ce1b258493cbf8aec43e9b50d89982346b98e9ffdfaae8ae5793bc112fb0068", + "sha256:664a47ce62fe4bef9e2d2c430306e1428ecea207ffd68649e3b942fa8ea83b0b", + "sha256:75ab269400706fab15981fd4bd5080c56bd5cc07c3bccb86aab5e1d5a88dc8f4", + "sha256:83c4e737f60c6936460c5be330d296dd5b48b3963f48634c53b3f7deb0f34ec4", + "sha256:84631e81dd053e8a0d4967cedab6db94345f1c36107c71698f746cb2636c63e3", + "sha256:84e65ef149028516c6d64461b95a8dbcfce95cfd5b9eb634320596173332ea84", + "sha256:865d69ae811a392f4d06bde506d531f6a28a00af36f5c8649684a9e5e4a85c83", + "sha256:87f4f3df85aa39da00fd3ec4b5abeb7407e82b68c7c5ad181308b0e2526da5d4", + "sha256:8c08da0bd238f2970230c2a0d28ff0e99961598cb2e810245d7fc5afcf1254e8", + "sha256:961e2fb0680b4f5ad63234e0bf55dfb90d302740ae9c7ed0120677a94a1590cb", + "sha256:9b3e07152b4563722be523e8cd0b209e0d1a373022cfbde395ebb6575bf6790d", + "sha256:a7f3049243783df2e6cc6deafc49ea123522b59f464831476d3d1448e30d72df", + "sha256:bf5601c33213d3cb19d17a796f8a14a9eaa5e87629a53979a5981e3e3ae166f6", + "sha256:cec3a0f75c8f1031825e19cd86ee787e87cf03e4fd2865c79c057092e69e3a3b", + "sha256:d42c549a8f41dc103a8004b9f0c433e2086add8a719da00e246e17cbe4056f72", + "sha256:d67d44996140af8b84284e5e7d398e589574b376fb4de8ccd28d82ad8e3bea13", + "sha256:d9c80df769f5ec05ad21ea34be7458d1dc51ff1fb4b2219e77fe24edf462d6df", + "sha256:e57816f8ffe46b1df8f12e1b348f06d164fd5219beba7d9433ba79608ef011cc", + "sha256:ee2ddcac99b2d2aec413e36d7a429ae9ebcadf912946b13ffa88e7d4c9b712d6", + "sha256:f02cbbf8119db68455b9d763f2f8737bb7db7e43720afa07d8eb1604e5c5ae28", + "sha256:f1d5aa2703e1dab4ae6cf416eb0095304f49d004c39e9db1d86f57924f43006b", + "sha256:f5b66caa62922531059bc5ac04f836860412f7f88d38a476eda0a6f11d4724f4", + "sha256:f69718750eaae75efe506406c490d6fc5a6161d047206cc63ce25527e8a3adad", + "sha256:fb73e0011b8793c053bfa85e53129ba5f0250fdc0392c1591fd35d915ec75c46", + "sha256:fd180ed867e289964404051a958f7cccabdeed423f91a899829264bb7974d3d3", + "sha256:fdb6f7bd51c2d1714cea40718f6149ad9be6a2ee7d93b19e9f00934c0f2a74d9", + "sha256:ffa9297c3a453fba4717d06df579af42ab9a28022444cae7fa605af4df612d54" + ], + "index": "pypi", + "version": "==6.4.1" + }, + "importlib-metadata": { + "hashes": [ + "sha256:5d26852efe48c0a32b0509ffbc583fda1a2266545a78d104a6f4aff3db17d700", + "sha256:c58c8eb8a762858f49e18436ff552e83914778e50e9d2f1660535ffb364552ec" + ], + "markers": "python_version < '3.10'", + "version": "==4.11.4" + }, + "mako": { + "hashes": [ + "sha256:23aab11fdbbb0f1051b93793a58323ff937e98e34aece1c4219675122e57e4ba", + "sha256:9a7c7e922b87db3686210cf49d5d767033a41d4010b284e747682c92bddd8b39" + ], + "markers": "python_version >= '3.7'", + "version": "==1.2.0" + }, + "markdown": { + "hashes": [ + "sha256:cbb516f16218e643d8e0a95b309f77eb118cb138d39a4f27851e6a63581db874", + "sha256:f5da449a6e1c989a4cea2631aa8ee67caa5a2ef855d551c88f9e309f4634c621" + ], + "markers": "python_version >= '3.6'", + "version": "==3.3.7" + }, + "markupsafe": { + "hashes": [ + "sha256:0212a68688482dc52b2d45013df70d169f542b7394fc744c02a57374a4207003", + "sha256:089cf3dbf0cd6c100f02945abeb18484bd1ee57a079aefd52cffd17fba910b88", + "sha256:10c1bfff05d95783da83491be968e8fe789263689c02724e0c691933c52994f5", + "sha256:33b74d289bd2f5e527beadcaa3f401e0df0a89927c1559c8566c066fa4248ab7", + "sha256:3799351e2336dc91ea70b034983ee71cf2f9533cdff7c14c90ea126bfd95d65a", + "sha256:3ce11ee3f23f79dbd06fb3d63e2f6af7b12db1d46932fe7bd8afa259a5996603", + "sha256:421be9fbf0ffe9ffd7a378aafebbf6f4602d564d34be190fc19a193232fd12b1", + "sha256:43093fb83d8343aac0b1baa75516da6092f58f41200907ef92448ecab8825135", + "sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247", + "sha256:4a33dea2b688b3190ee12bd7cfa29d39c9ed176bda40bfa11099a3ce5d3a7ac6", + "sha256:4b9fe39a2ccc108a4accc2676e77da025ce383c108593d65cc909add5c3bd601", + "sha256:56442863ed2b06d19c37f94d999035e15ee982988920e12a5b4ba29b62ad1f77", + "sha256:671cd1187ed5e62818414afe79ed29da836dde67166a9fac6d435873c44fdd02", + "sha256:694deca8d702d5db21ec83983ce0bb4b26a578e71fbdbd4fdcd387daa90e4d5e", + "sha256:6a074d34ee7a5ce3effbc526b7083ec9731bb3cbf921bbe1d3005d4d2bdb3a63", + "sha256:6d0072fea50feec76a4c418096652f2c3238eaa014b2f94aeb1d56a66b41403f", + "sha256:6fbf47b5d3728c6aea2abb0589b5d30459e369baa772e0f37a0320185e87c980", + "sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b", + "sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812", + "sha256:8dc1c72a69aa7e082593c4a203dcf94ddb74bb5c8a731e4e1eb68d031e8498ff", + "sha256:8e3dcf21f367459434c18e71b2a9532d96547aef8a871872a5bd69a715c15f96", + "sha256:8e576a51ad59e4bfaac456023a78f6b5e6e7651dcd383bcc3e18d06f9b55d6d1", + "sha256:96e37a3dc86e80bf81758c152fe66dbf60ed5eca3d26305edf01892257049925", + "sha256:97a68e6ada378df82bc9f16b800ab77cbf4b2fada0081794318520138c088e4a", + "sha256:99a2a507ed3ac881b975a2976d59f38c19386d128e7a9a18b7df6fff1fd4c1d6", + "sha256:a49907dd8420c5685cfa064a1335b6754b74541bbb3706c259c02ed65b644b3e", + "sha256:b09bf97215625a311f669476f44b8b318b075847b49316d3e28c08e41a7a573f", + "sha256:b7bd98b796e2b6553da7225aeb61f447f80a1ca64f41d83612e6139ca5213aa4", + "sha256:b87db4360013327109564f0e591bd2a3b318547bcef31b468a92ee504d07ae4f", + "sha256:bcb3ed405ed3222f9904899563d6fc492ff75cce56cba05e32eff40e6acbeaa3", + "sha256:d4306c36ca495956b6d568d276ac11fdd9c30a36f1b6eb928070dc5360b22e1c", + "sha256:d5ee4f386140395a2c818d149221149c54849dfcfcb9f1debfe07a8b8bd63f9a", + "sha256:dda30ba7e87fbbb7eab1ec9f58678558fd9a6b8b853530e176eabd064da81417", + "sha256:e04e26803c9c3851c931eac40c695602c6295b8d432cbe78609649ad9bd2da8a", + "sha256:e1c0b87e09fa55a220f058d1d49d3fb8df88fbfab58558f1198e08c1e1de842a", + "sha256:e72591e9ecd94d7feb70c1cbd7be7b3ebea3f548870aa91e2732960fa4d57a37", + "sha256:e8c843bbcda3a2f1e3c2ab25913c80a3c5376cd00c6e8c4a86a89a28c8dc5452", + "sha256:efc1913fd2ca4f334418481c7e595c00aad186563bbc1ec76067848c7ca0a933", + "sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a", + "sha256:fc7b548b17d238737688817ab67deebb30e8073c95749d55538ed473130ec0c7" + ], + "markers": "python_version >= '3.7'", + "version": "==2.1.1" + }, + "pdoc3": { + "hashes": [ + "sha256:5f22e7bcb969006738e1aa4219c75a32f34c2d62d46dc9d2fb2d3e0b0287e4b7", + "sha256:ba45d1ada1bd987427d2bf5cdec30b2631a3ff5fb01f6d0e77648a572ce6028b" + ], + "index": "pypi", + "version": "==0.10.0" + }, + "pyment": { + "hashes": [ + "sha256:951a4c52d6791ccec55bc739811169eed69917d3874f5fe722866623a697f39d", + "sha256:a0c6ec59d06d24aeec3eaecb22115d0dc95d09e14209b2df838381fdf47a78cc" + ], + "index": "pypi", + "version": "==0.3.3" + }, + "zipp": { + "hashes": [ + "sha256:56bf8aadb83c24db6c4b577e13de374ccfb67da2078beba1d037c17980bf43ad", + "sha256:c4f6e5bbf48e74f7a38e7cc5b0480ff42b0ae5178957d564d18932525d5cf099" + ], + "markers": "python_version < '3.10'", + "version": "==3.8.0" + } + } } diff --git a/README.md b/README.md index 18e51dc..14efd99 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,47 @@ -# Slots - -Modelling of slot- game. - -There is an output matrix $N \times M$. - -There are $P$ items(symbols) corresponding for each column. -The probability of the $i$-th item to be on the any row on the $j$-th -column is equal $p_{i,j}$. -The column is formed by the next rule: -at first the bottom row is form ed, than another symbol is generated -on the upper row - this symbol cannot be the same that is on bottom row: -so the probality of $i_1$-th item to be there is $p_{i_1}/(1-p_{i})$, where -$i \neq i_1$. The same way, the next symbol cannot equal to any of the previous -ones, therefore, the probabilyty of the $i2$-th item to be on the column is -$p_{i_2}/(1-p_{i}-p_{i_1}))$. By continuing so on, the last probability is -$p_{i_N}/(1-p_{i}-p_{i_1} -p_{i_{N-1}}))$. - -If there are same $l$ symbols in the first $K$ columns then the output result -is $S_{l,K}$. +## Клонуємо даний репозиторій. +#### git clone {repo-url} + +## Для роботи частини backend, +Необхідно встановити Python, бажано версії 3.8, та pip. + +За бажанням можна встановити pipenv. + +У корні репозиторія відкриваємо консоль +#### та вводимо +#### pip install -r requirements.txt, якщо встановили Python та pip +#### pipenv install Pipfile, якщо встановили Python, pip та pipenv + +Після встановлення Python, pip, можливо pipenv та необхідних пакетів, переходимо до директорії backend. + +Запишіть в файл probabilities.txt бажані ймовірності, сума яких має дорівнювати 1 + +#### або запустіть файл Bandit.py командою: +#### python Bandit.py, якщо ви встановили Python та pip +#### pipenv run python Bandit.py, якщо ви встановили Python, pip та pipenv + +На запит програми введіть бажаний коефіцієнт віддачі автомату, +через деякий час будуть згенеровані ймовірності для даного коефіцієнту віддачі, +які запишуться в файл probabilities.txt. + +(якщо вводити коефіцієнт віддачі, округляючи до десятих, обчислення займе менше часу, +ніж обчислення для коефіцієнту віддачі округленого до сотих) + +### Далі запускаємо flask сервер, який буде обмінюватися даними з сайтом на JavaScript. +#### запустіть файл wsgi.py командою: +#### python wsgi.py, якщо ви встановили Python та pip +#### pipenv run python wsgi.py, якщо ви встановили Python, pip та pipenv +Якщо не було ніяких проблем йдемо далі. +### Документацію до коду частини backend можна знайти у директорії Slots\backend\doc\backend + +## Для роботи частини user_interface, +яка являє собою сайт написаний на JavaScript, +який спілкується з сервером на Python фреймворці Flask, +необхідно встановити npm - менеджер пакетів. + +Після встановлення npm, відкриваємо консоль та переходимо у директорію user_interface, +#### вводимо в консоль npm install +для встановлення всіх необхідних пакетів для роботи сайту. +У директорії user_interface повинна з'явитися нова директорія node_modules. +#### Для старту сервера сайту необхідно ввести в консоль npm start. +Далі переходимо за адресою http://localhost:8080 +та використовуємо сайт \ No newline at end of file diff --git a/backend/Bandit.py b/backend/Bandit.py new file mode 100644 index 0000000..2b880c5 --- /dev/null +++ b/backend/Bandit.py @@ -0,0 +1,524 @@ +from __future__ import annotations + +import os +from multiprocessing import Pool +from typing import Dict, Tuple, List, Optional, Union +import numpy as np +from tqdm import tqdm + +from backend.CustomExceptions import * +from backend.GenAlgorithm import GeneticAlgorithm + + +class OneHandBandit: + """ """ + players: List[OneHandBandit] = [] + + def __init__(self, m: int, n: int, k: int, probs: Optional[List[float, ...]] = None): + """функція конструктор для класу OneHandBandit + Args: + m: number of columns + n: number of rows + k: number of pictures + + k(number of pictures) must be greater or equal to n(number of rows) + + probs: probability of each picture appearance + """ + + self.drumsColumns = [[i for i in range(k)] for _ in range(m)] + self.columnsNumb = m + self.rowsNumb = n + self.picturesNumb = k + + self.probabilities: List[float, ...] = list() + self.setProbabilities(probs) + + self.combinations: Dict[Tuple[int, ...], int] = dict() + self.price: int = 0 + + self.money: int = 0 + self.moneySpent = 0 + self.moneyWon: int = 0 + + self.state: List[List[int,],] = list() + + self.genAlgo = GeneticAlgorithm(self) + + OneHandBandit.players.append(self) + + def setWinningCombs(self, combs: Dict[Tuple[int, ...], int]): + """функція, яка встановлює виграшні комбінації + + Args: + combs: dict of winning combinations + + example: { (7, 7, 7): 100, (3, 5, 7): 200, (4, 3, 0): 500 } + + Returns: + + """ + combs = combs.copy() + for comb in combs: + if len(comb) != self.columnsNumb: + raise CombinationLengthException + elif max(comb) > (self.picturesNumb - 1) or min(comb) < 0: + raise CombinationValuesException + elif combs[comb] < 0: + raise WinningMoneyException + + self.combinations = combs + + def addWinningComb(self, comb: Tuple[int, ...], win: int) -> None: + """функція, яка додає одну виграшну комбінацію до списку виграшних комбінацій + + Args: + comb: комбінація індексів зображень + win: виграшна сума + + Returns: + + """ + self.combinations[comb] = win + + # the price of 1 turn + def setPriceOfGame(self, price) -> None: + """функція, яка встановлює вартість однієї гри + + Args: + price: вартість однієї гри + + Returns: + + """ + self.price = price + + # money player gives to bandit + def startGame(self, money: int, modeling: bool = False) -> None: + """функція, яка вносить до автомату певну суму грошей, на які можна грати + + Args: + money: сума грошей, яка вноситься до автомату + modeling: параметр, який регулює інформативність функції + + Returns: + + """ + if money > 0: + self.money = money + if not modeling: + print(f"you deposited {money} money") + + def setProbabilities(self, probs: Optional[List[float, ...]] = None) -> None: + """функція, яка встановлює ймовірності для відповідних зображень з індексами від 0 до self.picturesNumb - 1, + слідкуючи за тим, щоб сума ймовірностей була рівною одиниці + + Args: + probs: ймовірності зображень + + Returns: + + """ + if probs is None: + # uniform distribution + probs = [1 / self.picturesNumb for i in range(self.picturesNumb)] + + if 0.001 < abs(sum(probs) - 1.0): + # sum of probs dont equal 1 + raise ProbabilitiesSumException + + if len(probs) != self.picturesNumb: + raise ProbabilitiesArrayLengthException + + self.probabilities = probs.copy() + + def setProbabilitiesFromTxtFile(self, filename: str = "probabilities.txt") -> None: + """функція, яка читає ймовірності з файлу, які розділені одним пробілом та визиває функцію self.setProbabilities + + Args: + filename: ім'я файлу з ймовірностями + + Returns: + + """ + with open(filename, "r") as f: + probsLine = f.readline() + probs = list(map(lambda x: float(x), probsLine.split(" "))) + + self.setProbabilities(probs) + + def genBaseProbabilities(self, probsCount: int, returnCoef: Optional[float] = None, + debug: bool = False) -> List[float]: + """функція, яка генерує список ймовірностей, сума яких рівна одиниці. + + Можлива генерація випадкових ймовірностей + та генерація ймовірностей, які б задовольняли певний коефіцієнт віддачі автомату + + Args: + probsCount: довжина списку ймовірностей + returnCoef: коефіцієнт віддачі автомату, який має задовольняти ймовірностям + + якщо returnCoef is None, то генеруються випадкові ймовірності + + якщо returnCoef is not None, то генеруються ймовірності за допомогою генетичного алгоритму + + debug: параметр, який регулює інформативність функції + + Returns: + згенерований список ймовірностей + + """ + probs = [] + if returnCoef is None: + # generate random probabilities + high = 1 + e = 0.25 * high + + for _ in range(probsCount - 1): + probs.append(np.random.uniform(0, high - e)) + high -= probs[-1] + e = 0.01 * high + probs.append(high) + return probs + else: + probs = self.genAlgo.geneticAlgorithm(probsCount=probsCount, goalValue=returnCoef, debug=debug) + return probs + + def getReturnCoef(self, gamesCount: int = 10 ** 4, probs: Optional[List[float, ...]] = None, + isGraph: bool = False) -> Union[float, List[float]]: + """функція, яка рахує коефіцієнт віддачі автомата, моделюючи методом Монте-Карло gamesCount ігор. + + функція рахує середній виграш гравця, протягом gamesCount ігор, та ділить його на ціну однієї гри + + Args: + gamesCount: кількість ігор, які будуть промодельовані + probs: ймовірності зображень, з якими буде проводитись кожна гра + isGraph: параметр, який відповідає за надання функцією додаткових даних необхідних для створення графіку + + Returns: + коефіцієнт віддачі автомату, якщо isGraph рівне False + + список коефіцієнтів віддачі автомату для кожної гри, якщо isGraph рівне True + + """ + oldProbs = self.probabilities + oldMoneySpent = self.moneySpent + oldMoneyWon = self.moneyWon + if probs is not None: + self.setProbabilities(probs) + self.moneyWon = 0 + self.moneySpent = 0 + + money = self.price * gamesCount + + modeling = True + self.startGame(money, modeling=modeling) + meanRCs = self.play(gamesCount, modeling=modeling, isGraph=isGraph) + gameReturn = self.moneyWon + gameReturnPercentage = gameReturn / self.moneySpent + if probs is not None: + self.setProbabilities(oldProbs) + self.moneyWon = oldMoneyWon + self.moneySpent = oldMoneySpent + if isGraph: + return meanRCs + + return gameReturnPercentage + + def getReturnCoefWithNCores(self, gamesCount: int = 10 ** 3, probs: Optional[List[float, ...]] = None, + coresCount: int = -1): + """функція, яка прискорює обчислення коефіцієнту віддачі автомату для великої кількості ігор, + шляхом розпаралелювання роботи. + + функція запускає coresCount процесів, де виконуються функції self.getReturnCoef, + але для меншого значення gamesCount, рівному gamesCount // coresCount. + + Args: + gamesCount: кількість ігор, які будуть промодельовані + probs: ймовірності зображень, з якими буде проводитись кожна гра + coresCount: кількість процесів, які запустить дана функція + + Returns: + коефіцієнт віддачі автомату + + """ + if coresCount == -1: + coresCount = int(os.cpu_count() * 0.75) + if coresCount == 0: + coresCount = 1 + elif coresCount == 0: + return self.getReturnCoef(gamesCount, probs) + + gamesCountList = [gamesCount // coresCount, ] * coresCount + gamesCountList[-1] += (gamesCount - sum(gamesCountList)) + + args = zip(gamesCountList, [probs, ] * len(gamesCountList), ) + # args = list(args) + + with Pool(processes=coresCount) as executor: + results = executor.starmap(self.getReturnCoef, args) + + res = sum(results) / coresCount + return res + + # @benchmark + def currentTurn(self, modeling: bool = False) -> int: + """функція, яка генерує матрицю однієї гри(складається з стовпців, кожен з яких містить в собі індекси зображень) + та викликає функцію self.setState + + Args: + modeling: параметр, який регулює інформативність функції + + Returns: + виграш гравця + + """ + self.setState([self.turnColumn(i) for i in range(self.columnsNumb)]) + self.money -= self.price + self.moneySpent += self.price + won = self.currentWon() + + if not modeling: + self.printState() + + return won + + def turnColumn(self, i) -> List[int,]: + """функція, яка генерує один стовпець для майбутньої матриці однієї гри + + Args: + i: індекс стовпця, який також відповідає індексу стовпця з self.drumColumns, + де кожен стовпець містить індекси зображень, які взагалі можуть з'явитися в і-тому стовпці матриці однієї гри + + Returns: + список індексів зображень + + """ + downIdx = self.rowsNumb - 1 + upIdx = 0 + rolledPicturesIdx = [] + resColumn = [-1, ] * self.rowsNumb + for idx in range(self.rowsNumb): + newProbs = self.genTurnProbabilities(rolledPicturesIdxs=rolledPicturesIdx) + + # assert abs(sum(newProbs) - 1) < 0.001 + + rolledPicIdx = np.random.choice(self.drumsColumns[i], size=1, p=newProbs)[0] + rolledPicturesIdx.append(rolledPicIdx) + + if (idx % 2) == 0: + resColumn[downIdx] = rolledPicIdx + downIdx -= 1 + else: + resColumn[upIdx] = rolledPicIdx + upIdx += 1 + + return resColumn + + def genTurnProbabilities(self, rolledPicturesIdxs: List[int, ...]) -> List: + """функція, яка обраховує ймовірності для зображень у автоматі при генерації одного стовпчика, + точніше описано в файлі README.ipynb + + Args: + rolledPicturesIdxs: зображення, які уже були згенеровані у рядках одного стовпчика + + Returns: + значення ймовірностей для наступної генерації + + """ + newProbs = np.array(self.probabilities, dtype=float) + divideCoeff = 1 - sum([self.probabilities[idx] for idx in rolledPicturesIdxs]) + newProbs[rolledPicturesIdxs] = 0.0 + newProbs = newProbs / divideCoeff + return list(newProbs) + + def currentWon(self) -> int: + """функція, яка аналізує поточну матрицю гри та повертає весь виграш гравця за поточну гру + + Args: + + Returns: + весь виграш гравця + + """ + won = 0 + for i in range(self.rowsNumb): + comb = tuple(self.getRow(i)) + if comb in self.combinations: + # it is possible to have several winning combinations.... + won += self.combinations[comb] + + self.moneyWon += won # ???? not sure if it is needed + return won + + def getRow(self, i) -> np.array: + """функція, яка повертає і-ий рядок матриці self.state(матриці однієї гри) + + Args: + i: індекс рядка матриці гри + + Returns: + список індексів зображень + + """ + return np.array([self.state[j][i] for j in range(self.columnsNumb)]) + + def getColumn(self, j): + """функція, яка повертаю j-ий стовпець матриці self.state(матриці однієї гри) + + Args: + j: індекс стовпця матриці гри + + Returns: + список індексів зображень + + """ + return self.state[j] + + def getState(self) -> np.array: + """функція, яка повертає матрицю self.state(матрицю однієї гри) + + Args: + + Returns: + транспонована(задля спрощення її відображення) матриця self.state + + """ + return np.array(self.state).T + + def setState(self, state: List[List[int,],]) -> None: + """функція, яка встановлює матрицю однієї гри + + Args: + state: матриця індексів зображень однієї гри + + Returns: + + """ + if all([len(col) == len(set(col)) for col in state]): + self.state = state + else: + raise SettingStateException + + def printState(self) -> None: + """функція, яка відображає матрицю поточної гри, весь виграш гравця, гроші, які у нього залишилися""" + print(f"current state: \n{self.getState()}") + # print("money spent:", self.moneySpent) + print("money won:", self.moneyWon) + print("money remained:", self.money) + + def play(self, n, modeling: bool = False, isGraph: bool = False) -> Union[Optional[List[int,]], + List[float]]: + """функція, яка проводить n ігор + + Args: + n: кількість ігор + modeling: параметр, який впливає на інформативність функції + isGraph: параметра, який керує поверненням спеціальних значень + + Returns: + список виграшів гравця за n ігор, якщо isGraph рівне False + + список коефіцієнтів віддачі автомату для кожної з n ігор, якщо isGraph рівне True + + """ + if self.money < self.price * n: + print("Not enough money") + return None + + wons = [] + meanRCs = [] + # play game n times + for i in range(n): + won = self.currentTurn(modeling=modeling) + wons.append(won) + if not modeling: + print(f"you won {won} money for one turn") + print(f"you spent {self.price} money for one turn") + print() + + if isGraph: + meanRCs.append(sum(wons) / ((i + 1) * self.price)) + + if isGraph: + return meanRCs + + if not modeling: + return wons + + # winning sum in fact could be (and often is) negative ;-)) + @property + def currentPlayerWon(self): + """функція, яка декорується, як властивість об'єкту та повертає весь виграш гравця, + віднявши від нього витрати гравця + + Args: + + Returns: + виграш гравця - витрати гравця + + """ + return self.moneyWon - self.moneySpent # not sure + + @property + def totalPlayersWon(self): + """функція, яка декорується, як властивість об'єкту та повертає весь виграш всіх гравців, + який вона обраховує, використовуючи функцію self.currentPlayerWon + + Args: + + Returns: + виграш всіх гравців - витрати всіх гравців + + """ + totalWin = 0 + for p in OneHandBandit.players: + totalWin += p.currentPlayerWon + return totalWin + + +if __name__ == "__main__": + p2 = OneHandBandit(4, 3, 5) + # p2.setProbabilitiesFromTxtFile("probabilities.txt") + + p2.setPriceOfGame(10) + p2.setWinningCombs({(0, 0, 0, 0): 15, (1, 1, 1, 1): 15, (2, 2, 2, 2): 15, (3, 3, 3, 3): 15, (1, 2, 3, 4): 20, }) + p2.addWinningComb((4, 4, 4, 4), 15) + + # p2.startGame(100) + # p2.play(10) + try: + returnCoef = float( + input("write down return coefficient for probabilities you would like get for(Example 0.9): ")) + except Exception as e: + print("during input error occurred:", e) + print("return coefficient set to 0.9") + returnCoef = 0.9 + # np.random.seed(42) + probs = p2.genBaseProbabilities(p2.picturesNumb, returnCoef, debug=True) + print(f"{probs=}") + print(f"fitness val for probs and goalValue={returnCoef}:", + p2.genAlgo.fitness_func(probs=probs, goalValue=returnCoef)) + print("return coef (goalValue):", p2.getReturnCoefWithNCores(probs=probs, gamesCount=10 ** 4)) + + probsStrs = " ".join(map(str, probs)) + with open("probabilities.txt", "w") as f: + f.write(probsStrs) + # modelingGamesCount = 2000 + # + # print(p2.genBaseProbabilities(5, returnCoef=0.8)) + # p2.getReturnCoefWithNCores(modelingGamesCount) + # l = [] + # t0 = time.time() + # for i in range(10): + # + # returnCoef = p2.getReturnCoefWithNCores(gamesCount=modelingGamesCount, coresCount=5) + # l.append(returnCoef) + # print(f"{i}) this one hand bandit return coefficient: {returnCoef} " + # f"for {modelingGamesCount} games") + # print("min max range:", max(l) - min(l)) + # print(time.time() - t0, "secs") + +# метод ньютона + +# генетичний алгоритм(рандомна мутація) diff --git a/CustomExceptions.py b/backend/CustomExceptions.py similarity index 100% rename from CustomExceptions.py rename to backend/CustomExceptions.py diff --git a/backend/GenAlgorithm.py b/backend/GenAlgorithm.py new file mode 100644 index 0000000..5211545 --- /dev/null +++ b/backend/GenAlgorithm.py @@ -0,0 +1,385 @@ +from typing import List, Iterable, Tuple, Union + +import numpy as np +from scipy.stats import truncnorm +from tqdm import tqdm + + +class GeneticAlgorithm: + """ """ + + def __init__(self, bandit): + from backend.Bandit import OneHandBandit + self.bandit: OneHandBandit = bandit + + def geneticAlgorithm(self, probsCount: int, goalValue: float, + populationSize: int = 6, population: List[List[float]] = None, gamesCount: int = 3000, + generationsCount: int = 100, eps: float = 0.001, debug: bool = False, + isGraph: bool = False) -> np.array: + """основний метод генетичного алгоритму, який який керує усіма іншими методами та повертає результат, + в виді списка ймовірностей + + Args: + probsCount: довжина списку ймовірностей, який ви хотіли б отримати + goalValue: значення коефіцієнта віддачі, якого ви хотіли б досягти зі згенерованими ймовірностями + populationSize: розмір популяції генетичного алгоритму + population: список індивидів(списків з ймовірностями), які будуть використовуватись генетичним алгоритмом + gamesCount: кількість ігор, для функції обрахунку коефіцієнту віддачі, + яка надалі буде використовуватися функцією фітнесу(fitness_func) + generationsCount: максимальна кількість поколінь після, якої зупиниться генетичний алгоритм + eps: похибка, яку дозволяє поточний генетичний алгоритм: + генетичний алгоритм зупиниться, + якщо (коефіцієнт віддачі згенерованих ймовірностей) - goalValue буде менше за цю похибку) + debug: параметр, який регулює інформативність алгоритму: + + якщо debug рівне False алгоритм не інформує про зміни в поколіннях + + якщо debug рівне True алгоритм інформує про зміни в поколіннях + isGraph: параметр, який відповідає за надання функцією додаткових даних необхідних для створення графіку + + Returns: + якщо isGraph рівне False: список ймовірностей, + які мають коефіцієнт віддачі рівним goalValue з певною похибкою eps + якщо isGraph рівне True: списки коефіцієнтів віддачі популяції у різних поколіннях + + """ + if population is None: + population = [] + for _ in range(populationSize): + population.append(np.array(self.bandit.genBaseProbabilities(probsCount))) + + # population = [[0.34620235, 0.07168648, 0.06275534, 0.51076111, 0.00859472], + # [0.48455469, 0.29974074, 0.18556755, 0.02237517, 0.00776185], + # [0.4076457, 0.24869838, 0.14315961, 0.03328834000000005, 0.16720797], + # [0.15385934, 0.64327094, 0.14050975, 0.03888020000000005, 0.02347977], + # [0.32335616, 0.08463666, 0.28513406, 0.04416111, 0.26271201], + # [0.43279837, 0.25248431, 0.05619025, 0.2325301, 0.02599697], ] + # [0.39709205, 0.23165409, 0.0515545 , 0.24932884, 0.07037052], ] + # fitness coefs: 0.65333333, 0.677 , 0.80883333, 0.5645 , 0.81633333 + + # population = [[0.13124844, 0.47042438, 0.0631639 , 0.26138892, 0.07377436], + # [0.0952308 , 0.65992553, 0.14414762, 0.03988682, 0.06080923], + # [0.43279837, 0.25248431, 0.05619025, 0.2325301 , 0.02599697], + # [0.26570799, 0.52636168, 0.11265015, 0.05940529, 0.03587489], + # [0.39709205, 0.23165409, 0.0515545 , 0.24932884, 0.07037052], ] + # fitness coefs: 0.76933333, 0.56766667, 0.77983333, 0.72416667, 0.802 + + # цільова функція(фітнес функція) чим менше значення, тим більша ймовірність виживання індивіда + fitness = lambda probs: self.fitness_func(probs=probs, goalValue=goalValue, gamesCount=gamesCount, + debug=debug, isGraph=isGraph) + + + fitnessVals = [] + returnCoefsInGenerations = [] + # if debug is True: + # iterator = range(generationsCount) + # else: + # iterator = tqdm(range(generationsCount)) + + # for generationIdx in tqdm(range(generationsCount)): + for generationIdx in range(generationsCount): + if debug is True: + print("\n######################generation:", generationIdx) + + fitnessVals = np.array([fitness(ps) for ps in population]) + if isGraph is True: + mass = fitnessVals.copy() + fitnessVals = np.array([i[1] for i in mass]) + returnCoefsInGenerations.append([i[0] for i in mass]) + + if debug is True: + print(f"{fitnessVals=}\n") + + # Обчислення коефіцієнту виживання кожного індивіду + surviveProbs = self.getSurviveProbs(fitnessVals) + # print(f"{surviveProbs=}\n") + + # Перевірка умов зупинки Генетичного Алгоритму + if any(fitnessVals <= eps): + if debug is True: + print("\n-----------------fitness values to break cycle:", fitnessVals) + break + + # Відбір + population = self.selection(population, surviveProbs) + + # Скрещування + population = self.crossOver(population) + for idx in range(len(population)): + self.changeProbs(population[idx]) + + # Мутація + # ймовірність мутації кожної особини + mutateProb = 0.2 + for idx in range(len(population)): + rNumb = np.random.choice([0, 1], size=1, p=[1 - mutateProb, mutateProb])[0] + if rNumb: + self.mutatePerson(population[idx], indpb=1 / len(population[idx]), debug=debug) + + if isGraph is True: + return returnCoefsInGenerations + + mass = sorted(list(zip(fitnessVals, range(len(fitnessVals)))), key=lambda x: x[0]) + population = [population[i[1]] for i in mass] + return population[0] + + # @benchmark + def fitness_func(self, probs: List[float,], goalValue: float, debug: bool = False, isGraph: bool = False, + gamesCount: int = 5000) -> Union[float, Tuple]: + """функція обраховує значення функції фітнесу: + чим значення коефіцієнту віддачі ймовірностей probs ближче до goalValue, тим значення фітнес функції менше + і навпаки. + + Args: + probs: ймовірності для яких необхідно порахувати значення фітнес функції + goalValue: значення коефіцієнту віддачі при якому рахується значення фітнес функція + debug: параметр, який регулює інформативність функції + gamesCount: кількість ігор, для функції обрахунку коефіцієнту віддачі + isGraph: параметр, який відповідає за надання функцією додаткових даних необхідних для створення графіку + + Returns: + якщо isGraph рівне False: значення фітнес функції + якщо isGraph рівне True: (коефіцієнт віддачі, значення фітнес функції) + + """ + currReturnCoef = self.bandit.getReturnCoefWithNCores(gamesCount=gamesCount, probs=probs, coresCount=-1) + # print(f"\n{probs=}") + if debug is True: + print(f"currFitness={abs(currReturnCoef - goalValue)}") + print(f"{currReturnCoef=}\n") + if isGraph is True: + return currReturnCoef, abs(currReturnCoef - goalValue) + + return abs(currReturnCoef - goalValue) + + def getSurviveProbs(self, fitnessVals: np.array(List[Iterable[float]])) -> np.array(List[Iterable[float]]): + """функція обраховує ймовірність виживання кожної окремої особини з популяції + + Args: + fitnessVals: список значень функції фітнесу особин для популяції + + Returns: + список ймовірностей виживання особин з популяції, обрахованний на основі значень функції фітнесу + + """ + fitnessVals[fitnessVals == 0] = 0.000001 + + invS = sum(1 / fitnessVals) + surviveProbs = (1 / fitnessVals) / invS + return surviveProbs + + def selection(self, population: List[np.array,], surviveProbs): + """функція реалізовує відбір методом рулетки для генетичного алгоритму + + Args: + population: поточна популяція + surviveProbs: ймовірності виживання кожної особини з поточної популяції + + Returns: + нова популяція, такого ж розміру, що і стара, але без відсіяних особин + + """ + # + indices = list(np.random.choice(range(len(population)), size=len(population), p=surviveProbs)) + newPopulation = list(np.array(population)[indices]) + return newPopulation + + def crossOver(self, population): + """функція формує сім'ї(списки по дві особини) з поточної популяції, які потім передає у функцію скрещування + + Args: + population: поточна популяція + + Returns: + нова популяція, такого ж розміру, як і стара, але з особин утворених в результаті скрещювання + + """ + # len(population) повинно бути парним + + # N = (len(population) // 2) * 2 + N = len(population) + families = [population[i: (i + 2)] for i in range(0, N, 2)] + # if len(population) % 2 == 1: + # families.append([population[-1], population[0]]) + + # ймовірність скрещування + crossOverProb = 0.9 + successors = [self.getSuccessor(f, crossOverProb) for f in families] + newPopulation = [] + for i in successors: + newPopulation.extend(i) + + return newPopulation + + def getSuccessor(self, family: np.array(np.array(List[Iterable[float]])), crossOverProb: float): + """функціє реалізує одноточкове скрещування для генетичного алгоритму + + Args: + family: список з двох особин для скрещування + crossOverProb: ймовірність скрещювання(досить велика ймовірність) + + Returns: + повертає двох особин: + + якщо скрещування відбулось: повертаються нові особини, які є результатом скрещювання + + якщо скрещування не відбулось: повертаються батьки(список family) + + """ + parent1, parent2 = family + gensCount = len(parent1) + + # low border is inclusive, high border is exclusive + splitIdx = np.random.randint(1, gensCount) + # Х.-батько: a1 | b1,c1,d1; Х.-мати: a2 | b2,c2,d2; Х.-потомок: a1,b2,c2,d2 + successor1 = np.array(list(parent1[: splitIdx]) + list(parent2[splitIdx:])) + + # Х.-батько: a1 | b1,c1,d1; Х.-мати: a2 | b2,c2,d2; Х.-потомок: a2,b1,c1,d1 + successor2 = np.array(list(parent2[: splitIdx]) + list(parent1[splitIdx:])) + + # low border is inclusive, high border is exclusive + rIdx = np.random.choice([0, 1], size=1, p=[1 - crossOverProb, crossOverProb])[0] + if rIdx == 1: + self.changeProbs(successor1) + self.changeProbs(successor2) + return [successor1, successor2] + else: + return family + + def mutatePerson(self, person: np.array, indpb: float = 0.01, delta: float = 1, debug: bool = False) -> None: + """функція реалізує мутацію певної особини для генетичного алгоритму, + де новий мутований ген вибирається, + як значення нормально розподіленої випадкової величини + з середнім рівним значенню поточного гену та дисперсією рівною одиничці + + Args: + person: особина, яка буде піддана мутації + indpb: ймовірність мутації одного гена особини(однієї ймовірності) + delta: максимальна різниця між поточним геном та мутованим + debug: параметр, який регулює інформативність функції + + Returns: + + """ + + def get_truncated_normal(mean=0, sd=1, low=0, upp=10): + """ + + Args: + mean: (Default value = 0) + sd: (Default value = 1) + low: (Default value = 0) + upp: (Default value = 10) + + Returns: + + """ + return truncnorm( + (low - mean) / sd, (upp - mean) / sd, loc=mean, scale=sd) + + person0 = person.copy() + for idx in range(len(person)): + if np.random.uniform() <= indpb: + if debug is True: + print("mutation of one gen in one person_________________") + leftBorder = max(0, person[idx] - delta) + rightBorder = min(1, person[idx] + delta) + # newGenVal = random.triangular(leftBorder, rightBorder, random.gauss(person[idx], 1)) + newGenVal = get_truncated_normal(mean=person[idx], sd=1, low=leftBorder, upp=rightBorder).rvs() + if debug is True: + print(f"old {person=}") + print(f"old gen:{person[idx]}, new gen:{newGenVal}\n") + # newGenVal = np.random.uniform(low=leftBorder, high=rightBorder, size=1)[0] + person[idx] = newGenVal + self.changeProbs(person, indsNot2ch=[idx, ]) + + if debug is True: + print(f"new {person=}\n") + + if any(person < 0): + print(f"{person0=}") + print(f"{person=}") + + def changeProbs(self, probs: np.array, eps: float = 0.0001, indsNot2ch: List[int] = None) -> None: + """якщо indsNot2ch порожній, + функція зменшує або збільшує кожну ймовірність з probs, для того, щоб сума була рівна 1 + + якщо indsNot2ch не порожній, + функція зменшує або збільшує всі ймовірності з probs, окрім тих ймовірностей, + чиї індексі зазначені у списку indsNot2ch, для того, щоб сума була рівна 1 + + Args: + probs: масив ймовірностей + eps: похибка, яку дозволяє функція + indsNot2ch: список індексів ймовірностей, які не можна змінювати + + Returns: + + """ + sVal = probs.sum() + value = abs(1 - sVal) + + if value < eps: + maxElIdx = max(zip(range(len(probs)), probs), key=lambda x: x[1])[0] + indices = list(range(len(probs))) + indices.remove(maxElIdx) + + probs[maxElIdx] = (1 - probs[indices].sum()) + return + + if indsNot2ch is None: + inds2ch = list(range(len(probs))) + else: + inds2ch = [i for i in range(len(probs)) if i not in indsNot2ch] + + if sVal > 1: + self.decreaseProbs(probs, value, inds2ch) + else: + self.increaseProbs(probs, value, inds2ch) + + # для того, щоб прибрати відхилення суми від одиниці, яке менше за eps + self.changeProbs(probs) + + def decreaseProbs(self, probs: np.ndarray, value, inds2ch: List[int]) -> None: + """value не повинно бути більше за sum(probs) + зменшуємо кожне число пропорційно: + + якщо індекс ймовірності знаходиться у списку inds2ch, то ймовірність не змінюється + + якщо індекс ймовірності p0 не належить списку inds2ch + та ймовірність p0 становить 50% від суми всіх ймовірностей, то p0 зменшиться на 50% від числа value + + Args: + probs: список ймовірностей + value: значення на яке сумарно треба зменшити ймовірності + inds2ch: список індексів ймовірностей, які не можна змінювати + + Returns: + + """ + s = sum(probs[inds2ch]) + percents = probs[inds2ch] / s + percentVals = percents * value + probs[inds2ch] = probs[inds2ch] - percentVals + + def increaseProbs(self, probs: np.ndarray, value, inds2ch: List[int]): + """value не повинно бути більше за sum(probs) + збільшуємо кожне число пропорційно: + + якщо індекс ймовірності знаходиться у списку inds2ch, то ймовірність не змінюється + + якщо індекс ймовірності p0 не належить списку inds2ch + та ймовірність p0 становить 50% від суми всіх ймовірностей, то p0 збільшиться на 50% від числа value + + Args: + probs: список ймовірностей + value: значення на яке сумарно треба зменшити ймовірності + inds2ch: список індексів ймовірностей, які не можна змінювати + + Returns: + + """ + s = sum(probs[inds2ch]) + percents = probs[inds2ch] / s + percentVals = percents * value + probs[inds2ch] = probs[inds2ch] + percentVals diff --git a/backend/benchmark.py b/backend/benchmark.py new file mode 100644 index 0000000..12df887 --- /dev/null +++ b/backend/benchmark.py @@ -0,0 +1,19 @@ +import time + + +def benchmark(func): + """функція декоратор, яка використовується для обрахунку часу роботи функції func + + Args: + func: функція, яка декорується + + Returns: + функція _benchmark + + """ + def _benchmark(*args, **kwargs): + t0 = time.time() + res = func(*args, **kwargs) + print(f"{func.__name__} elapsed {time.time() - t0} secs") + return res + return _benchmark diff --git a/backend/doc/backend/Bandit.html b/backend/doc/backend/Bandit.html new file mode 100644 index 0000000..056a316 --- /dev/null +++ b/backend/doc/backend/Bandit.html @@ -0,0 +1,1909 @@ + + + + + + +backend.Bandit API documentation + + + + + + + + + + + +
+
+
+

Module backend.Bandit

+
+
+
+ +Expand source code + +
from __future__ import annotations
+
+import os
+from multiprocessing import Pool
+from typing import Dict, Tuple, List, Optional, Union
+import numpy as np
+from tqdm import tqdm
+
+from backend.CustomExceptions import *
+from backend.GenAlgorithm import GeneticAlgorithm
+
+
+class OneHandBandit:
+    """ """
+    players: List[OneHandBandit] = []
+
+    def __init__(self, m: int, n: int, k: int, probs: Optional[List[float, ...]] = None):
+        """функція конструктор для класу OneHandBandit
+        Args:
+            m: number of columns
+            n: number of rows
+            k: number of pictures
+
+                k(number of pictures) must be greater or equal to n(number of rows)
+
+            probs: probability of each picture appearance
+        """
+
+        self.drumsColumns = [[i for i in range(k)] for _ in range(m)]
+        self.columnsNumb = m
+        self.rowsNumb = n
+        self.picturesNumb = k
+
+        self.probabilities: List[float, ...] = list()
+        self.setProbabilities(probs)
+
+        self.combinations: Dict[Tuple[int, ...], int] = dict()
+        self.price: int = 0
+
+        self.money: int = 0
+        self.moneySpent = 0
+        self.moneyWon: int = 0
+
+        self.state: List[List[int,],] = list()
+
+        self.genAlgo = GeneticAlgorithm(self)
+
+        OneHandBandit.players.append(self)
+
+    def setWinningCombs(self, combs: Dict[Tuple[int, ...], int]):
+        """функція, яка встановлює виграшні комбінації
+
+        Args:
+          combs: dict of winning combinations
+
+            example: { (7, 7, 7): 100, (3, 5, 7): 200, (4, 3, 0): 500 }
+
+        Returns:
+
+        """
+        combs = combs.copy()
+        for comb in combs:
+            if len(comb) != self.columnsNumb:
+                raise CombinationLengthException
+            elif max(comb) > (self.picturesNumb - 1) or min(comb) < 0:
+                raise CombinationValuesException
+            elif combs[comb] < 0:
+                raise WinningMoneyException
+
+        self.combinations = combs
+
+    def addWinningComb(self, comb: Tuple[int, ...], win: int) -> None:
+        """функція, яка додає одну виграшну комбінацію до списку виграшних комбінацій
+
+        Args:
+          comb: комбінація індексів зображень
+          win: виграшна сума
+
+        Returns:
+
+        """
+        self.combinations[comb] = win
+
+    # the price of 1 turn
+    def setPriceOfGame(self, price) -> None:
+        """функція, яка встановлює вартість однієї гри
+
+        Args:
+          price: вартість однієї гри
+
+        Returns:
+
+        """
+        self.price = price
+
+    # money player gives to bandit
+    def startGame(self, money: int, modeling: bool = False) -> None:
+        """функція, яка вносить до автомату певну суму грошей, на які можна грати
+
+        Args:
+          money: сума грошей, яка вноситься до автомату
+          modeling: параметр, який регулює інформативність функції
+
+        Returns:
+
+        """
+        if money > 0:
+            self.money = money
+            if not modeling:
+                print(f"you deposited {money} money")
+
+    def setProbabilities(self, probs: Optional[List[float, ...]] = None) -> None:
+        """функція, яка встановлює ймовірності для відповідних зображень з індексами від 0 до self.picturesNumb - 1,
+        слідкуючи за тим, щоб сума ймовірностей була рівною одиниці
+
+        Args:
+          probs: ймовірності зображень
+
+        Returns:
+
+        """
+        if probs is None:
+            # uniform distribution
+            probs = [1 / self.picturesNumb for i in range(self.picturesNumb)]
+
+        if 0.001 < abs(sum(probs) - 1.0):
+            # sum of probs dont equal 1
+            raise ProbabilitiesSumException
+
+        if len(probs) != self.picturesNumb:
+            raise ProbabilitiesArrayLengthException
+
+        self.probabilities = probs.copy()
+
+    def setProbabilitiesFromTxtFile(self, filename: str = "probabilities.txt") -> None:
+        """функція, яка читає ймовірності з файлу, які розділені одним пробілом та визиває функцію self.setProbabilities
+
+        Args:
+          filename: ім'я файлу з ймовірностями
+
+        Returns:
+
+        """
+        with open(filename, "r") as f:
+            probsLine = f.readline()
+            probs = list(map(lambda x: float(x), probsLine.split(" ")))
+
+        self.setProbabilities(probs)
+
+    def genBaseProbabilities(self, probsCount: int, returnCoef: Optional[float] = None,
+                             debug: bool = False) -> List[float]:
+        """функція, яка генерує список ймовірностей, сума яких рівна одиниці.
+
+        Можлива генерація випадкових ймовірностей
+        та генерація ймовірностей, які б задовольняли певний коефіцієнт віддачі автомату
+
+        Args:
+          probsCount: довжина списку ймовірностей
+          returnCoef: коефіцієнт віддачі автомату, який має задовольняти ймовірностям
+
+            якщо returnCoef is None, то генеруються випадкові ймовірності
+
+            якщо returnCoef is not None, то генеруються ймовірності за допомогою генетичного алгоритму
+
+          debug: параметр, який регулює інформативність функції
+
+        Returns:
+          згенерований список ймовірностей
+
+        """
+        probs = []
+        if returnCoef is None:
+            # generate random probabilities
+            high = 1
+            e = 0.25 * high
+
+            for _ in range(probsCount - 1):
+                probs.append(np.random.uniform(0, high - e))
+                high -= probs[-1]
+                e = 0.01 * high
+            probs.append(high)
+            return probs
+        else:
+            probs = self.genAlgo.geneticAlgorithm(probsCount=probsCount, goalValue=returnCoef, debug=debug)
+            return probs
+
+    def getReturnCoef(self, gamesCount: int = 10 ** 4, probs: Optional[List[float, ...]] = None,
+                      isGraph: bool = False) -> Union[float, List[float]]:
+        """функція, яка рахує коефіцієнт віддачі автомата, моделюючи методом Монте-Карло gamesCount ігор.
+
+        функція рахує середній виграш гравця, протягом gamesCount ігор, та ділить його на ціну однієї гри
+
+        Args:
+          gamesCount: кількість ігор, які будуть промодельовані
+          probs: ймовірності зображень, з якими буде проводитись кожна гра
+          isGraph: параметр, який відповідає за надання функцією додаткових даних необхідних для створення графіку
+
+        Returns:
+            коефіцієнт віддачі автомату, якщо isGraph рівне False
+
+            список коефіцієнтів віддачі автомату для кожної гри, якщо isGraph рівне True
+
+        """
+        oldProbs = self.probabilities
+        oldMoneySpent = self.moneySpent
+        oldMoneyWon = self.moneyWon
+        if probs is not None:
+            self.setProbabilities(probs)
+        self.moneyWon = 0
+        self.moneySpent = 0
+
+        money = self.price * gamesCount
+
+        modeling = True
+        self.startGame(money, modeling=modeling)
+        meanRCs = self.play(gamesCount, modeling=modeling, isGraph=isGraph)
+        gameReturn = self.moneyWon
+        gameReturnPercentage = gameReturn / self.moneySpent
+        if probs is not None:
+            self.setProbabilities(oldProbs)
+        self.moneyWon = oldMoneyWon
+        self.moneySpent = oldMoneySpent
+        if isGraph:
+            return meanRCs
+
+        return gameReturnPercentage
+
+    def getReturnCoefWithNCores(self, gamesCount: int = 10 ** 3, probs: Optional[List[float, ...]] = None,
+                                coresCount: int = -1):
+        """функція, яка прискорює обчислення коефіцієнту віддачі автомату для великої кількості ігор,
+        шляхом розпаралелювання роботи.
+
+        функція запускає coresCount процесів, де виконуються функції self.getReturnCoef,
+        але для меншого значення gamesCount, рівному gamesCount // coresCount.
+
+        Args:
+          gamesCount: кількість ігор, які будуть промодельовані
+          probs: ймовірності зображень, з якими буде проводитись кожна гра
+          coresCount: кількість процесів, які запустить дана функція
+
+        Returns:
+          коефіцієнт віддачі автомату
+
+        """
+        if coresCount == -1:
+            coresCount = int(os.cpu_count() * 0.75)
+            if coresCount == 0:
+                coresCount = 1
+        elif coresCount == 0:
+            return self.getReturnCoef(gamesCount, probs)
+
+        gamesCountList = [gamesCount // coresCount, ] * coresCount
+        gamesCountList[-1] += (gamesCount - sum(gamesCountList))
+
+        args = zip(gamesCountList, [probs, ] * len(gamesCountList), )
+        # args = list(args)
+
+        with Pool(processes=coresCount) as executor:
+            results = executor.starmap(self.getReturnCoef, args)
+
+        res = sum(results) / coresCount
+        return res
+
+    # @benchmark
+    def currentTurn(self, modeling: bool = False) -> int:
+        """функція, яка генерує матрицю однієї гри(складається з стовпців, кожен з яких містить в собі індекси зображень)
+        та викликає функцію self.setState
+
+        Args:
+          modeling: параметр, який регулює інформативність функції
+
+        Returns:
+          виграш гравця
+
+        """
+        self.setState([self.turnColumn(i) for i in range(self.columnsNumb)])
+        self.money -= self.price
+        self.moneySpent += self.price
+        won = self.currentWon()
+
+        if not modeling:
+            self.printState()
+
+        return won
+
+    def turnColumn(self, i) -> List[int,]:
+        """функція, яка генерує один стовпець для майбутньої матриці однієї гри
+
+        Args:
+          i: індекс стовпця, який також відповідає індексу стовпця з self.drumColumns,
+             де кожен стовпець містить індекси зображень, які взагалі можуть з'явитися в і-тому стовпці матриці однієї гри
+
+        Returns:
+          список індексів зображень
+
+        """
+        downIdx = self.rowsNumb - 1
+        upIdx = 0
+        rolledPicturesIdx = []
+        resColumn = [-1, ] * self.rowsNumb
+        for idx in range(self.rowsNumb):
+            newProbs = self.genTurnProbabilities(rolledPicturesIdxs=rolledPicturesIdx)
+
+            # assert abs(sum(newProbs) - 1) < 0.001
+
+            rolledPicIdx = np.random.choice(self.drumsColumns[i], size=1, p=newProbs)[0]
+            rolledPicturesIdx.append(rolledPicIdx)
+
+            if (idx % 2) == 0:
+                resColumn[downIdx] = rolledPicIdx
+                downIdx -= 1
+            else:
+                resColumn[upIdx] = rolledPicIdx
+                upIdx += 1
+
+        return resColumn
+
+    def genTurnProbabilities(self, rolledPicturesIdxs: List[int, ...]) -> List:
+        """функція, яка обраховує ймовірності для зображень у автоматі при генерації одного стовпчика,
+        точніше описано в файлі README.ipynb
+
+        Args:
+          rolledPicturesIdxs: зображення, які уже були згенеровані у рядках одного стовпчика
+
+        Returns:
+          значення ймовірностей для наступної генерації
+
+        """
+        newProbs = np.array(self.probabilities, dtype=float)
+        divideCoeff = 1 - sum([self.probabilities[idx] for idx in rolledPicturesIdxs])
+        newProbs[rolledPicturesIdxs] = 0.0
+        newProbs = newProbs / divideCoeff
+        return list(newProbs)
+
+    def currentWon(self) -> int:
+        """функція, яка аналізує поточну матрицю гри та повертає весь виграш гравця за поточну гру
+
+        Args:
+
+        Returns:
+          весь виграш гравця
+
+        """
+        won = 0
+        for i in range(self.rowsNumb):
+            comb = tuple(self.getRow(i))
+            if comb in self.combinations:
+                # it is possible to have several winning combinations....
+                won += self.combinations[comb]
+
+        self.moneyWon += won  # ???? not sure if it is needed
+        return won
+
+    def getRow(self, i) -> np.array:
+        """функція, яка повертає і-ий рядок матриці self.state(матриці однієї гри)
+
+        Args:
+          i: індекс рядка матриці гри
+
+        Returns:
+          список індексів зображень
+
+        """
+        return np.array([self.state[j][i] for j in range(self.columnsNumb)])
+
+    def getColumn(self, j):
+        """функція, яка повертаю j-ий стовпець матриці self.state(матриці однієї гри)
+
+        Args:
+          j: індекс стовпця матриці гри
+
+        Returns:
+          список індексів зображень
+
+        """
+        return self.state[j]
+
+    def getState(self) -> np.array:
+        """функція, яка повертає матрицю self.state(матрицю однієї гри)
+
+        Args:
+
+        Returns:
+          транспонована(задля спрощення її відображення) матриця self.state
+
+        """
+        return np.array(self.state).T
+
+    def setState(self, state: List[List[int,],]) -> None:
+        """функція, яка встановлює матрицю однієї гри
+
+        Args:
+          state: матриця індексів зображень однієї гри
+
+        Returns:
+
+        """
+        if all([len(col) == len(set(col)) for col in state]):
+            self.state = state
+        else:
+            raise SettingStateException
+
+    def printState(self) -> None:
+        """функція, яка відображає матрицю поточної гри, весь виграш гравця, гроші, які у нього залишилися"""
+        print(f"current state: \n{self.getState()}")
+        # print("money spent:", self.moneySpent)
+        print("money won:", self.moneyWon)
+        print("money remained:", self.money)
+
+    def play(self, n, modeling: bool = False, isGraph: bool = False) -> Union[Optional[List[int,]],
+                                                                              List[float]]:
+        """функція, яка проводить n ігор
+
+        Args:
+          n: кількість ігор
+          modeling: параметр, який впливає на інформативність функції
+          isGraph: параметра, який керує поверненням спеціальних значень
+
+        Returns:
+          список виграшів гравця за n ігор, якщо isGraph рівне False
+
+          список коефіцієнтів віддачі автомату для кожної з n ігор, якщо isGraph рівне True
+
+        """
+        if self.money < self.price * n:
+            print("Not enough money")
+            return None
+
+        wons = []
+        meanRCs = []
+        # play game n times
+        for i in range(n):
+            won = self.currentTurn(modeling=modeling)
+            wons.append(won)
+            if not modeling:
+                print(f"you won {won} money for one turn")
+                print(f"you spent {self.price} money for one turn")
+                print()
+
+            if isGraph:
+                meanRCs.append(sum(wons) / ((i + 1) * self.price))
+
+        if isGraph:
+            return meanRCs
+
+        if not modeling:
+            return wons
+
+    # winning sum in fact could be (and often is) negative ;-))
+    @property
+    def currentPlayerWon(self):
+        """функція, яка декорується, як властивість об'єкту та повертає весь виграш гравця,
+        віднявши від нього витрати гравця
+
+        Args:
+
+        Returns:
+          виграш гравця - витрати гравця
+
+        """
+        return self.moneyWon - self.moneySpent  # not sure
+
+    @property
+    def totalPlayersWon(self):
+        """функція, яка декорується, як властивість об'єкту та повертає весь виграш всіх гравців,
+        який вона обраховує, використовуючи функцію self.currentPlayerWon
+
+        Args:
+
+        Returns:
+          виграш всіх гравців - витрати всіх гравців
+
+        """
+        totalWin = 0
+        for p in OneHandBandit.players:
+            totalWin += p.currentPlayerWon
+        return totalWin
+
+
+if __name__ == "__main__":
+    p2 = OneHandBandit(4, 3, 5)
+    # p2.setProbabilitiesFromTxtFile("probabilities.txt")
+
+    p2.setPriceOfGame(10)
+    p2.setWinningCombs({(0, 0, 0, 0): 15, (1, 1, 1, 1): 15, (2, 2, 2, 2): 15, (3, 3, 3, 3): 15, (1, 2, 3, 4): 20, })
+    p2.addWinningComb((4, 4, 4, 4), 15)
+
+    # p2.startGame(100)
+    # p2.play(10)
+    try:
+        returnCoef = float(
+            input("write down return coefficient for probabilities you would like get for(Example 0.9): "))
+    except Exception as e:
+        print("during input error occurred:", e)
+        print("return coefficient set to 0.9")
+        returnCoef = 0.9
+    # np.random.seed(42)
+    probs = p2.genBaseProbabilities(p2.picturesNumb, returnCoef, debug=True)
+    print(f"{probs=}")
+    print(f"fitness val for probs and goalValue={returnCoef}:",
+          p2.genAlgo.fitness_func(probs=probs, goalValue=returnCoef))
+    print("return coef (goalValue):", p2.getReturnCoefWithNCores(probs=probs, gamesCount=10 ** 4))
+
+    probsStrs = " ".join(map(str, probs))
+    with open("probabilities.txt", "w") as f:
+        f.write(probsStrs)
+    # modelingGamesCount = 2000
+    #
+    # print(p2.genBaseProbabilities(5, returnCoef=0.8))
+    # p2.getReturnCoefWithNCores(modelingGamesCount)
+    # l = []
+    # t0 = time.time()
+    # for i in range(10):
+    #
+    #     returnCoef = p2.getReturnCoefWithNCores(gamesCount=modelingGamesCount, coresCount=5)
+    #     l.append(returnCoef)
+    #     print(f"{i}) this one hand bandit return coefficient: {returnCoef} "
+    #           f"for {modelingGamesCount} games")
+    # print("min max range:", max(l) - min(l))
+    # print(time.time() - t0, "secs")
+
+# метод ньютона
+
+# генетичний алгоритм(рандомна мутація)
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class OneHandBandit +(m: int, n: int, k: int, probs: Optional[List[float, ...]] = None) +
+
+

функція конструктор для класу OneHandBandit

+

Args

+
+
m
+
number of columns
+
n
+
number of rows
+
k
+
+

number of pictures

+

k(number of pictures) must be greater or equal to n(number of rows)

+
+
probs
+
probability of each picture appearance
+
+
+ +Expand source code + +
class OneHandBandit:
+    """ """
+    players: List[OneHandBandit] = []
+
+    def __init__(self, m: int, n: int, k: int, probs: Optional[List[float, ...]] = None):
+        """функція конструктор для класу OneHandBandit
+        Args:
+            m: number of columns
+            n: number of rows
+            k: number of pictures
+
+                k(number of pictures) must be greater or equal to n(number of rows)
+
+            probs: probability of each picture appearance
+        """
+
+        self.drumsColumns = [[i for i in range(k)] for _ in range(m)]
+        self.columnsNumb = m
+        self.rowsNumb = n
+        self.picturesNumb = k
+
+        self.probabilities: List[float, ...] = list()
+        self.setProbabilities(probs)
+
+        self.combinations: Dict[Tuple[int, ...], int] = dict()
+        self.price: int = 0
+
+        self.money: int = 0
+        self.moneySpent = 0
+        self.moneyWon: int = 0
+
+        self.state: List[List[int,],] = list()
+
+        self.genAlgo = GeneticAlgorithm(self)
+
+        OneHandBandit.players.append(self)
+
+    def setWinningCombs(self, combs: Dict[Tuple[int, ...], int]):
+        """функція, яка встановлює виграшні комбінації
+
+        Args:
+          combs: dict of winning combinations
+
+            example: { (7, 7, 7): 100, (3, 5, 7): 200, (4, 3, 0): 500 }
+
+        Returns:
+
+        """
+        combs = combs.copy()
+        for comb in combs:
+            if len(comb) != self.columnsNumb:
+                raise CombinationLengthException
+            elif max(comb) > (self.picturesNumb - 1) or min(comb) < 0:
+                raise CombinationValuesException
+            elif combs[comb] < 0:
+                raise WinningMoneyException
+
+        self.combinations = combs
+
+    def addWinningComb(self, comb: Tuple[int, ...], win: int) -> None:
+        """функція, яка додає одну виграшну комбінацію до списку виграшних комбінацій
+
+        Args:
+          comb: комбінація індексів зображень
+          win: виграшна сума
+
+        Returns:
+
+        """
+        self.combinations[comb] = win
+
+    # the price of 1 turn
+    def setPriceOfGame(self, price) -> None:
+        """функція, яка встановлює вартість однієї гри
+
+        Args:
+          price: вартість однієї гри
+
+        Returns:
+
+        """
+        self.price = price
+
+    # money player gives to bandit
+    def startGame(self, money: int, modeling: bool = False) -> None:
+        """функція, яка вносить до автомату певну суму грошей, на які можна грати
+
+        Args:
+          money: сума грошей, яка вноситься до автомату
+          modeling: параметр, який регулює інформативність функції
+
+        Returns:
+
+        """
+        if money > 0:
+            self.money = money
+            if not modeling:
+                print(f"you deposited {money} money")
+
+    def setProbabilities(self, probs: Optional[List[float, ...]] = None) -> None:
+        """функція, яка встановлює ймовірності для відповідних зображень з індексами від 0 до self.picturesNumb - 1,
+        слідкуючи за тим, щоб сума ймовірностей була рівною одиниці
+
+        Args:
+          probs: ймовірності зображень
+
+        Returns:
+
+        """
+        if probs is None:
+            # uniform distribution
+            probs = [1 / self.picturesNumb for i in range(self.picturesNumb)]
+
+        if 0.001 < abs(sum(probs) - 1.0):
+            # sum of probs dont equal 1
+            raise ProbabilitiesSumException
+
+        if len(probs) != self.picturesNumb:
+            raise ProbabilitiesArrayLengthException
+
+        self.probabilities = probs.copy()
+
+    def setProbabilitiesFromTxtFile(self, filename: str = "probabilities.txt") -> None:
+        """функція, яка читає ймовірності з файлу, які розділені одним пробілом та визиває функцію self.setProbabilities
+
+        Args:
+          filename: ім'я файлу з ймовірностями
+
+        Returns:
+
+        """
+        with open(filename, "r") as f:
+            probsLine = f.readline()
+            probs = list(map(lambda x: float(x), probsLine.split(" ")))
+
+        self.setProbabilities(probs)
+
+    def genBaseProbabilities(self, probsCount: int, returnCoef: Optional[float] = None,
+                             debug: bool = False) -> List[float]:
+        """функція, яка генерує список ймовірностей, сума яких рівна одиниці.
+
+        Можлива генерація випадкових ймовірностей
+        та генерація ймовірностей, які б задовольняли певний коефіцієнт віддачі автомату
+
+        Args:
+          probsCount: довжина списку ймовірностей
+          returnCoef: коефіцієнт віддачі автомату, який має задовольняти ймовірностям
+
+            якщо returnCoef is None, то генеруються випадкові ймовірності
+
+            якщо returnCoef is not None, то генеруються ймовірності за допомогою генетичного алгоритму
+
+          debug: параметр, який регулює інформативність функції
+
+        Returns:
+          згенерований список ймовірностей
+
+        """
+        probs = []
+        if returnCoef is None:
+            # generate random probabilities
+            high = 1
+            e = 0.25 * high
+
+            for _ in range(probsCount - 1):
+                probs.append(np.random.uniform(0, high - e))
+                high -= probs[-1]
+                e = 0.01 * high
+            probs.append(high)
+            return probs
+        else:
+            probs = self.genAlgo.geneticAlgorithm(probsCount=probsCount, goalValue=returnCoef, debug=debug)
+            return probs
+
+    def getReturnCoef(self, gamesCount: int = 10 ** 4, probs: Optional[List[float, ...]] = None,
+                      isGraph: bool = False) -> Union[float, List[float]]:
+        """функція, яка рахує коефіцієнт віддачі автомата, моделюючи методом Монте-Карло gamesCount ігор.
+
+        функція рахує середній виграш гравця, протягом gamesCount ігор, та ділить його на ціну однієї гри
+
+        Args:
+          gamesCount: кількість ігор, які будуть промодельовані
+          probs: ймовірності зображень, з якими буде проводитись кожна гра
+          isGraph: параметр, який відповідає за надання функцією додаткових даних необхідних для створення графіку
+
+        Returns:
+            коефіцієнт віддачі автомату, якщо isGraph рівне False
+
+            список коефіцієнтів віддачі автомату для кожної гри, якщо isGraph рівне True
+
+        """
+        oldProbs = self.probabilities
+        oldMoneySpent = self.moneySpent
+        oldMoneyWon = self.moneyWon
+        if probs is not None:
+            self.setProbabilities(probs)
+        self.moneyWon = 0
+        self.moneySpent = 0
+
+        money = self.price * gamesCount
+
+        modeling = True
+        self.startGame(money, modeling=modeling)
+        meanRCs = self.play(gamesCount, modeling=modeling, isGraph=isGraph)
+        gameReturn = self.moneyWon
+        gameReturnPercentage = gameReturn / self.moneySpent
+        if probs is not None:
+            self.setProbabilities(oldProbs)
+        self.moneyWon = oldMoneyWon
+        self.moneySpent = oldMoneySpent
+        if isGraph:
+            return meanRCs
+
+        return gameReturnPercentage
+
+    def getReturnCoefWithNCores(self, gamesCount: int = 10 ** 3, probs: Optional[List[float, ...]] = None,
+                                coresCount: int = -1):
+        """функція, яка прискорює обчислення коефіцієнту віддачі автомату для великої кількості ігор,
+        шляхом розпаралелювання роботи.
+
+        функція запускає coresCount процесів, де виконуються функції self.getReturnCoef,
+        але для меншого значення gamesCount, рівному gamesCount // coresCount.
+
+        Args:
+          gamesCount: кількість ігор, які будуть промодельовані
+          probs: ймовірності зображень, з якими буде проводитись кожна гра
+          coresCount: кількість процесів, які запустить дана функція
+
+        Returns:
+          коефіцієнт віддачі автомату
+
+        """
+        if coresCount == -1:
+            coresCount = int(os.cpu_count() * 0.75)
+            if coresCount == 0:
+                coresCount = 1
+        elif coresCount == 0:
+            return self.getReturnCoef(gamesCount, probs)
+
+        gamesCountList = [gamesCount // coresCount, ] * coresCount
+        gamesCountList[-1] += (gamesCount - sum(gamesCountList))
+
+        args = zip(gamesCountList, [probs, ] * len(gamesCountList), )
+        # args = list(args)
+
+        with Pool(processes=coresCount) as executor:
+            results = executor.starmap(self.getReturnCoef, args)
+
+        res = sum(results) / coresCount
+        return res
+
+    # @benchmark
+    def currentTurn(self, modeling: bool = False) -> int:
+        """функція, яка генерує матрицю однієї гри(складається з стовпців, кожен з яких містить в собі індекси зображень)
+        та викликає функцію self.setState
+
+        Args:
+          modeling: параметр, який регулює інформативність функції
+
+        Returns:
+          виграш гравця
+
+        """
+        self.setState([self.turnColumn(i) for i in range(self.columnsNumb)])
+        self.money -= self.price
+        self.moneySpent += self.price
+        won = self.currentWon()
+
+        if not modeling:
+            self.printState()
+
+        return won
+
+    def turnColumn(self, i) -> List[int,]:
+        """функція, яка генерує один стовпець для майбутньої матриці однієї гри
+
+        Args:
+          i: індекс стовпця, який також відповідає індексу стовпця з self.drumColumns,
+             де кожен стовпець містить індекси зображень, які взагалі можуть з'явитися в і-тому стовпці матриці однієї гри
+
+        Returns:
+          список індексів зображень
+
+        """
+        downIdx = self.rowsNumb - 1
+        upIdx = 0
+        rolledPicturesIdx = []
+        resColumn = [-1, ] * self.rowsNumb
+        for idx in range(self.rowsNumb):
+            newProbs = self.genTurnProbabilities(rolledPicturesIdxs=rolledPicturesIdx)
+
+            # assert abs(sum(newProbs) - 1) < 0.001
+
+            rolledPicIdx = np.random.choice(self.drumsColumns[i], size=1, p=newProbs)[0]
+            rolledPicturesIdx.append(rolledPicIdx)
+
+            if (idx % 2) == 0:
+                resColumn[downIdx] = rolledPicIdx
+                downIdx -= 1
+            else:
+                resColumn[upIdx] = rolledPicIdx
+                upIdx += 1
+
+        return resColumn
+
+    def genTurnProbabilities(self, rolledPicturesIdxs: List[int, ...]) -> List:
+        """функція, яка обраховує ймовірності для зображень у автоматі при генерації одного стовпчика,
+        точніше описано в файлі README.ipynb
+
+        Args:
+          rolledPicturesIdxs: зображення, які уже були згенеровані у рядках одного стовпчика
+
+        Returns:
+          значення ймовірностей для наступної генерації
+
+        """
+        newProbs = np.array(self.probabilities, dtype=float)
+        divideCoeff = 1 - sum([self.probabilities[idx] for idx in rolledPicturesIdxs])
+        newProbs[rolledPicturesIdxs] = 0.0
+        newProbs = newProbs / divideCoeff
+        return list(newProbs)
+
+    def currentWon(self) -> int:
+        """функція, яка аналізує поточну матрицю гри та повертає весь виграш гравця за поточну гру
+
+        Args:
+
+        Returns:
+          весь виграш гравця
+
+        """
+        won = 0
+        for i in range(self.rowsNumb):
+            comb = tuple(self.getRow(i))
+            if comb in self.combinations:
+                # it is possible to have several winning combinations....
+                won += self.combinations[comb]
+
+        self.moneyWon += won  # ???? not sure if it is needed
+        return won
+
+    def getRow(self, i) -> np.array:
+        """функція, яка повертає і-ий рядок матриці self.state(матриці однієї гри)
+
+        Args:
+          i: індекс рядка матриці гри
+
+        Returns:
+          список індексів зображень
+
+        """
+        return np.array([self.state[j][i] for j in range(self.columnsNumb)])
+
+    def getColumn(self, j):
+        """функція, яка повертаю j-ий стовпець матриці self.state(матриці однієї гри)
+
+        Args:
+          j: індекс стовпця матриці гри
+
+        Returns:
+          список індексів зображень
+
+        """
+        return self.state[j]
+
+    def getState(self) -> np.array:
+        """функція, яка повертає матрицю self.state(матрицю однієї гри)
+
+        Args:
+
+        Returns:
+          транспонована(задля спрощення її відображення) матриця self.state
+
+        """
+        return np.array(self.state).T
+
+    def setState(self, state: List[List[int,],]) -> None:
+        """функція, яка встановлює матрицю однієї гри
+
+        Args:
+          state: матриця індексів зображень однієї гри
+
+        Returns:
+
+        """
+        if all([len(col) == len(set(col)) for col in state]):
+            self.state = state
+        else:
+            raise SettingStateException
+
+    def printState(self) -> None:
+        """функція, яка відображає матрицю поточної гри, весь виграш гравця, гроші, які у нього залишилися"""
+        print(f"current state: \n{self.getState()}")
+        # print("money spent:", self.moneySpent)
+        print("money won:", self.moneyWon)
+        print("money remained:", self.money)
+
+    def play(self, n, modeling: bool = False, isGraph: bool = False) -> Union[Optional[List[int,]],
+                                                                              List[float]]:
+        """функція, яка проводить n ігор
+
+        Args:
+          n: кількість ігор
+          modeling: параметр, який впливає на інформативність функції
+          isGraph: параметра, який керує поверненням спеціальних значень
+
+        Returns:
+          список виграшів гравця за n ігор, якщо isGraph рівне False
+
+          список коефіцієнтів віддачі автомату для кожної з n ігор, якщо isGraph рівне True
+
+        """
+        if self.money < self.price * n:
+            print("Not enough money")
+            return None
+
+        wons = []
+        meanRCs = []
+        # play game n times
+        for i in range(n):
+            won = self.currentTurn(modeling=modeling)
+            wons.append(won)
+            if not modeling:
+                print(f"you won {won} money for one turn")
+                print(f"you spent {self.price} money for one turn")
+                print()
+
+            if isGraph:
+                meanRCs.append(sum(wons) / ((i + 1) * self.price))
+
+        if isGraph:
+            return meanRCs
+
+        if not modeling:
+            return wons
+
+    # winning sum in fact could be (and often is) negative ;-))
+    @property
+    def currentPlayerWon(self):
+        """функція, яка декорується, як властивість об'єкту та повертає весь виграш гравця,
+        віднявши від нього витрати гравця
+
+        Args:
+
+        Returns:
+          виграш гравця - витрати гравця
+
+        """
+        return self.moneyWon - self.moneySpent  # not sure
+
+    @property
+    def totalPlayersWon(self):
+        """функція, яка декорується, як властивість об'єкту та повертає весь виграш всіх гравців,
+        який вона обраховує, використовуючи функцію self.currentPlayerWon
+
+        Args:
+
+        Returns:
+          виграш всіх гравців - витрати всіх гравців
+
+        """
+        totalWin = 0
+        for p in OneHandBandit.players:
+            totalWin += p.currentPlayerWon
+        return totalWin
+
+

Class variables

+
+
var players : List[OneHandBandit]
+
+
+
+
+

Instance variables

+
+
var currentPlayerWon
+
+

функція, яка декорується, як властивість об'єкту та повертає весь виграш гравця, +віднявши від нього витрати гравця

+

Args:

+

Returns

+

виграш гравця - витрати гравця

+
+ +Expand source code + +
@property
+def currentPlayerWon(self):
+    """функція, яка декорується, як властивість об'єкту та повертає весь виграш гравця,
+    віднявши від нього витрати гравця
+
+    Args:
+
+    Returns:
+      виграш гравця - витрати гравця
+
+    """
+    return self.moneyWon - self.moneySpent  # not sure
+
+
+
var totalPlayersWon
+
+

функція, яка декорується, як властивість об'єкту та повертає весь виграш всіх гравців, +який вона обраховує, використовуючи функцію self.currentPlayerWon

+

Args:

+

Returns

+

виграш всіх гравців - витрати всіх гравців

+
+ +Expand source code + +
@property
+def totalPlayersWon(self):
+    """функція, яка декорується, як властивість об'єкту та повертає весь виграш всіх гравців,
+    який вона обраховує, використовуючи функцію self.currentPlayerWon
+
+    Args:
+
+    Returns:
+      виграш всіх гравців - витрати всіх гравців
+
+    """
+    totalWin = 0
+    for p in OneHandBandit.players:
+        totalWin += p.currentPlayerWon
+    return totalWin
+
+
+
+

Methods

+
+
+def addWinningComb(self, comb: Tuple[int, ...], win: int) ‑> None +
+
+

функція, яка додає одну виграшну комбінацію до списку виграшних комбінацій

+

Args

+
+
comb
+
комбінація індексів зображень
+
win
+
виграшна сума
+
+

Returns:

+
+ +Expand source code + +
def addWinningComb(self, comb: Tuple[int, ...], win: int) -> None:
+    """функція, яка додає одну виграшну комбінацію до списку виграшних комбінацій
+
+    Args:
+      comb: комбінація індексів зображень
+      win: виграшна сума
+
+    Returns:
+
+    """
+    self.combinations[comb] = win
+
+
+
+def currentTurn(self, modeling: bool = False) ‑> int +
+
+

функція, яка генерує матрицю однієї гри(складається з стовпців, кожен з яких містить в собі індекси зображень) +та викликає функцію self.setState

+

Args

+
+
modeling
+
параметр, який регулює інформативність функції
+
+

Returns

+

виграш гравця

+
+ +Expand source code + +
def currentTurn(self, modeling: bool = False) -> int:
+    """функція, яка генерує матрицю однієї гри(складається з стовпців, кожен з яких містить в собі індекси зображень)
+    та викликає функцію self.setState
+
+    Args:
+      modeling: параметр, який регулює інформативність функції
+
+    Returns:
+      виграш гравця
+
+    """
+    self.setState([self.turnColumn(i) for i in range(self.columnsNumb)])
+    self.money -= self.price
+    self.moneySpent += self.price
+    won = self.currentWon()
+
+    if not modeling:
+        self.printState()
+
+    return won
+
+
+
+def currentWon(self) ‑> int +
+
+

функція, яка аналізує поточну матрицю гри та повертає весь виграш гравця за поточну гру

+

Args:

+

Returns

+

весь виграш гравця

+
+ +Expand source code + +
def currentWon(self) -> int:
+    """функція, яка аналізує поточну матрицю гри та повертає весь виграш гравця за поточну гру
+
+    Args:
+
+    Returns:
+      весь виграш гравця
+
+    """
+    won = 0
+    for i in range(self.rowsNumb):
+        comb = tuple(self.getRow(i))
+        if comb in self.combinations:
+            # it is possible to have several winning combinations....
+            won += self.combinations[comb]
+
+    self.moneyWon += won  # ???? not sure if it is needed
+    return won
+
+
+
+def genBaseProbabilities(self, probsCount: int, returnCoef: Optional[float] = None, debug: bool = False) ‑> List[float] +
+
+

функція, яка генерує список ймовірностей, сума яких рівна одиниці.

+

Можлива генерація випадкових ймовірностей +та генерація ймовірностей, які б задовольняли певний коефіцієнт віддачі автомату

+

Args

+
+
probsCount
+
довжина списку ймовірностей
+
returnCoef
+
+

коефіцієнт віддачі автомату, який має задовольняти ймовірностям

+

якщо returnCoef is None, то генеруються випадкові ймовірності

+

якщо returnCoef is not None, то генеруються ймовірності за допомогою генетичного алгоритму

+
+
debug
+
параметр, який регулює інформативність функції
+
+

Returns

+

згенерований список ймовірностей

+
+ +Expand source code + +
def genBaseProbabilities(self, probsCount: int, returnCoef: Optional[float] = None,
+                         debug: bool = False) -> List[float]:
+    """функція, яка генерує список ймовірностей, сума яких рівна одиниці.
+
+    Можлива генерація випадкових ймовірностей
+    та генерація ймовірностей, які б задовольняли певний коефіцієнт віддачі автомату
+
+    Args:
+      probsCount: довжина списку ймовірностей
+      returnCoef: коефіцієнт віддачі автомату, який має задовольняти ймовірностям
+
+        якщо returnCoef is None, то генеруються випадкові ймовірності
+
+        якщо returnCoef is not None, то генеруються ймовірності за допомогою генетичного алгоритму
+
+      debug: параметр, який регулює інформативність функції
+
+    Returns:
+      згенерований список ймовірностей
+
+    """
+    probs = []
+    if returnCoef is None:
+        # generate random probabilities
+        high = 1
+        e = 0.25 * high
+
+        for _ in range(probsCount - 1):
+            probs.append(np.random.uniform(0, high - e))
+            high -= probs[-1]
+            e = 0.01 * high
+        probs.append(high)
+        return probs
+    else:
+        probs = self.genAlgo.geneticAlgorithm(probsCount=probsCount, goalValue=returnCoef, debug=debug)
+        return probs
+
+
+
+def genTurnProbabilities(self, rolledPicturesIdxs: List[int, ...]) ‑> List +
+
+

функція, яка обраховує ймовірності для зображень у автоматі при генерації одного стовпчика, +точніше описано в файлі README.ipynb

+

Args

+
+
rolledPicturesIdxs
+
зображення, які уже були згенеровані у рядках одного стовпчика
+
+

Returns

+

значення ймовірностей для наступної генерації

+
+ +Expand source code + +
def genTurnProbabilities(self, rolledPicturesIdxs: List[int, ...]) -> List:
+    """функція, яка обраховує ймовірності для зображень у автоматі при генерації одного стовпчика,
+    точніше описано в файлі README.ipynb
+
+    Args:
+      rolledPicturesIdxs: зображення, які уже були згенеровані у рядках одного стовпчика
+
+    Returns:
+      значення ймовірностей для наступної генерації
+
+    """
+    newProbs = np.array(self.probabilities, dtype=float)
+    divideCoeff = 1 - sum([self.probabilities[idx] for idx in rolledPicturesIdxs])
+    newProbs[rolledPicturesIdxs] = 0.0
+    newProbs = newProbs / divideCoeff
+    return list(newProbs)
+
+
+
+def getColumn(self, j) +
+
+

функція, яка повертаю j-ий стовпець матриці self.state(матриці однієї гри)

+

Args

+
+
j
+
індекс стовпця матриці гри
+
+

Returns

+

список індексів зображень

+
+ +Expand source code + +
def getColumn(self, j):
+    """функція, яка повертаю j-ий стовпець матриці self.state(матриці однієї гри)
+
+    Args:
+      j: індекс стовпця матриці гри
+
+    Returns:
+      список індексів зображень
+
+    """
+    return self.state[j]
+
+
+
+def getReturnCoef(self, gamesCount: int = 10000, probs: Optional[List[float, ...]] = None, isGraph: bool = False) ‑> Union[float, List[float]] +
+
+

функція, яка рахує коефіцієнт віддачі автомата, моделюючи методом Монте-Карло gamesCount ігор.

+

функція рахує середній виграш гравця, протягом gamesCount ігор, та ділить його на ціну однієї гри

+

Args

+
+
gamesCount
+
кількість ігор, які будуть промодельовані
+
probs
+
ймовірності зображень, з якими буде проводитись кожна гра
+
isGraph
+
параметр, який відповідає за надання функцією додаткових даних необхідних для створення графіку
+
+

Returns

+

коефіцієнт віддачі автомату, якщо isGraph рівне False

+

список коефіцієнтів віддачі автомату для кожної гри, якщо isGraph рівне True

+
+ +Expand source code + +
def getReturnCoef(self, gamesCount: int = 10 ** 4, probs: Optional[List[float, ...]] = None,
+                  isGraph: bool = False) -> Union[float, List[float]]:
+    """функція, яка рахує коефіцієнт віддачі автомата, моделюючи методом Монте-Карло gamesCount ігор.
+
+    функція рахує середній виграш гравця, протягом gamesCount ігор, та ділить його на ціну однієї гри
+
+    Args:
+      gamesCount: кількість ігор, які будуть промодельовані
+      probs: ймовірності зображень, з якими буде проводитись кожна гра
+      isGraph: параметр, який відповідає за надання функцією додаткових даних необхідних для створення графіку
+
+    Returns:
+        коефіцієнт віддачі автомату, якщо isGraph рівне False
+
+        список коефіцієнтів віддачі автомату для кожної гри, якщо isGraph рівне True
+
+    """
+    oldProbs = self.probabilities
+    oldMoneySpent = self.moneySpent
+    oldMoneyWon = self.moneyWon
+    if probs is not None:
+        self.setProbabilities(probs)
+    self.moneyWon = 0
+    self.moneySpent = 0
+
+    money = self.price * gamesCount
+
+    modeling = True
+    self.startGame(money, modeling=modeling)
+    meanRCs = self.play(gamesCount, modeling=modeling, isGraph=isGraph)
+    gameReturn = self.moneyWon
+    gameReturnPercentage = gameReturn / self.moneySpent
+    if probs is not None:
+        self.setProbabilities(oldProbs)
+    self.moneyWon = oldMoneyWon
+    self.moneySpent = oldMoneySpent
+    if isGraph:
+        return meanRCs
+
+    return gameReturnPercentage
+
+
+
+def getReturnCoefWithNCores(self, gamesCount: int = 1000, probs: Optional[List[float, ...]] = None, coresCount: int = -1) +
+
+

функція, яка прискорює обчислення коефіцієнту віддачі автомату для великої кількості ігор, +шляхом розпаралелювання роботи.

+

функція запускає coresCount процесів, де виконуються функції self.getReturnCoef, +але для меншого значення gamesCount, рівному gamesCount // coresCount.

+

Args

+
+
gamesCount
+
кількість ігор, які будуть промодельовані
+
probs
+
ймовірності зображень, з якими буде проводитись кожна гра
+
coresCount
+
кількість процесів, які запустить дана функція
+
+

Returns

+

коефіцієнт віддачі автомату

+
+ +Expand source code + +
def getReturnCoefWithNCores(self, gamesCount: int = 10 ** 3, probs: Optional[List[float, ...]] = None,
+                            coresCount: int = -1):
+    """функція, яка прискорює обчислення коефіцієнту віддачі автомату для великої кількості ігор,
+    шляхом розпаралелювання роботи.
+
+    функція запускає coresCount процесів, де виконуються функції self.getReturnCoef,
+    але для меншого значення gamesCount, рівному gamesCount // coresCount.
+
+    Args:
+      gamesCount: кількість ігор, які будуть промодельовані
+      probs: ймовірності зображень, з якими буде проводитись кожна гра
+      coresCount: кількість процесів, які запустить дана функція
+
+    Returns:
+      коефіцієнт віддачі автомату
+
+    """
+    if coresCount == -1:
+        coresCount = int(os.cpu_count() * 0.75)
+        if coresCount == 0:
+            coresCount = 1
+    elif coresCount == 0:
+        return self.getReturnCoef(gamesCount, probs)
+
+    gamesCountList = [gamesCount // coresCount, ] * coresCount
+    gamesCountList[-1] += (gamesCount - sum(gamesCountList))
+
+    args = zip(gamesCountList, [probs, ] * len(gamesCountList), )
+    # args = list(args)
+
+    with Pool(processes=coresCount) as executor:
+        results = executor.starmap(self.getReturnCoef, args)
+
+    res = sum(results) / coresCount
+    return res
+
+
+
+def getRow(self, i) ‑>  +
+
+

функція, яка повертає і-ий рядок матриці self.state(матриці однієї гри)

+

Args

+
+
i
+
індекс рядка матриці гри
+
+

Returns

+

список індексів зображень

+
+ +Expand source code + +
def getRow(self, i) -> np.array:
+    """функція, яка повертає і-ий рядок матриці self.state(матриці однієї гри)
+
+    Args:
+      i: індекс рядка матриці гри
+
+    Returns:
+      список індексів зображень
+
+    """
+    return np.array([self.state[j][i] for j in range(self.columnsNumb)])
+
+
+
+def getState(self) ‑>  +
+
+

функція, яка повертає матрицю self.state(матрицю однієї гри)

+

Args:

+

Returns

+

транспонована(задля спрощення її відображення) матриця self.state

+
+ +Expand source code + +
def getState(self) -> np.array:
+    """функція, яка повертає матрицю self.state(матрицю однієї гри)
+
+    Args:
+
+    Returns:
+      транспонована(задля спрощення її відображення) матриця self.state
+
+    """
+    return np.array(self.state).T
+
+
+
+def play(self, n, modeling: bool = False, isGraph: bool = False) ‑> Union[List[int], None, List[float]] +
+
+

функція, яка проводить n ігор

+

Args

+
+
n
+
кількість ігор
+
modeling
+
параметр, який впливає на інформативність функції
+
isGraph
+
параметра, який керує поверненням спеціальних значень
+
+

Returns

+

список виграшів гравця за n ігор, якщо isGraph рівне False

+

список коефіцієнтів віддачі автомату для кожної з n ігор, якщо isGraph рівне True

+
+ +Expand source code + +
def play(self, n, modeling: bool = False, isGraph: bool = False) -> Union[Optional[List[int,]],
+                                                                          List[float]]:
+    """функція, яка проводить n ігор
+
+    Args:
+      n: кількість ігор
+      modeling: параметр, який впливає на інформативність функції
+      isGraph: параметра, який керує поверненням спеціальних значень
+
+    Returns:
+      список виграшів гравця за n ігор, якщо isGraph рівне False
+
+      список коефіцієнтів віддачі автомату для кожної з n ігор, якщо isGraph рівне True
+
+    """
+    if self.money < self.price * n:
+        print("Not enough money")
+        return None
+
+    wons = []
+    meanRCs = []
+    # play game n times
+    for i in range(n):
+        won = self.currentTurn(modeling=modeling)
+        wons.append(won)
+        if not modeling:
+            print(f"you won {won} money for one turn")
+            print(f"you spent {self.price} money for one turn")
+            print()
+
+        if isGraph:
+            meanRCs.append(sum(wons) / ((i + 1) * self.price))
+
+    if isGraph:
+        return meanRCs
+
+    if not modeling:
+        return wons
+
+
+
+def printState(self) ‑> None +
+
+

функція, яка відображає матрицю поточної гри, весь виграш гравця, гроші, які у нього залишилися

+
+ +Expand source code + +
def printState(self) -> None:
+    """функція, яка відображає матрицю поточної гри, весь виграш гравця, гроші, які у нього залишилися"""
+    print(f"current state: \n{self.getState()}")
+    # print("money spent:", self.moneySpent)
+    print("money won:", self.moneyWon)
+    print("money remained:", self.money)
+
+
+
+def setPriceOfGame(self, price) ‑> None +
+
+

функція, яка встановлює вартість однієї гри

+

Args

+
+
price
+
вартість однієї гри
+
+

Returns:

+
+ +Expand source code + +
def setPriceOfGame(self, price) -> None:
+    """функція, яка встановлює вартість однієї гри
+
+    Args:
+      price: вартість однієї гри
+
+    Returns:
+
+    """
+    self.price = price
+
+
+
+def setProbabilities(self, probs: Optional[List[float, ...]] = None) ‑> None +
+
+

функція, яка встановлює ймовірності для відповідних зображень з індексами від 0 до self.picturesNumb - 1, +слідкуючи за тим, щоб сума ймовірностей була рівною одиниці

+

Args

+
+
probs
+
ймовірності зображень
+
+

Returns:

+
+ +Expand source code + +
def setProbabilities(self, probs: Optional[List[float, ...]] = None) -> None:
+    """функція, яка встановлює ймовірності для відповідних зображень з індексами від 0 до self.picturesNumb - 1,
+    слідкуючи за тим, щоб сума ймовірностей була рівною одиниці
+
+    Args:
+      probs: ймовірності зображень
+
+    Returns:
+
+    """
+    if probs is None:
+        # uniform distribution
+        probs = [1 / self.picturesNumb for i in range(self.picturesNumb)]
+
+    if 0.001 < abs(sum(probs) - 1.0):
+        # sum of probs dont equal 1
+        raise ProbabilitiesSumException
+
+    if len(probs) != self.picturesNumb:
+        raise ProbabilitiesArrayLengthException
+
+    self.probabilities = probs.copy()
+
+
+
+def setProbabilitiesFromTxtFile(self, filename: str = 'probabilities.txt') ‑> None +
+
+

функція, яка читає ймовірності з файлу, які розділені одним пробілом та визиває функцію self.setProbabilities

+

Args

+
+
filename
+
ім'я файлу з ймовірностями
+
+

Returns:

+
+ +Expand source code + +
def setProbabilitiesFromTxtFile(self, filename: str = "probabilities.txt") -> None:
+    """функція, яка читає ймовірності з файлу, які розділені одним пробілом та визиває функцію self.setProbabilities
+
+    Args:
+      filename: ім'я файлу з ймовірностями
+
+    Returns:
+
+    """
+    with open(filename, "r") as f:
+        probsLine = f.readline()
+        probs = list(map(lambda x: float(x), probsLine.split(" ")))
+
+    self.setProbabilities(probs)
+
+
+
+def setState(self, state: List[List[int,],]) ‑> None +
+
+

функція, яка встановлює матрицю однієї гри

+

Args

+
+
state
+
матриця індексів зображень однієї гри
+
+

Returns:

+
+ +Expand source code + +
def setState(self, state: List[List[int,],]) -> None:
+    """функція, яка встановлює матрицю однієї гри
+
+    Args:
+      state: матриця індексів зображень однієї гри
+
+    Returns:
+
+    """
+    if all([len(col) == len(set(col)) for col in state]):
+        self.state = state
+    else:
+        raise SettingStateException
+
+
+
+def setWinningCombs(self, combs: Dict[Tuple[int, ...], int]) +
+
+

функція, яка встановлює виграшні комбінації

+

Args

+
+
combs
+
+

dict of winning combinations

+

example: { (7, 7, 7): 100, (3, 5, 7): 200, (4, 3, 0): 500 }

+
+
+

Returns:

+
+ +Expand source code + +
def setWinningCombs(self, combs: Dict[Tuple[int, ...], int]):
+    """функція, яка встановлює виграшні комбінації
+
+    Args:
+      combs: dict of winning combinations
+
+        example: { (7, 7, 7): 100, (3, 5, 7): 200, (4, 3, 0): 500 }
+
+    Returns:
+
+    """
+    combs = combs.copy()
+    for comb in combs:
+        if len(comb) != self.columnsNumb:
+            raise CombinationLengthException
+        elif max(comb) > (self.picturesNumb - 1) or min(comb) < 0:
+            raise CombinationValuesException
+        elif combs[comb] < 0:
+            raise WinningMoneyException
+
+    self.combinations = combs
+
+
+
+def startGame(self, money: int, modeling: bool = False) ‑> None +
+
+

функція, яка вносить до автомату певну суму грошей, на які можна грати

+

Args

+
+
money
+
сума грошей, яка вноситься до автомату
+
modeling
+
параметр, який регулює інформативність функції
+
+

Returns:

+
+ +Expand source code + +
def startGame(self, money: int, modeling: bool = False) -> None:
+    """функція, яка вносить до автомату певну суму грошей, на які можна грати
+
+    Args:
+      money: сума грошей, яка вноситься до автомату
+      modeling: параметр, який регулює інформативність функції
+
+    Returns:
+
+    """
+    if money > 0:
+        self.money = money
+        if not modeling:
+            print(f"you deposited {money} money")
+
+
+
+def turnColumn(self, i) ‑> List[int] +
+
+

функція, яка генерує один стовпець для майбутньої матриці однієї гри

+

Args

+
+
i
+
індекс стовпця, який також відповідає індексу стовпця з self.drumColumns, +де кожен стовпець містить індекси зображень, які взагалі можуть з'явитися в і-тому стовпці матриці однієї гри
+
+

Returns

+

список індексів зображень

+
+ +Expand source code + +
def turnColumn(self, i) -> List[int,]:
+    """функція, яка генерує один стовпець для майбутньої матриці однієї гри
+
+    Args:
+      i: індекс стовпця, який також відповідає індексу стовпця з self.drumColumns,
+         де кожен стовпець містить індекси зображень, які взагалі можуть з'явитися в і-тому стовпці матриці однієї гри
+
+    Returns:
+      список індексів зображень
+
+    """
+    downIdx = self.rowsNumb - 1
+    upIdx = 0
+    rolledPicturesIdx = []
+    resColumn = [-1, ] * self.rowsNumb
+    for idx in range(self.rowsNumb):
+        newProbs = self.genTurnProbabilities(rolledPicturesIdxs=rolledPicturesIdx)
+
+        # assert abs(sum(newProbs) - 1) < 0.001
+
+        rolledPicIdx = np.random.choice(self.drumsColumns[i], size=1, p=newProbs)[0]
+        rolledPicturesIdx.append(rolledPicIdx)
+
+        if (idx % 2) == 0:
+            resColumn[downIdx] = rolledPicIdx
+            downIdx -= 1
+        else:
+            resColumn[upIdx] = rolledPicIdx
+            upIdx += 1
+
+    return resColumn
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/backend/doc/backend/CustomExceptions.html b/backend/doc/backend/CustomExceptions.html new file mode 100644 index 0000000..a3aa729 --- /dev/null +++ b/backend/doc/backend/CustomExceptions.html @@ -0,0 +1,317 @@ + + + + + + +backend.CustomExceptions API documentation + + + + + + + + + + + +
+
+
+

Module backend.CustomExceptions

+
+
+
+ +Expand source code + +
class SettingStateException(Exception):
+    def __str__(self):
+        return "invalid values for state massive: values in each column have to be distinct"
+
+
+class SettingWinningCombinationsException(Exception):
+    def __str__(self):
+        return "invalid values for dict combinations:"
+
+
+class CombinationLengthException(SettingWinningCombinationsException):
+    """
+    key length of dict combinations have to equal number columns of massive state in class OneHandBandit
+    """
+
+    def __str__(self):
+        return super().__str__() + " key length of dict combinations have to equal number columns " \
+                                   "of massive state in class OneHandBandit"
+
+
+class CombinationValuesException(SettingWinningCombinationsException):
+    def __str__(self):
+        return super().__str__() + " key values of dict combinations have to be " \
+                                   "lower than (picturesNumb - 1) in class OneHandBandit" \
+                                   "and greater than 0"
+
+
+class WinningMoneyException(SettingWinningCombinationsException):
+    def __str__(self):
+        return super().__str__() + " winning money have to be greater than 0"
+
+
+class SettingProbabilities2PicturesException(Exception):
+    def __str__(self):
+        return "error when set the probabilities for pictures;"
+
+
+class ProbabilitiesArrayLengthException(SettingProbabilities2PicturesException):
+    def __str__(self):
+        return super().__str__() + " probabilities array must be the same length as pictures array length"
+
+
+class ProbabilitiesSumException(SettingProbabilities2PicturesException):
+    def __str__(self):
+        return super().__str__() + " probabilities array sum must equal 1"
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class CombinationLengthException +(*args, **kwargs) +
+
+

key length of dict combinations have to equal number columns of massive state in class OneHandBandit

+
+ +Expand source code + +
class CombinationLengthException(SettingWinningCombinationsException):
+    """
+    key length of dict combinations have to equal number columns of massive state in class OneHandBandit
+    """
+
+    def __str__(self):
+        return super().__str__() + " key length of dict combinations have to equal number columns " \
+                                   "of massive state in class OneHandBandit"
+
+

Ancestors

+ +
+
+class CombinationValuesException +(*args, **kwargs) +
+
+

Common base class for all non-exit exceptions.

+
+ +Expand source code + +
class CombinationValuesException(SettingWinningCombinationsException):
+    def __str__(self):
+        return super().__str__() + " key values of dict combinations have to be " \
+                                   "lower than (picturesNumb - 1) in class OneHandBandit" \
+                                   "and greater than 0"
+
+

Ancestors

+ +
+
+class ProbabilitiesArrayLengthException +(*args, **kwargs) +
+
+

Common base class for all non-exit exceptions.

+
+ +Expand source code + +
class ProbabilitiesArrayLengthException(SettingProbabilities2PicturesException):
+    def __str__(self):
+        return super().__str__() + " probabilities array must be the same length as pictures array length"
+
+

Ancestors

+ +
+
+class ProbabilitiesSumException +(*args, **kwargs) +
+
+

Common base class for all non-exit exceptions.

+
+ +Expand source code + +
class ProbabilitiesSumException(SettingProbabilities2PicturesException):
+    def __str__(self):
+        return super().__str__() + " probabilities array sum must equal 1"
+
+

Ancestors

+ +
+
+class SettingProbabilities2PicturesException +(*args, **kwargs) +
+
+

Common base class for all non-exit exceptions.

+
+ +Expand source code + +
class SettingProbabilities2PicturesException(Exception):
+    def __str__(self):
+        return "error when set the probabilities for pictures;"
+
+

Ancestors

+
    +
  • builtins.Exception
  • +
  • builtins.BaseException
  • +
+

Subclasses

+ +
+
+class SettingStateException +(*args, **kwargs) +
+
+

Common base class for all non-exit exceptions.

+
+ +Expand source code + +
class SettingStateException(Exception):
+    def __str__(self):
+        return "invalid values for state massive: values in each column have to be distinct"
+
+

Ancestors

+
    +
  • builtins.Exception
  • +
  • builtins.BaseException
  • +
+
+
+class SettingWinningCombinationsException +(*args, **kwargs) +
+
+

Common base class for all non-exit exceptions.

+
+ +Expand source code + +
class SettingWinningCombinationsException(Exception):
+    def __str__(self):
+        return "invalid values for dict combinations:"
+
+

Ancestors

+
    +
  • builtins.Exception
  • +
  • builtins.BaseException
  • +
+

Subclasses

+ +
+
+class WinningMoneyException +(*args, **kwargs) +
+
+

Common base class for all non-exit exceptions.

+
+ +Expand source code + +
class WinningMoneyException(SettingWinningCombinationsException):
+    def __str__(self):
+        return super().__str__() + " winning money have to be greater than 0"
+
+

Ancestors

+ +
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/backend/doc/backend/GenAlgorithm.html b/backend/doc/backend/GenAlgorithm.html new file mode 100644 index 0000000..35f0f4d --- /dev/null +++ b/backend/doc/backend/GenAlgorithm.html @@ -0,0 +1,1484 @@ + + + + + + +backend.GenAlgorithm API documentation + + + + + + + + + + + +
+
+
+

Module backend.GenAlgorithm

+
+
+
+ +Expand source code + +
from typing import List, Iterable, Tuple, Union
+
+import numpy as np
+from scipy.stats import truncnorm
+from tqdm import tqdm
+
+
+class GeneticAlgorithm:
+    """ """
+
+    def __init__(self, bandit):
+        from backend.Bandit import OneHandBandit
+        self.bandit: OneHandBandit = bandit
+
+    def geneticAlgorithm(self, probsCount: int, goalValue: float,
+                         populationSize: int = 6, population: List[List[float]] = None, gamesCount: int = 3000,
+                         generationsCount: int = 100, eps: float = 0.001, debug: bool = False,
+                         isGraph: bool = False) -> np.array:
+        """основний метод генетичного алгоритму, який який керує усіма іншими методами та повертає результат,
+        в виді списка ймовірностей
+
+        Args:
+          probsCount: довжина списку ймовірностей, який ви хотіли б отримати
+          goalValue: значення коефіцієнта віддачі, якого ви хотіли б досягти зі згенерованими ймовірностями
+          populationSize: розмір популяції генетичного алгоритму
+          population: список індивидів(списків з ймовірностями), які будуть використовуватись генетичним алгоритмом
+          gamesCount: кількість ігор, для функції обрахунку коефіцієнту віддачі,
+            яка надалі буде використовуватися функцією фітнесу(fitness_func)
+          generationsCount: максимальна кількість поколінь після, якої зупиниться генетичний алгоритм
+          eps: похибка, яку дозволяє поточний генетичний алгоритм:
+            генетичний алгоритм зупиниться,
+            якщо (коефіцієнт віддачі згенерованих ймовірностей) - goalValue буде менше за цю похибку)
+          debug: параметр, який регулює інформативність алгоритму:
+
+            якщо debug рівне False алгоритм не інформує про зміни в поколіннях
+
+            якщо debug рівне True алгоритм інформує про зміни в поколіннях
+          isGraph: параметр, який відповідає за надання функцією додаткових даних необхідних для створення графіку
+
+        Returns:
+          якщо isGraph рівне False: список ймовірностей,
+            які мають коефіцієнт віддачі рівним goalValue з певною похибкою eps
+          якщо isGraph рівне True: списки коефіцієнтів віддачі популяції у різних поколіннях
+
+        """
+        if population is None:
+            population = []
+            for _ in range(populationSize):
+                population.append(np.array(self.bandit.genBaseProbabilities(probsCount)))
+
+            # population = [[0.34620235, 0.07168648, 0.06275534, 0.51076111, 0.00859472],
+            #               [0.48455469, 0.29974074, 0.18556755, 0.02237517, 0.00776185],
+            #               [0.4076457, 0.24869838, 0.14315961, 0.03328834000000005, 0.16720797],
+            #               [0.15385934, 0.64327094, 0.14050975, 0.03888020000000005, 0.02347977],
+            #               [0.32335616, 0.08463666, 0.28513406, 0.04416111, 0.26271201],
+            #               [0.43279837, 0.25248431, 0.05619025, 0.2325301, 0.02599697], ]
+            #               [0.39709205, 0.23165409, 0.0515545 , 0.24932884, 0.07037052], ]
+            # fitness coefs: 0.65333333, 0.677     , 0.80883333, 0.5645    , 0.81633333
+
+            # population = [[0.13124844, 0.47042438, 0.0631639 , 0.26138892, 0.07377436],
+            #               [0.0952308 , 0.65992553, 0.14414762, 0.03988682, 0.06080923],
+            #               [0.43279837, 0.25248431, 0.05619025, 0.2325301 , 0.02599697],
+            #               [0.26570799, 0.52636168, 0.11265015, 0.05940529, 0.03587489],
+            #               [0.39709205, 0.23165409, 0.0515545 , 0.24932884, 0.07037052], ]
+            # fitness coefs: 0.76933333, 0.56766667, 0.77983333, 0.72416667, 0.802
+
+        # цільова функція(фітнес функція) чим менше значення, тим більша ймовірність виживання індивіда
+        fitness = lambda probs: self.fitness_func(probs=probs, goalValue=goalValue, gamesCount=gamesCount,
+                                                  debug=debug, isGraph=isGraph)
+
+
+        fitnessVals = []
+        returnCoefsInGenerations = []
+        # if debug is True:
+        #     iterator = range(generationsCount)
+        # else:
+        #     iterator = tqdm(range(generationsCount))
+
+        # for generationIdx in tqdm(range(generationsCount)):
+        for generationIdx in range(generationsCount):
+            if debug is True:
+                print("\n######################generation:", generationIdx)
+
+            fitnessVals = np.array([fitness(ps) for ps in population])
+            if isGraph is True:
+                mass = fitnessVals.copy()
+                fitnessVals = np.array([i[1] for i in mass])
+                returnCoefsInGenerations.append([i[0] for i in mass])
+
+            if debug is True:
+                print(f"{fitnessVals=}\n")
+
+            # Обчислення коефіцієнту виживання кожного індивіду
+            surviveProbs = self.getSurviveProbs(fitnessVals)
+            # print(f"{surviveProbs=}\n")
+
+            # Перевірка умов зупинки Генетичного Алгоритму
+            if any(fitnessVals <= eps):
+                if debug is True:
+                    print("\n-----------------fitness values to break cycle:", fitnessVals)
+                break
+
+            # Відбір
+            population = self.selection(population, surviveProbs)
+
+            # Скрещування
+            population = self.crossOver(population)
+            for idx in range(len(population)):
+                self.changeProbs(population[idx])
+
+            # Мутація
+            # ймовірність мутації кожної особини
+            mutateProb = 0.2
+            for idx in range(len(population)):
+                rNumb = np.random.choice([0, 1], size=1, p=[1 - mutateProb, mutateProb])[0]
+                if rNumb:
+                    self.mutatePerson(population[idx], indpb=1 / len(population[idx]), debug=debug)
+
+        if isGraph is True:
+            return returnCoefsInGenerations
+
+        mass = sorted(list(zip(fitnessVals, range(len(fitnessVals)))), key=lambda x: x[0])
+        population = [population[i[1]] for i in mass]
+        return population[0]
+
+    # @benchmark
+    def fitness_func(self, probs: List[float,], goalValue: float, debug: bool = False, isGraph: bool = False,
+                     gamesCount: int = 5000) -> Union[float, Tuple]:
+        """функція обраховує значення функції фітнесу:
+        чим значення коефіцієнту віддачі ймовірностей probs ближче до goalValue, тим значення фітнес функції менше
+        і навпаки.
+
+        Args:
+          probs: ймовірності для яких необхідно порахувати значення фітнес функції
+          goalValue: значення коефіцієнту віддачі при якому рахується значення фітнес функція
+          debug: параметр, який регулює інформативність функції
+          gamesCount: кількість ігор, для функції обрахунку коефіцієнту віддачі
+          isGraph: параметр, який відповідає за надання функцією додаткових даних необхідних для створення графіку
+
+        Returns:
+          якщо isGraph рівне False: значення фітнес функції
+          якщо isGraph рівне True: (коефіцієнт віддачі, значення фітнес функції)
+
+        """
+        currReturnCoef = self.bandit.getReturnCoefWithNCores(gamesCount=gamesCount, probs=probs, coresCount=-1)
+        # print(f"\n{probs=}")
+        if debug is True:
+            print(f"currFitness={abs(currReturnCoef - goalValue)}")
+            print(f"{currReturnCoef=}\n")
+        if isGraph is True:
+            return currReturnCoef, abs(currReturnCoef - goalValue)
+
+        return abs(currReturnCoef - goalValue)
+
+    def getSurviveProbs(self, fitnessVals: np.array(List[Iterable[float]])) -> np.array(List[Iterable[float]]):
+        """функція обраховує ймовірність виживання кожної окремої особини з популяції
+
+        Args:
+          fitnessVals: список значень функції фітнесу особин для популяції
+
+        Returns:
+          список ймовірностей виживання особин з популяції, обрахованний на основі значень функції фітнесу
+
+        """
+        fitnessVals[fitnessVals == 0] = 0.000001
+
+        invS = sum(1 / fitnessVals)
+        surviveProbs = (1 / fitnessVals) / invS
+        return surviveProbs
+
+    def selection(self, population: List[np.array,], surviveProbs):
+        """функція реалізовує відбір методом рулетки для генетичного алгоритму
+
+        Args:
+          population: поточна популяція
+          surviveProbs: ймовірності виживання кожної особини з поточної популяції
+
+        Returns:
+          нова популяція, такого ж розміру, що і стара, але без відсіяних особин
+
+        """
+        #
+        indices = list(np.random.choice(range(len(population)), size=len(population), p=surviveProbs))
+        newPopulation = list(np.array(population)[indices])
+        return newPopulation
+
+    def crossOver(self, population):
+        """функція формує сім'ї(списки по дві особини) з поточної популяції, які потім передає у функцію скрещування
+
+        Args:
+          population: поточна популяція
+
+        Returns:
+          нова популяція, такого ж розміру, як і  стара, але з особин утворених в результаті скрещювання
+
+        """
+        # len(population) повинно бути парним
+
+        # N = (len(population) // 2) * 2
+        N = len(population)
+        families = [population[i: (i + 2)] for i in range(0, N, 2)]
+        # if len(population) % 2 == 1:
+        #     families.append([population[-1], population[0]])
+
+        # ймовірність скрещування
+        crossOverProb = 0.9
+        successors = [self.getSuccessor(f, crossOverProb) for f in families]
+        newPopulation = []
+        for i in successors:
+            newPopulation.extend(i)
+
+        return newPopulation
+
+    def getSuccessor(self, family: np.array(np.array(List[Iterable[float]])), crossOverProb: float):
+        """функціє реалізує одноточкове скрещування для генетичного алгоритму
+
+        Args:
+          family: список з двох особин для скрещування
+          crossOverProb: ймовірність скрещювання(досить велика ймовірність)
+
+        Returns:
+          повертає двох особин:
+
+          якщо скрещування відбулось: повертаються нові особини, які є результатом скрещювання
+
+          якщо скрещування не відбулось: повертаються батьки(список family)
+
+        """
+        parent1, parent2 = family
+        gensCount = len(parent1)
+
+        # low border is inclusive, high border is exclusive
+        splitIdx = np.random.randint(1, gensCount)
+        # Х.-батько: a1 | b1,c1,d1; Х.-мати: a2 | b2,c2,d2; Х.-потомок: a1,b2,c2,d2
+        successor1 = np.array(list(parent1[: splitIdx]) + list(parent2[splitIdx:]))
+
+        # Х.-батько: a1 | b1,c1,d1; Х.-мати: a2 | b2,c2,d2; Х.-потомок: a2,b1,c1,d1
+        successor2 = np.array(list(parent2[: splitIdx]) + list(parent1[splitIdx:]))
+
+        # low border is inclusive, high border is exclusive
+        rIdx = np.random.choice([0, 1], size=1, p=[1 - crossOverProb, crossOverProb])[0]
+        if rIdx == 1:
+            self.changeProbs(successor1)
+            self.changeProbs(successor2)
+            return [successor1, successor2]
+        else:
+            return family
+
+    def mutatePerson(self, person: np.array, indpb: float = 0.01, delta: float = 1, debug: bool = False) -> None:
+        """функція реалізує мутацію певної особини для генетичного алгоритму,
+        де новий мутований ген вибирається,
+        як значення нормально розподіленої випадкової величини
+        з середнім рівним значенню поточного гену та дисперсією рівною одиничці
+
+        Args:
+          person: особина, яка буде піддана мутації
+          indpb: ймовірність мутації одного гена особини(однієї ймовірності)
+          delta: максимальна різниця між поточним геном та мутованим
+          debug: параметр, який регулює інформативність функції
+
+        Returns:
+
+        """
+
+        def get_truncated_normal(mean=0, sd=1, low=0, upp=10):
+            """
+
+            Args:
+              mean:  (Default value = 0)
+              sd:  (Default value = 1)
+              low:  (Default value = 0)
+              upp:  (Default value = 10)
+
+            Returns:
+
+            """
+            return truncnorm(
+                (low - mean) / sd, (upp - mean) / sd, loc=mean, scale=sd)
+
+        person0 = person.copy()
+        for idx in range(len(person)):
+            if np.random.uniform() <= indpb:
+                if debug is True:
+                    print("mutation of one gen in one person_________________")
+                leftBorder = max(0, person[idx] - delta)
+                rightBorder = min(1, person[idx] + delta)
+                # newGenVal = random.triangular(leftBorder, rightBorder, random.gauss(person[idx], 1))
+                newGenVal = get_truncated_normal(mean=person[idx], sd=1, low=leftBorder, upp=rightBorder).rvs()
+                if debug is True:
+                    print(f"old {person=}")
+                    print(f"old gen:{person[idx]}, new gen:{newGenVal}\n")
+                # newGenVal = np.random.uniform(low=leftBorder, high=rightBorder, size=1)[0]
+                person[idx] = newGenVal
+                self.changeProbs(person, indsNot2ch=[idx, ])
+
+                if debug is True:
+                    print(f"new {person=}\n")
+
+                if any(person < 0):
+                    print(f"{person0=}")
+                    print(f"{person=}")
+
+    def changeProbs(self, probs: np.array, eps: float = 0.0001, indsNot2ch: List[int] = None) -> None:
+        """якщо indsNot2ch порожній,
+        функція зменшує або збільшує кожну ймовірність з probs, для того, щоб сума була рівна 1
+
+        якщо indsNot2ch не порожній,
+        функція зменшує або збільшує всі ймовірності з probs, окрім тих ймовірностей,
+        чиї індексі зазначені у списку indsNot2ch, для того, щоб сума була рівна 1
+
+        Args:
+          probs: масив ймовірностей
+          eps: похибка, яку дозволяє функція
+          indsNot2ch: список індексів ймовірностей, які не можна змінювати
+
+        Returns:
+
+        """
+        sVal = probs.sum()
+        value = abs(1 - sVal)
+
+        if value < eps:
+            maxElIdx = max(zip(range(len(probs)), probs), key=lambda x: x[1])[0]
+            indices = list(range(len(probs)))
+            indices.remove(maxElIdx)
+
+            probs[maxElIdx] = (1 - probs[indices].sum())
+            return
+
+        if indsNot2ch is None:
+            inds2ch = list(range(len(probs)))
+        else:
+            inds2ch = [i for i in range(len(probs)) if i not in indsNot2ch]
+
+        if sVal > 1:
+            self.decreaseProbs(probs, value, inds2ch)
+        else:
+            self.increaseProbs(probs, value, inds2ch)
+
+        # для того, щоб прибрати відхилення суми від одиниці, яке менше за eps
+        self.changeProbs(probs)
+
+    def decreaseProbs(self, probs: np.ndarray, value, inds2ch: List[int]) -> None:
+        """value не повинно бути більше за sum(probs)
+        зменшуємо кожне число пропорційно:
+
+        якщо індекс ймовірності знаходиться у списку inds2ch, то ймовірність не змінюється
+
+        якщо індекс ймовірності p0 не належить списку inds2ch
+        та ймовірність p0 становить 50% від суми всіх ймовірностей, то p0 зменшиться на 50% від числа value
+
+        Args:
+          probs: список ймовірностей
+          value: значення на яке сумарно треба зменшити ймовірності
+          inds2ch: список індексів ймовірностей, які не можна змінювати
+
+        Returns:
+
+        """
+        s = sum(probs[inds2ch])
+        percents = probs[inds2ch] / s
+        percentVals = percents * value
+        probs[inds2ch] = probs[inds2ch] - percentVals
+
+    def increaseProbs(self, probs: np.ndarray, value, inds2ch: List[int]):
+        """value не повинно бути більше за sum(probs)
+        збільшуємо кожне число пропорційно:
+
+        якщо індекс ймовірності знаходиться у списку inds2ch, то ймовірність не змінюється
+
+        якщо індекс ймовірності p0 не належить списку inds2ch
+        та ймовірність p0 становить 50% від суми всіх ймовірностей, то p0 збільшиться на 50% від числа value
+
+        Args:
+          probs: список ймовірностей
+          value: значення на яке сумарно треба зменшити ймовірності
+          inds2ch: список індексів ймовірностей, які не можна змінювати
+
+        Returns:
+
+        """
+        s = sum(probs[inds2ch])
+        percents = probs[inds2ch] / s
+        percentVals = percents * value
+        probs[inds2ch] = probs[inds2ch] + percentVals
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class GeneticAlgorithm +(bandit) +
+
+
+
+ +Expand source code + +
class GeneticAlgorithm:
+    """ """
+
+    def __init__(self, bandit):
+        from backend.Bandit import OneHandBandit
+        self.bandit: OneHandBandit = bandit
+
+    def geneticAlgorithm(self, probsCount: int, goalValue: float,
+                         populationSize: int = 6, population: List[List[float]] = None, gamesCount: int = 3000,
+                         generationsCount: int = 100, eps: float = 0.001, debug: bool = False,
+                         isGraph: bool = False) -> np.array:
+        """основний метод генетичного алгоритму, який який керує усіма іншими методами та повертає результат,
+        в виді списка ймовірностей
+
+        Args:
+          probsCount: довжина списку ймовірностей, який ви хотіли б отримати
+          goalValue: значення коефіцієнта віддачі, якого ви хотіли б досягти зі згенерованими ймовірностями
+          populationSize: розмір популяції генетичного алгоритму
+          population: список індивидів(списків з ймовірностями), які будуть використовуватись генетичним алгоритмом
+          gamesCount: кількість ігор, для функції обрахунку коефіцієнту віддачі,
+            яка надалі буде використовуватися функцією фітнесу(fitness_func)
+          generationsCount: максимальна кількість поколінь після, якої зупиниться генетичний алгоритм
+          eps: похибка, яку дозволяє поточний генетичний алгоритм:
+            генетичний алгоритм зупиниться,
+            якщо (коефіцієнт віддачі згенерованих ймовірностей) - goalValue буде менше за цю похибку)
+          debug: параметр, який регулює інформативність алгоритму:
+
+            якщо debug рівне False алгоритм не інформує про зміни в поколіннях
+
+            якщо debug рівне True алгоритм інформує про зміни в поколіннях
+          isGraph: параметр, який відповідає за надання функцією додаткових даних необхідних для створення графіку
+
+        Returns:
+          якщо isGraph рівне False: список ймовірностей,
+            які мають коефіцієнт віддачі рівним goalValue з певною похибкою eps
+          якщо isGraph рівне True: списки коефіцієнтів віддачі популяції у різних поколіннях
+
+        """
+        if population is None:
+            population = []
+            for _ in range(populationSize):
+                population.append(np.array(self.bandit.genBaseProbabilities(probsCount)))
+
+            # population = [[0.34620235, 0.07168648, 0.06275534, 0.51076111, 0.00859472],
+            #               [0.48455469, 0.29974074, 0.18556755, 0.02237517, 0.00776185],
+            #               [0.4076457, 0.24869838, 0.14315961, 0.03328834000000005, 0.16720797],
+            #               [0.15385934, 0.64327094, 0.14050975, 0.03888020000000005, 0.02347977],
+            #               [0.32335616, 0.08463666, 0.28513406, 0.04416111, 0.26271201],
+            #               [0.43279837, 0.25248431, 0.05619025, 0.2325301, 0.02599697], ]
+            #               [0.39709205, 0.23165409, 0.0515545 , 0.24932884, 0.07037052], ]
+            # fitness coefs: 0.65333333, 0.677     , 0.80883333, 0.5645    , 0.81633333
+
+            # population = [[0.13124844, 0.47042438, 0.0631639 , 0.26138892, 0.07377436],
+            #               [0.0952308 , 0.65992553, 0.14414762, 0.03988682, 0.06080923],
+            #               [0.43279837, 0.25248431, 0.05619025, 0.2325301 , 0.02599697],
+            #               [0.26570799, 0.52636168, 0.11265015, 0.05940529, 0.03587489],
+            #               [0.39709205, 0.23165409, 0.0515545 , 0.24932884, 0.07037052], ]
+            # fitness coefs: 0.76933333, 0.56766667, 0.77983333, 0.72416667, 0.802
+
+        # цільова функція(фітнес функція) чим менше значення, тим більша ймовірність виживання індивіда
+        fitness = lambda probs: self.fitness_func(probs=probs, goalValue=goalValue, gamesCount=gamesCount,
+                                                  debug=debug, isGraph=isGraph)
+
+
+        fitnessVals = []
+        returnCoefsInGenerations = []
+        # if debug is True:
+        #     iterator = range(generationsCount)
+        # else:
+        #     iterator = tqdm(range(generationsCount))
+
+        # for generationIdx in tqdm(range(generationsCount)):
+        for generationIdx in range(generationsCount):
+            if debug is True:
+                print("\n######################generation:", generationIdx)
+
+            fitnessVals = np.array([fitness(ps) for ps in population])
+            if isGraph is True:
+                mass = fitnessVals.copy()
+                fitnessVals = np.array([i[1] for i in mass])
+                returnCoefsInGenerations.append([i[0] for i in mass])
+
+            if debug is True:
+                print(f"{fitnessVals=}\n")
+
+            # Обчислення коефіцієнту виживання кожного індивіду
+            surviveProbs = self.getSurviveProbs(fitnessVals)
+            # print(f"{surviveProbs=}\n")
+
+            # Перевірка умов зупинки Генетичного Алгоритму
+            if any(fitnessVals <= eps):
+                if debug is True:
+                    print("\n-----------------fitness values to break cycle:", fitnessVals)
+                break
+
+            # Відбір
+            population = self.selection(population, surviveProbs)
+
+            # Скрещування
+            population = self.crossOver(population)
+            for idx in range(len(population)):
+                self.changeProbs(population[idx])
+
+            # Мутація
+            # ймовірність мутації кожної особини
+            mutateProb = 0.2
+            for idx in range(len(population)):
+                rNumb = np.random.choice([0, 1], size=1, p=[1 - mutateProb, mutateProb])[0]
+                if rNumb:
+                    self.mutatePerson(population[idx], indpb=1 / len(population[idx]), debug=debug)
+
+        if isGraph is True:
+            return returnCoefsInGenerations
+
+        mass = sorted(list(zip(fitnessVals, range(len(fitnessVals)))), key=lambda x: x[0])
+        population = [population[i[1]] for i in mass]
+        return population[0]
+
+    # @benchmark
+    def fitness_func(self, probs: List[float,], goalValue: float, debug: bool = False, isGraph: bool = False,
+                     gamesCount: int = 5000) -> Union[float, Tuple]:
+        """функція обраховує значення функції фітнесу:
+        чим значення коефіцієнту віддачі ймовірностей probs ближче до goalValue, тим значення фітнес функції менше
+        і навпаки.
+
+        Args:
+          probs: ймовірності для яких необхідно порахувати значення фітнес функції
+          goalValue: значення коефіцієнту віддачі при якому рахується значення фітнес функція
+          debug: параметр, який регулює інформативність функції
+          gamesCount: кількість ігор, для функції обрахунку коефіцієнту віддачі
+          isGraph: параметр, який відповідає за надання функцією додаткових даних необхідних для створення графіку
+
+        Returns:
+          якщо isGraph рівне False: значення фітнес функції
+          якщо isGraph рівне True: (коефіцієнт віддачі, значення фітнес функції)
+
+        """
+        currReturnCoef = self.bandit.getReturnCoefWithNCores(gamesCount=gamesCount, probs=probs, coresCount=-1)
+        # print(f"\n{probs=}")
+        if debug is True:
+            print(f"currFitness={abs(currReturnCoef - goalValue)}")
+            print(f"{currReturnCoef=}\n")
+        if isGraph is True:
+            return currReturnCoef, abs(currReturnCoef - goalValue)
+
+        return abs(currReturnCoef - goalValue)
+
+    def getSurviveProbs(self, fitnessVals: np.array(List[Iterable[float]])) -> np.array(List[Iterable[float]]):
+        """функція обраховує ймовірність виживання кожної окремої особини з популяції
+
+        Args:
+          fitnessVals: список значень функції фітнесу особин для популяції
+
+        Returns:
+          список ймовірностей виживання особин з популяції, обрахованний на основі значень функції фітнесу
+
+        """
+        fitnessVals[fitnessVals == 0] = 0.000001
+
+        invS = sum(1 / fitnessVals)
+        surviveProbs = (1 / fitnessVals) / invS
+        return surviveProbs
+
+    def selection(self, population: List[np.array,], surviveProbs):
+        """функція реалізовує відбір методом рулетки для генетичного алгоритму
+
+        Args:
+          population: поточна популяція
+          surviveProbs: ймовірності виживання кожної особини з поточної популяції
+
+        Returns:
+          нова популяція, такого ж розміру, що і стара, але без відсіяних особин
+
+        """
+        #
+        indices = list(np.random.choice(range(len(population)), size=len(population), p=surviveProbs))
+        newPopulation = list(np.array(population)[indices])
+        return newPopulation
+
+    def crossOver(self, population):
+        """функція формує сім'ї(списки по дві особини) з поточної популяції, які потім передає у функцію скрещування
+
+        Args:
+          population: поточна популяція
+
+        Returns:
+          нова популяція, такого ж розміру, як і  стара, але з особин утворених в результаті скрещювання
+
+        """
+        # len(population) повинно бути парним
+
+        # N = (len(population) // 2) * 2
+        N = len(population)
+        families = [population[i: (i + 2)] for i in range(0, N, 2)]
+        # if len(population) % 2 == 1:
+        #     families.append([population[-1], population[0]])
+
+        # ймовірність скрещування
+        crossOverProb = 0.9
+        successors = [self.getSuccessor(f, crossOverProb) for f in families]
+        newPopulation = []
+        for i in successors:
+            newPopulation.extend(i)
+
+        return newPopulation
+
+    def getSuccessor(self, family: np.array(np.array(List[Iterable[float]])), crossOverProb: float):
+        """функціє реалізує одноточкове скрещування для генетичного алгоритму
+
+        Args:
+          family: список з двох особин для скрещування
+          crossOverProb: ймовірність скрещювання(досить велика ймовірність)
+
+        Returns:
+          повертає двох особин:
+
+          якщо скрещування відбулось: повертаються нові особини, які є результатом скрещювання
+
+          якщо скрещування не відбулось: повертаються батьки(список family)
+
+        """
+        parent1, parent2 = family
+        gensCount = len(parent1)
+
+        # low border is inclusive, high border is exclusive
+        splitIdx = np.random.randint(1, gensCount)
+        # Х.-батько: a1 | b1,c1,d1; Х.-мати: a2 | b2,c2,d2; Х.-потомок: a1,b2,c2,d2
+        successor1 = np.array(list(parent1[: splitIdx]) + list(parent2[splitIdx:]))
+
+        # Х.-батько: a1 | b1,c1,d1; Х.-мати: a2 | b2,c2,d2; Х.-потомок: a2,b1,c1,d1
+        successor2 = np.array(list(parent2[: splitIdx]) + list(parent1[splitIdx:]))
+
+        # low border is inclusive, high border is exclusive
+        rIdx = np.random.choice([0, 1], size=1, p=[1 - crossOverProb, crossOverProb])[0]
+        if rIdx == 1:
+            self.changeProbs(successor1)
+            self.changeProbs(successor2)
+            return [successor1, successor2]
+        else:
+            return family
+
+    def mutatePerson(self, person: np.array, indpb: float = 0.01, delta: float = 1, debug: bool = False) -> None:
+        """функція реалізує мутацію певної особини для генетичного алгоритму,
+        де новий мутований ген вибирається,
+        як значення нормально розподіленої випадкової величини
+        з середнім рівним значенню поточного гену та дисперсією рівною одиничці
+
+        Args:
+          person: особина, яка буде піддана мутації
+          indpb: ймовірність мутації одного гена особини(однієї ймовірності)
+          delta: максимальна різниця між поточним геном та мутованим
+          debug: параметр, який регулює інформативність функції
+
+        Returns:
+
+        """
+
+        def get_truncated_normal(mean=0, sd=1, low=0, upp=10):
+            """
+
+            Args:
+              mean:  (Default value = 0)
+              sd:  (Default value = 1)
+              low:  (Default value = 0)
+              upp:  (Default value = 10)
+
+            Returns:
+
+            """
+            return truncnorm(
+                (low - mean) / sd, (upp - mean) / sd, loc=mean, scale=sd)
+
+        person0 = person.copy()
+        for idx in range(len(person)):
+            if np.random.uniform() <= indpb:
+                if debug is True:
+                    print("mutation of one gen in one person_________________")
+                leftBorder = max(0, person[idx] - delta)
+                rightBorder = min(1, person[idx] + delta)
+                # newGenVal = random.triangular(leftBorder, rightBorder, random.gauss(person[idx], 1))
+                newGenVal = get_truncated_normal(mean=person[idx], sd=1, low=leftBorder, upp=rightBorder).rvs()
+                if debug is True:
+                    print(f"old {person=}")
+                    print(f"old gen:{person[idx]}, new gen:{newGenVal}\n")
+                # newGenVal = np.random.uniform(low=leftBorder, high=rightBorder, size=1)[0]
+                person[idx] = newGenVal
+                self.changeProbs(person, indsNot2ch=[idx, ])
+
+                if debug is True:
+                    print(f"new {person=}\n")
+
+                if any(person < 0):
+                    print(f"{person0=}")
+                    print(f"{person=}")
+
+    def changeProbs(self, probs: np.array, eps: float = 0.0001, indsNot2ch: List[int] = None) -> None:
+        """якщо indsNot2ch порожній,
+        функція зменшує або збільшує кожну ймовірність з probs, для того, щоб сума була рівна 1
+
+        якщо indsNot2ch не порожній,
+        функція зменшує або збільшує всі ймовірності з probs, окрім тих ймовірностей,
+        чиї індексі зазначені у списку indsNot2ch, для того, щоб сума була рівна 1
+
+        Args:
+          probs: масив ймовірностей
+          eps: похибка, яку дозволяє функція
+          indsNot2ch: список індексів ймовірностей, які не можна змінювати
+
+        Returns:
+
+        """
+        sVal = probs.sum()
+        value = abs(1 - sVal)
+
+        if value < eps:
+            maxElIdx = max(zip(range(len(probs)), probs), key=lambda x: x[1])[0]
+            indices = list(range(len(probs)))
+            indices.remove(maxElIdx)
+
+            probs[maxElIdx] = (1 - probs[indices].sum())
+            return
+
+        if indsNot2ch is None:
+            inds2ch = list(range(len(probs)))
+        else:
+            inds2ch = [i for i in range(len(probs)) if i not in indsNot2ch]
+
+        if sVal > 1:
+            self.decreaseProbs(probs, value, inds2ch)
+        else:
+            self.increaseProbs(probs, value, inds2ch)
+
+        # для того, щоб прибрати відхилення суми від одиниці, яке менше за eps
+        self.changeProbs(probs)
+
+    def decreaseProbs(self, probs: np.ndarray, value, inds2ch: List[int]) -> None:
+        """value не повинно бути більше за sum(probs)
+        зменшуємо кожне число пропорційно:
+
+        якщо індекс ймовірності знаходиться у списку inds2ch, то ймовірність не змінюється
+
+        якщо індекс ймовірності p0 не належить списку inds2ch
+        та ймовірність p0 становить 50% від суми всіх ймовірностей, то p0 зменшиться на 50% від числа value
+
+        Args:
+          probs: список ймовірностей
+          value: значення на яке сумарно треба зменшити ймовірності
+          inds2ch: список індексів ймовірностей, які не можна змінювати
+
+        Returns:
+
+        """
+        s = sum(probs[inds2ch])
+        percents = probs[inds2ch] / s
+        percentVals = percents * value
+        probs[inds2ch] = probs[inds2ch] - percentVals
+
+    def increaseProbs(self, probs: np.ndarray, value, inds2ch: List[int]):
+        """value не повинно бути більше за sum(probs)
+        збільшуємо кожне число пропорційно:
+
+        якщо індекс ймовірності знаходиться у списку inds2ch, то ймовірність не змінюється
+
+        якщо індекс ймовірності p0 не належить списку inds2ch
+        та ймовірність p0 становить 50% від суми всіх ймовірностей, то p0 збільшиться на 50% від числа value
+
+        Args:
+          probs: список ймовірностей
+          value: значення на яке сумарно треба зменшити ймовірності
+          inds2ch: список індексів ймовірностей, які не можна змінювати
+
+        Returns:
+
+        """
+        s = sum(probs[inds2ch])
+        percents = probs[inds2ch] / s
+        percentVals = percents * value
+        probs[inds2ch] = probs[inds2ch] + percentVals
+
+

Methods

+
+
+def changeProbs(self, probs: , eps: float = 0.0001, indsNot2ch: List[int] = None) ‑> None +
+
+

якщо indsNot2ch порожній, +функція зменшує або збільшує кожну ймовірність з probs, для того, щоб сума була рівна 1

+

якщо indsNot2ch не порожній, +функція зменшує або збільшує всі ймовірності з probs, окрім тих ймовірностей, +чиї індексі зазначені у списку indsNot2ch, для того, щоб сума була рівна 1

+

Args

+
+
probs
+
масив ймовірностей
+
eps
+
похибка, яку дозволяє функція
+
indsNot2ch
+
список індексів ймовірностей, які не можна змінювати
+
+

Returns:

+
+ +Expand source code + +
def changeProbs(self, probs: np.array, eps: float = 0.0001, indsNot2ch: List[int] = None) -> None:
+    """якщо indsNot2ch порожній,
+    функція зменшує або збільшує кожну ймовірність з probs, для того, щоб сума була рівна 1
+
+    якщо indsNot2ch не порожній,
+    функція зменшує або збільшує всі ймовірності з probs, окрім тих ймовірностей,
+    чиї індексі зазначені у списку indsNot2ch, для того, щоб сума була рівна 1
+
+    Args:
+      probs: масив ймовірностей
+      eps: похибка, яку дозволяє функція
+      indsNot2ch: список індексів ймовірностей, які не можна змінювати
+
+    Returns:
+
+    """
+    sVal = probs.sum()
+    value = abs(1 - sVal)
+
+    if value < eps:
+        maxElIdx = max(zip(range(len(probs)), probs), key=lambda x: x[1])[0]
+        indices = list(range(len(probs)))
+        indices.remove(maxElIdx)
+
+        probs[maxElIdx] = (1 - probs[indices].sum())
+        return
+
+    if indsNot2ch is None:
+        inds2ch = list(range(len(probs)))
+    else:
+        inds2ch = [i for i in range(len(probs)) if i not in indsNot2ch]
+
+    if sVal > 1:
+        self.decreaseProbs(probs, value, inds2ch)
+    else:
+        self.increaseProbs(probs, value, inds2ch)
+
+    # для того, щоб прибрати відхилення суми від одиниці, яке менше за eps
+    self.changeProbs(probs)
+
+
+
+def crossOver(self, population) +
+
+

функція формує сім'ї(списки по дві особини) з поточної популяції, які потім передає у функцію скрещування

+

Args

+
+
population
+
поточна популяція
+
+

Returns

+

нова популяція, такого ж розміру, як і +стара, але з особин утворених в результаті скрещювання

+
+ +Expand source code + +
def crossOver(self, population):
+    """функція формує сім'ї(списки по дві особини) з поточної популяції, які потім передає у функцію скрещування
+
+    Args:
+      population: поточна популяція
+
+    Returns:
+      нова популяція, такого ж розміру, як і  стара, але з особин утворених в результаті скрещювання
+
+    """
+    # len(population) повинно бути парним
+
+    # N = (len(population) // 2) * 2
+    N = len(population)
+    families = [population[i: (i + 2)] for i in range(0, N, 2)]
+    # if len(population) % 2 == 1:
+    #     families.append([population[-1], population[0]])
+
+    # ймовірність скрещування
+    crossOverProb = 0.9
+    successors = [self.getSuccessor(f, crossOverProb) for f in families]
+    newPopulation = []
+    for i in successors:
+        newPopulation.extend(i)
+
+    return newPopulation
+
+
+
+def decreaseProbs(self, probs: numpy.ndarray, value, inds2ch: List[int]) ‑> None +
+
+

value не повинно бути більше за sum(probs) +зменшуємо кожне число пропорційно:

+

якщо індекс ймовірності знаходиться у списку inds2ch, то ймовірність не змінюється

+

якщо індекс ймовірності p0 не належить списку inds2ch +та ймовірність p0 становить 50% від суми всіх ймовірностей, то p0 зменшиться на 50% від числа value

+

Args

+
+
probs
+
список ймовірностей
+
value
+
значення на яке сумарно треба зменшити ймовірності
+
inds2ch
+
список індексів ймовірностей, які не можна змінювати
+
+

Returns:

+
+ +Expand source code + +
def decreaseProbs(self, probs: np.ndarray, value, inds2ch: List[int]) -> None:
+    """value не повинно бути більше за sum(probs)
+    зменшуємо кожне число пропорційно:
+
+    якщо індекс ймовірності знаходиться у списку inds2ch, то ймовірність не змінюється
+
+    якщо індекс ймовірності p0 не належить списку inds2ch
+    та ймовірність p0 становить 50% від суми всіх ймовірностей, то p0 зменшиться на 50% від числа value
+
+    Args:
+      probs: список ймовірностей
+      value: значення на яке сумарно треба зменшити ймовірності
+      inds2ch: список індексів ймовірностей, які не можна змінювати
+
+    Returns:
+
+    """
+    s = sum(probs[inds2ch])
+    percents = probs[inds2ch] / s
+    percentVals = percents * value
+    probs[inds2ch] = probs[inds2ch] - percentVals
+
+
+
+def fitness_func(self, probs: List[float], goalValue: float, debug: bool = False, isGraph: bool = False, gamesCount: int = 5000) ‑> Union[float, Tuple[]] +
+
+

функція обраховує значення функції фітнесу: +чим значення коефіцієнту віддачі ймовірностей probs ближче до goalValue, тим значення фітнес функції менше +і навпаки.

+

Args

+
+
probs
+
ймовірності для яких необхідно порахувати значення фітнес функції
+
goalValue
+
значення коефіцієнту віддачі при якому рахується значення фітнес функція
+
debug
+
параметр, який регулює інформативність функції
+
gamesCount
+
кількість ігор, для функції обрахунку коефіцієнту віддачі
+
isGraph
+
параметр, який відповідає за надання функцією додаткових даних необхідних для створення графіку
+
+

Returns

+
+
якщо isGraph рівне False
+
значення фітнес функції
+
якщо isGraph рівне True
+
(коефіцієнт віддачі, значення фітнес функції)
+
+
+ +Expand source code + +
def fitness_func(self, probs: List[float,], goalValue: float, debug: bool = False, isGraph: bool = False,
+                 gamesCount: int = 5000) -> Union[float, Tuple]:
+    """функція обраховує значення функції фітнесу:
+    чим значення коефіцієнту віддачі ймовірностей probs ближче до goalValue, тим значення фітнес функції менше
+    і навпаки.
+
+    Args:
+      probs: ймовірності для яких необхідно порахувати значення фітнес функції
+      goalValue: значення коефіцієнту віддачі при якому рахується значення фітнес функція
+      debug: параметр, який регулює інформативність функції
+      gamesCount: кількість ігор, для функції обрахунку коефіцієнту віддачі
+      isGraph: параметр, який відповідає за надання функцією додаткових даних необхідних для створення графіку
+
+    Returns:
+      якщо isGraph рівне False: значення фітнес функції
+      якщо isGraph рівне True: (коефіцієнт віддачі, значення фітнес функції)
+
+    """
+    currReturnCoef = self.bandit.getReturnCoefWithNCores(gamesCount=gamesCount, probs=probs, coresCount=-1)
+    # print(f"\n{probs=}")
+    if debug is True:
+        print(f"currFitness={abs(currReturnCoef - goalValue)}")
+        print(f"{currReturnCoef=}\n")
+    if isGraph is True:
+        return currReturnCoef, abs(currReturnCoef - goalValue)
+
+    return abs(currReturnCoef - goalValue)
+
+
+
+def geneticAlgorithm(self, probsCount: int, goalValue: float, populationSize: int = 6, population: List[List[float]] = None, gamesCount: int = 3000, generationsCount: int = 100, eps: float = 0.001, debug: bool = False, isGraph: bool = False) ‑>  +
+
+

основний метод генетичного алгоритму, який який керує усіма іншими методами та повертає результат, +в виді списка ймовірностей

+

Args

+
+
probsCount
+
довжина списку ймовірностей, який ви хотіли б отримати
+
goalValue
+
значення коефіцієнта віддачі, якого ви хотіли б досягти зі згенерованими ймовірностями
+
populationSize
+
розмір популяції генетичного алгоритму
+
population
+
список індивидів(списків з ймовірностями), які будуть використовуватись генетичним алгоритмом
+
gamesCount
+
кількість ігор, для функції обрахунку коефіцієнту віддачі, +яка надалі буде використовуватися функцією фітнесу(fitness_func)
+
generationsCount
+
максимальна кількість поколінь після, якої зупиниться генетичний алгоритм
+
eps
+
похибка, яку дозволяє поточний генетичний алгоритм: +генетичний алгоритм зупиниться, +якщо (коефіцієнт віддачі згенерованих ймовірностей) - goalValue буде менше за цю похибку)
+
debug
+
+

параметр, який регулює інформативність алгоритму:

+

якщо debug рівне False алгоритм не інформує про зміни в поколіннях

+

якщо debug рівне True алгоритм інформує про зміни в поколіннях

+
+
isGraph
+
параметр, який відповідає за надання функцією додаткових даних необхідних для створення графіку
+
+

Returns

+
+
якщо isGraph рівне False
+
список ймовірностей, +які мають коефіцієнт віддачі рівним goalValue з певною похибкою eps
+
якщо isGraph рівне True
+
списки коефіцієнтів віддачі популяції у різних поколіннях
+
+
+ +Expand source code + +
def geneticAlgorithm(self, probsCount: int, goalValue: float,
+                     populationSize: int = 6, population: List[List[float]] = None, gamesCount: int = 3000,
+                     generationsCount: int = 100, eps: float = 0.001, debug: bool = False,
+                     isGraph: bool = False) -> np.array:
+    """основний метод генетичного алгоритму, який який керує усіма іншими методами та повертає результат,
+    в виді списка ймовірностей
+
+    Args:
+      probsCount: довжина списку ймовірностей, який ви хотіли б отримати
+      goalValue: значення коефіцієнта віддачі, якого ви хотіли б досягти зі згенерованими ймовірностями
+      populationSize: розмір популяції генетичного алгоритму
+      population: список індивидів(списків з ймовірностями), які будуть використовуватись генетичним алгоритмом
+      gamesCount: кількість ігор, для функції обрахунку коефіцієнту віддачі,
+        яка надалі буде використовуватися функцією фітнесу(fitness_func)
+      generationsCount: максимальна кількість поколінь після, якої зупиниться генетичний алгоритм
+      eps: похибка, яку дозволяє поточний генетичний алгоритм:
+        генетичний алгоритм зупиниться,
+        якщо (коефіцієнт віддачі згенерованих ймовірностей) - goalValue буде менше за цю похибку)
+      debug: параметр, який регулює інформативність алгоритму:
+
+        якщо debug рівне False алгоритм не інформує про зміни в поколіннях
+
+        якщо debug рівне True алгоритм інформує про зміни в поколіннях
+      isGraph: параметр, який відповідає за надання функцією додаткових даних необхідних для створення графіку
+
+    Returns:
+      якщо isGraph рівне False: список ймовірностей,
+        які мають коефіцієнт віддачі рівним goalValue з певною похибкою eps
+      якщо isGraph рівне True: списки коефіцієнтів віддачі популяції у різних поколіннях
+
+    """
+    if population is None:
+        population = []
+        for _ in range(populationSize):
+            population.append(np.array(self.bandit.genBaseProbabilities(probsCount)))
+
+        # population = [[0.34620235, 0.07168648, 0.06275534, 0.51076111, 0.00859472],
+        #               [0.48455469, 0.29974074, 0.18556755, 0.02237517, 0.00776185],
+        #               [0.4076457, 0.24869838, 0.14315961, 0.03328834000000005, 0.16720797],
+        #               [0.15385934, 0.64327094, 0.14050975, 0.03888020000000005, 0.02347977],
+        #               [0.32335616, 0.08463666, 0.28513406, 0.04416111, 0.26271201],
+        #               [0.43279837, 0.25248431, 0.05619025, 0.2325301, 0.02599697], ]
+        #               [0.39709205, 0.23165409, 0.0515545 , 0.24932884, 0.07037052], ]
+        # fitness coefs: 0.65333333, 0.677     , 0.80883333, 0.5645    , 0.81633333
+
+        # population = [[0.13124844, 0.47042438, 0.0631639 , 0.26138892, 0.07377436],
+        #               [0.0952308 , 0.65992553, 0.14414762, 0.03988682, 0.06080923],
+        #               [0.43279837, 0.25248431, 0.05619025, 0.2325301 , 0.02599697],
+        #               [0.26570799, 0.52636168, 0.11265015, 0.05940529, 0.03587489],
+        #               [0.39709205, 0.23165409, 0.0515545 , 0.24932884, 0.07037052], ]
+        # fitness coefs: 0.76933333, 0.56766667, 0.77983333, 0.72416667, 0.802
+
+    # цільова функція(фітнес функція) чим менше значення, тим більша ймовірність виживання індивіда
+    fitness = lambda probs: self.fitness_func(probs=probs, goalValue=goalValue, gamesCount=gamesCount,
+                                              debug=debug, isGraph=isGraph)
+
+
+    fitnessVals = []
+    returnCoefsInGenerations = []
+    # if debug is True:
+    #     iterator = range(generationsCount)
+    # else:
+    #     iterator = tqdm(range(generationsCount))
+
+    # for generationIdx in tqdm(range(generationsCount)):
+    for generationIdx in range(generationsCount):
+        if debug is True:
+            print("\n######################generation:", generationIdx)
+
+        fitnessVals = np.array([fitness(ps) for ps in population])
+        if isGraph is True:
+            mass = fitnessVals.copy()
+            fitnessVals = np.array([i[1] for i in mass])
+            returnCoefsInGenerations.append([i[0] for i in mass])
+
+        if debug is True:
+            print(f"{fitnessVals=}\n")
+
+        # Обчислення коефіцієнту виживання кожного індивіду
+        surviveProbs = self.getSurviveProbs(fitnessVals)
+        # print(f"{surviveProbs=}\n")
+
+        # Перевірка умов зупинки Генетичного Алгоритму
+        if any(fitnessVals <= eps):
+            if debug is True:
+                print("\n-----------------fitness values to break cycle:", fitnessVals)
+            break
+
+        # Відбір
+        population = self.selection(population, surviveProbs)
+
+        # Скрещування
+        population = self.crossOver(population)
+        for idx in range(len(population)):
+            self.changeProbs(population[idx])
+
+        # Мутація
+        # ймовірність мутації кожної особини
+        mutateProb = 0.2
+        for idx in range(len(population)):
+            rNumb = np.random.choice([0, 1], size=1, p=[1 - mutateProb, mutateProb])[0]
+            if rNumb:
+                self.mutatePerson(population[idx], indpb=1 / len(population[idx]), debug=debug)
+
+    if isGraph is True:
+        return returnCoefsInGenerations
+
+    mass = sorted(list(zip(fitnessVals, range(len(fitnessVals)))), key=lambda x: x[0])
+    population = [population[i[1]] for i in mass]
+    return population[0]
+
+
+
+def getSuccessor(self, family: array(typing.List[typing.Iterable[float]], dtype=object), crossOverProb: float) +
+
+

функціє реалізує одноточкове скрещування для генетичного алгоритму

+

Args

+
+
family
+
список з двох особин для скрещування
+
crossOverProb
+
ймовірність скрещювання(досить велика ймовірність)
+
+

Returns

+

повертає двох особин:

+
+
якщо скрещування відбулось
+
повертаються нові особини, які є результатом скрещювання
+
якщо скрещування не відбулось
+
повертаються батьки(список family)
+
+
+ +Expand source code + +
def getSuccessor(self, family: np.array(np.array(List[Iterable[float]])), crossOverProb: float):
+    """функціє реалізує одноточкове скрещування для генетичного алгоритму
+
+    Args:
+      family: список з двох особин для скрещування
+      crossOverProb: ймовірність скрещювання(досить велика ймовірність)
+
+    Returns:
+      повертає двох особин:
+
+      якщо скрещування відбулось: повертаються нові особини, які є результатом скрещювання
+
+      якщо скрещування не відбулось: повертаються батьки(список family)
+
+    """
+    parent1, parent2 = family
+    gensCount = len(parent1)
+
+    # low border is inclusive, high border is exclusive
+    splitIdx = np.random.randint(1, gensCount)
+    # Х.-батько: a1 | b1,c1,d1; Х.-мати: a2 | b2,c2,d2; Х.-потомок: a1,b2,c2,d2
+    successor1 = np.array(list(parent1[: splitIdx]) + list(parent2[splitIdx:]))
+
+    # Х.-батько: a1 | b1,c1,d1; Х.-мати: a2 | b2,c2,d2; Х.-потомок: a2,b1,c1,d1
+    successor2 = np.array(list(parent2[: splitIdx]) + list(parent1[splitIdx:]))
+
+    # low border is inclusive, high border is exclusive
+    rIdx = np.random.choice([0, 1], size=1, p=[1 - crossOverProb, crossOverProb])[0]
+    if rIdx == 1:
+        self.changeProbs(successor1)
+        self.changeProbs(successor2)
+        return [successor1, successor2]
+    else:
+        return family
+
+
+
+def getSurviveProbs(self, fitnessVals: array(typing.List[typing.Iterable[float]], dtype=object)) ‑> array(typing.List[typing.Iterable[float]], dtype=object) +
+
+

функція обраховує ймовірність виживання кожної окремої особини з популяції

+

Args

+
+
fitnessVals
+
список значень функції фітнесу особин для популяції
+
+

Returns

+

список ймовірностей виживання особин з популяції, обрахованний на основі значень функції фітнесу

+
+ +Expand source code + +
def getSurviveProbs(self, fitnessVals: np.array(List[Iterable[float]])) -> np.array(List[Iterable[float]]):
+    """функція обраховує ймовірність виживання кожної окремої особини з популяції
+
+    Args:
+      fitnessVals: список значень функції фітнесу особин для популяції
+
+    Returns:
+      список ймовірностей виживання особин з популяції, обрахованний на основі значень функції фітнесу
+
+    """
+    fitnessVals[fitnessVals == 0] = 0.000001
+
+    invS = sum(1 / fitnessVals)
+    surviveProbs = (1 / fitnessVals) / invS
+    return surviveProbs
+
+
+
+def increaseProbs(self, probs: numpy.ndarray, value, inds2ch: List[int]) +
+
+

value не повинно бути більше за sum(probs) +збільшуємо кожне число пропорційно:

+

якщо індекс ймовірності знаходиться у списку inds2ch, то ймовірність не змінюється

+

якщо індекс ймовірності p0 не належить списку inds2ch +та ймовірність p0 становить 50% від суми всіх ймовірностей, то p0 збільшиться на 50% від числа value

+

Args

+
+
probs
+
список ймовірностей
+
value
+
значення на яке сумарно треба зменшити ймовірності
+
inds2ch
+
список індексів ймовірностей, які не можна змінювати
+
+

Returns:

+
+ +Expand source code + +
def increaseProbs(self, probs: np.ndarray, value, inds2ch: List[int]):
+    """value не повинно бути більше за sum(probs)
+    збільшуємо кожне число пропорційно:
+
+    якщо індекс ймовірності знаходиться у списку inds2ch, то ймовірність не змінюється
+
+    якщо індекс ймовірності p0 не належить списку inds2ch
+    та ймовірність p0 становить 50% від суми всіх ймовірностей, то p0 збільшиться на 50% від числа value
+
+    Args:
+      probs: список ймовірностей
+      value: значення на яке сумарно треба зменшити ймовірності
+      inds2ch: список індексів ймовірностей, які не можна змінювати
+
+    Returns:
+
+    """
+    s = sum(probs[inds2ch])
+    percents = probs[inds2ch] / s
+    percentVals = percents * value
+    probs[inds2ch] = probs[inds2ch] + percentVals
+
+
+
+def mutatePerson(self, person: , indpb: float = 0.01, delta: float = 1, debug: bool = False) ‑> None +
+
+

функція реалізує мутацію певної особини для генетичного алгоритму, +де новий мутований ген вибирається, +як значення нормально розподіленої випадкової величини +з середнім рівним значенню поточного гену та дисперсією рівною одиничці

+

Args

+
+
person
+
особина, яка буде піддана мутації
+
indpb
+
ймовірність мутації одного гена особини(однієї ймовірності)
+
delta
+
максимальна різниця між поточним геном та мутованим
+
debug
+
параметр, який регулює інформативність функції
+
+

Returns:

+
+ +Expand source code + +
def mutatePerson(self, person: np.array, indpb: float = 0.01, delta: float = 1, debug: bool = False) -> None:
+    """функція реалізує мутацію певної особини для генетичного алгоритму,
+    де новий мутований ген вибирається,
+    як значення нормально розподіленої випадкової величини
+    з середнім рівним значенню поточного гену та дисперсією рівною одиничці
+
+    Args:
+      person: особина, яка буде піддана мутації
+      indpb: ймовірність мутації одного гена особини(однієї ймовірності)
+      delta: максимальна різниця між поточним геном та мутованим
+      debug: параметр, який регулює інформативність функції
+
+    Returns:
+
+    """
+
+    def get_truncated_normal(mean=0, sd=1, low=0, upp=10):
+        """
+
+        Args:
+          mean:  (Default value = 0)
+          sd:  (Default value = 1)
+          low:  (Default value = 0)
+          upp:  (Default value = 10)
+
+        Returns:
+
+        """
+        return truncnorm(
+            (low - mean) / sd, (upp - mean) / sd, loc=mean, scale=sd)
+
+    person0 = person.copy()
+    for idx in range(len(person)):
+        if np.random.uniform() <= indpb:
+            if debug is True:
+                print("mutation of one gen in one person_________________")
+            leftBorder = max(0, person[idx] - delta)
+            rightBorder = min(1, person[idx] + delta)
+            # newGenVal = random.triangular(leftBorder, rightBorder, random.gauss(person[idx], 1))
+            newGenVal = get_truncated_normal(mean=person[idx], sd=1, low=leftBorder, upp=rightBorder).rvs()
+            if debug is True:
+                print(f"old {person=}")
+                print(f"old gen:{person[idx]}, new gen:{newGenVal}\n")
+            # newGenVal = np.random.uniform(low=leftBorder, high=rightBorder, size=1)[0]
+            person[idx] = newGenVal
+            self.changeProbs(person, indsNot2ch=[idx, ])
+
+            if debug is True:
+                print(f"new {person=}\n")
+
+            if any(person < 0):
+                print(f"{person0=}")
+                print(f"{person=}")
+
+
+
+def selection(self, population: List[], surviveProbs) +
+
+

функція реалізовує відбір методом рулетки для генетичного алгоритму

+

Args

+
+
population
+
поточна популяція
+
surviveProbs
+
ймовірності виживання кожної особини з поточної популяції
+
+

Returns

+

нова популяція, такого ж розміру, що і стара, але без відсіяних особин

+
+ +Expand source code + +
def selection(self, population: List[np.array,], surviveProbs):
+    """функція реалізовує відбір методом рулетки для генетичного алгоритму
+
+    Args:
+      population: поточна популяція
+      surviveProbs: ймовірності виживання кожної особини з поточної популяції
+
+    Returns:
+      нова популяція, такого ж розміру, що і стара, але без відсіяних особин
+
+    """
+    #
+    indices = list(np.random.choice(range(len(population)), size=len(population), p=surviveProbs))
+    newPopulation = list(np.array(population)[indices])
+    return newPopulation
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/backend/doc/backend/benchmark.html b/backend/doc/backend/benchmark.html new file mode 100644 index 0000000..b5e2771 --- /dev/null +++ b/backend/doc/backend/benchmark.html @@ -0,0 +1,119 @@ + + + + + + +backend.benchmark API documentation + + + + + + + + + + + +
+
+
+

Module backend.benchmark

+
+
+
+ +Expand source code + +
import time
+
+
+def benchmark(func):
+    """функція декоратор, яка використовується для обрахунку часу роботи функції func
+
+    Args:
+      func: функція, яка декорується
+
+    Returns:
+      функція _benchmark
+
+    """
+    def _benchmark(*args, **kwargs):
+        t0 = time.time()
+        res = func(*args, **kwargs)
+        print(f"{func.__name__} elapsed {time.time() - t0} secs")
+        return res
+    return _benchmark
+
+
+
+
+
+
+
+

Functions

+
+
+def benchmark(func) +
+
+

функція декоратор, яка використовується для обрахунку часу роботи функції func

+

Args

+
+
func
+
функція, яка декорується
+
+

Returns

+

функція _benchmark

+
+ +Expand source code + +
def benchmark(func):
+    """функція декоратор, яка використовується для обрахунку часу роботи функції func
+
+    Args:
+      func: функція, яка декорується
+
+    Returns:
+      функція _benchmark
+
+    """
+    def _benchmark(*args, **kwargs):
+        t0 = time.time()
+        res = func(*args, **kwargs)
+        print(f"{func.__name__} elapsed {time.time() - t0} secs")
+        return res
+    return _benchmark
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/backend/doc/backend/index.html b/backend/doc/backend/index.html new file mode 100644 index 0000000..c9d708b --- /dev/null +++ b/backend/doc/backend/index.html @@ -0,0 +1,90 @@ + + + + + + +backend API documentation + + + + + + + + + + + +
+
+
+

Namespace backend

+
+
+
+
+

Sub-modules

+
+
backend.Bandit
+
+
+
+
backend.CustomExceptions
+
+
+
+
backend.GenAlgorithm
+
+
+
+
backend.benchmark
+
+
+
+
backend.test
+
+
+
+
backend.tests
+
+
+
+
backend.wsgi
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/backend/doc/backend/test.html b/backend/doc/backend/test.html new file mode 100644 index 0000000..22d91cd --- /dev/null +++ b/backend/doc/backend/test.html @@ -0,0 +1,429 @@ + + + + + + +backend.test API documentation + + + + + + + + + + + +
+
+
+

Module backend.test

+
+
+
+ +Expand source code + +
import time
+
+from backend.Bandit import OneHandBandit
+import numpy as np
+import matplotlib.pyplot as plt
+
+
+def test1():
+    """
+    5 разів рахує ймовірності для
+    двох заданих коефіцієнтів віддачі {0.9, 0.95};
+
+    при різній кількості ігор: {1000, 3000, 5000, 10000};
+
+    при різних початкових ймовірностях: {для ймовірностей с дуже великим значенням фітнесу, для рандомних ймовірностей}.
+
+    Записує час за який було обраховано кожний набір ймовірностей та коефіцієнт віддачі на цих ймовірностях,
+    обрахованний на 20000 ігор.
+    """
+    p2 = OneHandBandit(4, 3, 5)
+    p2.setPriceOfGame(10)
+    p2.setWinningCombs({(0, 0, 0, 0): 15, (1, 1, 1, 1): 15, (2, 2, 2, 2): 15, (3, 3, 3, 3): 15, (1, 2, 3, 4): 20, })
+    p2.addWinningComb((4, 4, 4, 4), 15)
+    # p2.addWinningComb((1, 1, 2, 2), 15)
+    # p2.addWinningComb((2, 2, 3, 3), 15)
+    # p2.addWinningComb((3, 3, 4, 4), 15)
+    np.random.seed(42)
+
+    # print("\ngamesCount=", 1000, "----------------\n")
+    # test11(p2, gamesCount=1000)
+    #
+    # print("\ngamesCount=", 3000, "----------------\n")
+    # test11(p2, gamesCount=3000)
+    #
+    print("\ngamesCount=", 5000, "----------------\n")
+    test11(p2, gamesCount=5000)
+
+    print("\ngamesCount=", 10000, "---------------\n")
+    test11(p2, gamesCount=10000)
+
+
+def test11(bandit, gamesCount):
+    print("\nstarts with bad values --------------\n")
+    population = [[0.34620235, 0.07168648, 0.06275534, 0.51076111, 0.00859472],
+                  [0.48455469, 0.29974074, 0.18556755, 0.02237517, 0.00776185],
+                  [0.4076457, 0.24869838, 0.14315961, 0.03328834000000005, 0.16720797],
+                  [0.15385934, 0.64327094, 0.14050975, 0.03888020000000005, 0.02347977],
+                  [0.32335616, 0.08463666, 0.28513406, 0.04416111, 0.26271201],
+                  [0.43279837, 0.25248431, 0.05619025, 0.2325301, 0.02599697], ]
+
+    print("\ngoal value: 0.9\n")
+    badFitnessVals = [bandit.genAlgo.fitness_func(i, 0.9, gamesCount=gamesCount) for i in population]
+    print(f"{badFitnessVals=}")
+    test12(bandit, 0.9, gamesCount, population=population)
+
+    print("\ngoal value: 0.95\n")
+    badFitnessVals = [bandit.genAlgo.fitness_func(i, 0.95, gamesCount=gamesCount) for i in population]
+    print(f"{badFitnessVals=}")
+    test12(bandit, 0.95, gamesCount, population=population)
+
+    print("\nstart with random values ------------\n")
+    print("\ngoal value: 0.9\n")
+    test12(bandit, 0.9, gamesCount)
+
+    print("\ngoal value: 0.95\n")
+    test12(bandit, 0.95, gamesCount)
+
+
+def test12(bandit, goalValue, gamesCount, population=None):
+    timeVals = []
+    returnCoefVals = []
+    for i in range(5):
+        print(i, end=" ")
+        t0 = time.time()
+        probs = bandit.genAlgo.geneticAlgorithm(5, goalValue, population=population, gamesCount=gamesCount, )
+        dt = time.time() - t0
+        timeVals.append(dt)
+        returnCoefVals.append(bandit.getReturnCoefWithNCores(gamesCount=20000, probs=probs))
+
+    timeVals = np.array(timeVals)
+    returnCoefVals = np.array(returnCoefVals)
+    print("For 200 iterations:")
+    print(f"min time: {min(timeVals)}, max time: {max(timeVals)}, mean time: {timeVals.mean()}")
+    print(f"{timeVals=}")
+    print(f"min returnCoefVals: {min(returnCoefVals)}, max returnCoefVals: {max(returnCoefVals)}, "
+          f"mean returnCoefVals: {returnCoefVals.mean()}")
+    print(f"{returnCoefVals=}")
+
+
+def test2():
+    """
+    малює графік збіжності коефіцієнту віддачі автомата до певного числа при збільшенні номера гри
+    """
+    p2 = OneHandBandit(4, 3, 5)
+    p2.setPriceOfGame(10)
+    p2.setWinningCombs({(0, 0, 0, 0): 15, (1, 1, 1, 1): 15, (2, 2, 2, 2): 15, (3, 3, 3, 3): 15, (1, 2, 3, 4): 20, })
+    p2.addWinningComb((4, 4, 4, 4), 15)
+
+    # np.random.seed(42)
+    # probs = p2.genBaseProbabilities(5, 0.9, debug=True)
+    # print(f"{probs=}")
+    # print("fitness val for probs and goalValue=0.9:", p2.genAlgo.fitness_func(probs=probs, goalValue=0.9))
+    # print("return coef (goalValue):", p2.getReturnCoefWithNCores(probs=probs, gamesCount=10 ** 4))
+    probs = [0.81674399, 0.10322578, 0.06974247, 0.0050837, 0.00520406]
+    gamesCount = 5000
+    meanRCs = p2.getReturnCoef(probs=probs, gamesCount=gamesCount, isGraph=True)
+    plt.plot(list(range(1, gamesCount + 1)), meanRCs)
+    plt.xlabel("Номер гри")
+    plt.ylabel("Середнє значення коефіцієнту віддачі")
+    plt.plot([0, gamesCount], [0.9, 0.9], color="yellow")
+    plt.annotate("0.9", (gamesCount - 5, 0.9))
+
+    plt.plot([gamesCount - 10, gamesCount - 10], [0.9, meanRCs[-1]], color="red")
+    plt.annotate(f"differance={round(abs(0.9 - meanRCs[-1]), 4)}", (gamesCount - 2 * 10 ** 3, 0.8))
+    plt.show()
+
+    gamesCount = 10000
+    meanRCs = p2.getReturnCoef(probs=probs, gamesCount=gamesCount, isGraph=True)
+    plt.plot(list(range(1, gamesCount + 1)), meanRCs)
+    plt.xlabel("Номер гри")
+    plt.ylabel("Середнє значення коефіцієнту віддачі")
+    plt.plot([0, gamesCount], [0.9, 0.9], color="yellow")
+    plt.annotate("0.9", (gamesCount - 5, 0.9))
+
+    plt.plot([gamesCount - 10, gamesCount - 10], [0.9, meanRCs[-1]], color="red")
+    plt.annotate(f"differance={round(abs(0.9 - meanRCs[-1]), 4)}", (gamesCount - 2 * 10 ** 3, 0.8))
+    plt.show()
+
+
+def test3():
+    """
+    функція, яка малює графік роботі генетичного алгоритму
+    """
+    p2 = OneHandBandit(4, 3, 5)
+    p2.setPriceOfGame(10)
+    p2.setWinningCombs({(0, 0, 0, 0): 15, (1, 1, 1, 1): 15, (2, 2, 2, 2): 15, (3, 3, 3, 3): 15, (1, 2, 3, 4): 20, })
+    p2.addWinningComb((4, 4, 4, 4), 15)
+    returnCoef = 0.95
+    returnCoefsInGenerations = p2.genAlgo.geneticAlgorithm(p2.picturesNumb, returnCoef, isGraph=True, debug=True)
+    plt.ylabel("коефіцієт віддачі")
+    plt.xlabel("покоління")
+    xs = [i * (len(returnCoefsInGenerations[0]) + 3) - 6 for i in range(1, len(returnCoefsInGenerations) + 1)]
+    labels = list(range(len(returnCoefsInGenerations)))
+    plt.xticks(xs, labels)
+    plt.plot([0, len(returnCoefsInGenerations) * (len(returnCoefsInGenerations[0]) + 3)], [returnCoef, returnCoef],
+             color="red")
+
+
+    for idx, returnCoefs in enumerate(returnCoefsInGenerations):
+        plt.plot([(len(returnCoefs) + 3) * idx + i for i in range(len(returnCoefs))], returnCoefs, "*")
+        x0 = (len(returnCoefs) + 3) * idx + len(returnCoefs) + 1
+        plt.plot([x0, x0], [0, 1], color="green")
+
+    plt.show()
+
+
+if __name__ == '__main__':
+    # test1()
+    # test2()
+    test3()
+
+
+
+
+
+
+
+

Functions

+
+
+def test1() +
+
+

5 разів рахує ймовірності для +двох заданих коефіцієнтів віддачі {0.9, 0.95};

+

при різній кількості ігор: {1000, 3000, 5000, 10000};

+

при різних початкових ймовірностях: {для ймовірностей с дуже великим значенням фітнесу, для рандомних ймовірностей}.

+

Записує час за який було обраховано кожний набір ймовірностей та коефіцієнт віддачі на цих ймовірностях, +обрахованний на 20000 ігор.

+
+ +Expand source code + +
def test1():
+    """
+    5 разів рахує ймовірності для
+    двох заданих коефіцієнтів віддачі {0.9, 0.95};
+
+    при різній кількості ігор: {1000, 3000, 5000, 10000};
+
+    при різних початкових ймовірностях: {для ймовірностей с дуже великим значенням фітнесу, для рандомних ймовірностей}.
+
+    Записує час за який було обраховано кожний набір ймовірностей та коефіцієнт віддачі на цих ймовірностях,
+    обрахованний на 20000 ігор.
+    """
+    p2 = OneHandBandit(4, 3, 5)
+    p2.setPriceOfGame(10)
+    p2.setWinningCombs({(0, 0, 0, 0): 15, (1, 1, 1, 1): 15, (2, 2, 2, 2): 15, (3, 3, 3, 3): 15, (1, 2, 3, 4): 20, })
+    p2.addWinningComb((4, 4, 4, 4), 15)
+    # p2.addWinningComb((1, 1, 2, 2), 15)
+    # p2.addWinningComb((2, 2, 3, 3), 15)
+    # p2.addWinningComb((3, 3, 4, 4), 15)
+    np.random.seed(42)
+
+    # print("\ngamesCount=", 1000, "----------------\n")
+    # test11(p2, gamesCount=1000)
+    #
+    # print("\ngamesCount=", 3000, "----------------\n")
+    # test11(p2, gamesCount=3000)
+    #
+    print("\ngamesCount=", 5000, "----------------\n")
+    test11(p2, gamesCount=5000)
+
+    print("\ngamesCount=", 10000, "---------------\n")
+    test11(p2, gamesCount=10000)
+
+
+
+def test11(bandit, gamesCount) +
+
+
+
+ +Expand source code + +
def test11(bandit, gamesCount):
+    print("\nstarts with bad values --------------\n")
+    population = [[0.34620235, 0.07168648, 0.06275534, 0.51076111, 0.00859472],
+                  [0.48455469, 0.29974074, 0.18556755, 0.02237517, 0.00776185],
+                  [0.4076457, 0.24869838, 0.14315961, 0.03328834000000005, 0.16720797],
+                  [0.15385934, 0.64327094, 0.14050975, 0.03888020000000005, 0.02347977],
+                  [0.32335616, 0.08463666, 0.28513406, 0.04416111, 0.26271201],
+                  [0.43279837, 0.25248431, 0.05619025, 0.2325301, 0.02599697], ]
+
+    print("\ngoal value: 0.9\n")
+    badFitnessVals = [bandit.genAlgo.fitness_func(i, 0.9, gamesCount=gamesCount) for i in population]
+    print(f"{badFitnessVals=}")
+    test12(bandit, 0.9, gamesCount, population=population)
+
+    print("\ngoal value: 0.95\n")
+    badFitnessVals = [bandit.genAlgo.fitness_func(i, 0.95, gamesCount=gamesCount) for i in population]
+    print(f"{badFitnessVals=}")
+    test12(bandit, 0.95, gamesCount, population=population)
+
+    print("\nstart with random values ------------\n")
+    print("\ngoal value: 0.9\n")
+    test12(bandit, 0.9, gamesCount)
+
+    print("\ngoal value: 0.95\n")
+    test12(bandit, 0.95, gamesCount)
+
+
+
+def test12(bandit, goalValue, gamesCount, population=None) +
+
+
+
+ +Expand source code + +
def test12(bandit, goalValue, gamesCount, population=None):
+    timeVals = []
+    returnCoefVals = []
+    for i in range(5):
+        print(i, end=" ")
+        t0 = time.time()
+        probs = bandit.genAlgo.geneticAlgorithm(5, goalValue, population=population, gamesCount=gamesCount, )
+        dt = time.time() - t0
+        timeVals.append(dt)
+        returnCoefVals.append(bandit.getReturnCoefWithNCores(gamesCount=20000, probs=probs))
+
+    timeVals = np.array(timeVals)
+    returnCoefVals = np.array(returnCoefVals)
+    print("For 200 iterations:")
+    print(f"min time: {min(timeVals)}, max time: {max(timeVals)}, mean time: {timeVals.mean()}")
+    print(f"{timeVals=}")
+    print(f"min returnCoefVals: {min(returnCoefVals)}, max returnCoefVals: {max(returnCoefVals)}, "
+          f"mean returnCoefVals: {returnCoefVals.mean()}")
+    print(f"{returnCoefVals=}")
+
+
+
+def test2() +
+
+

малює графік збіжності коефіцієнту віддачі автомата до певного числа при збільшенні номера гри

+
+ +Expand source code + +
def test2():
+    """
+    малює графік збіжності коефіцієнту віддачі автомата до певного числа при збільшенні номера гри
+    """
+    p2 = OneHandBandit(4, 3, 5)
+    p2.setPriceOfGame(10)
+    p2.setWinningCombs({(0, 0, 0, 0): 15, (1, 1, 1, 1): 15, (2, 2, 2, 2): 15, (3, 3, 3, 3): 15, (1, 2, 3, 4): 20, })
+    p2.addWinningComb((4, 4, 4, 4), 15)
+
+    # np.random.seed(42)
+    # probs = p2.genBaseProbabilities(5, 0.9, debug=True)
+    # print(f"{probs=}")
+    # print("fitness val for probs and goalValue=0.9:", p2.genAlgo.fitness_func(probs=probs, goalValue=0.9))
+    # print("return coef (goalValue):", p2.getReturnCoefWithNCores(probs=probs, gamesCount=10 ** 4))
+    probs = [0.81674399, 0.10322578, 0.06974247, 0.0050837, 0.00520406]
+    gamesCount = 5000
+    meanRCs = p2.getReturnCoef(probs=probs, gamesCount=gamesCount, isGraph=True)
+    plt.plot(list(range(1, gamesCount + 1)), meanRCs)
+    plt.xlabel("Номер гри")
+    plt.ylabel("Середнє значення коефіцієнту віддачі")
+    plt.plot([0, gamesCount], [0.9, 0.9], color="yellow")
+    plt.annotate("0.9", (gamesCount - 5, 0.9))
+
+    plt.plot([gamesCount - 10, gamesCount - 10], [0.9, meanRCs[-1]], color="red")
+    plt.annotate(f"differance={round(abs(0.9 - meanRCs[-1]), 4)}", (gamesCount - 2 * 10 ** 3, 0.8))
+    plt.show()
+
+    gamesCount = 10000
+    meanRCs = p2.getReturnCoef(probs=probs, gamesCount=gamesCount, isGraph=True)
+    plt.plot(list(range(1, gamesCount + 1)), meanRCs)
+    plt.xlabel("Номер гри")
+    plt.ylabel("Середнє значення коефіцієнту віддачі")
+    plt.plot([0, gamesCount], [0.9, 0.9], color="yellow")
+    plt.annotate("0.9", (gamesCount - 5, 0.9))
+
+    plt.plot([gamesCount - 10, gamesCount - 10], [0.9, meanRCs[-1]], color="red")
+    plt.annotate(f"differance={round(abs(0.9 - meanRCs[-1]), 4)}", (gamesCount - 2 * 10 ** 3, 0.8))
+    plt.show()
+
+
+
+def test3() +
+
+

функція, яка малює графік роботі генетичного алгоритму

+
+ +Expand source code + +
def test3():
+    """
+    функція, яка малює графік роботі генетичного алгоритму
+    """
+    p2 = OneHandBandit(4, 3, 5)
+    p2.setPriceOfGame(10)
+    p2.setWinningCombs({(0, 0, 0, 0): 15, (1, 1, 1, 1): 15, (2, 2, 2, 2): 15, (3, 3, 3, 3): 15, (1, 2, 3, 4): 20, })
+    p2.addWinningComb((4, 4, 4, 4), 15)
+    returnCoef = 0.95
+    returnCoefsInGenerations = p2.genAlgo.geneticAlgorithm(p2.picturesNumb, returnCoef, isGraph=True, debug=True)
+    plt.ylabel("коефіцієт віддачі")
+    plt.xlabel("покоління")
+    xs = [i * (len(returnCoefsInGenerations[0]) + 3) - 6 for i in range(1, len(returnCoefsInGenerations) + 1)]
+    labels = list(range(len(returnCoefsInGenerations)))
+    plt.xticks(xs, labels)
+    plt.plot([0, len(returnCoefsInGenerations) * (len(returnCoefsInGenerations[0]) + 3)], [returnCoef, returnCoef],
+             color="red")
+
+
+    for idx, returnCoefs in enumerate(returnCoefsInGenerations):
+        plt.plot([(len(returnCoefs) + 3) * idx + i for i in range(len(returnCoefs))], returnCoefs, "*")
+        x0 = (len(returnCoefs) + 3) * idx + len(returnCoefs) + 1
+        plt.plot([x0, x0], [0, 1], color="green")
+
+    plt.show()
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/backend/doc/backend/tests/OneHandBandit_test.html b/backend/doc/backend/tests/OneHandBandit_test.html new file mode 100644 index 0000000..64226b4 --- /dev/null +++ b/backend/doc/backend/tests/OneHandBandit_test.html @@ -0,0 +1,522 @@ + + + + + + +backend.tests.OneHandBandit_test API documentation + + + + + + + + + + + +
+
+
+

Module backend.tests.OneHandBandit_test

+
+
+
+ +Expand source code + +
from unittest import TestCase, main
+
+from backend.Bandit import OneHandBandit
+from backend.CustomExceptions import *
+
+
+class OneHandBanditTest(TestCase):
+    def setUp(self):
+        self.banditObj = OneHandBandit(3, 3, 8)
+
+    def test_setWinningCombs(self):
+        combs = {(7, 7, 7): 100, (3, 5, 7): 200, (4, 3, 0): 500}
+        self.banditObj.setWinningCombs(combs)
+        # combs[(4,3,0)] = 5
+        self.assertDictEqual(combs, self.banditObj.combinations)
+
+    def test_setWinningCombs_exception_0(self):
+        with self.assertRaises(CombinationLengthException):
+            self.banditObj.setWinningCombs({(7, 7, 7): 100, (3, 5, 7): 200, (4, 3, 0, 0): 500})
+
+    def test_setWinningCombs_exception_1(self):
+        with self.assertRaises(CombinationValuesException):
+            self.banditObj.setWinningCombs({(7, 7, 8): 100, (3, 5, 7): 200, (4, 3, 0): 500})
+
+    def test_setWinningCombs_exception_2(self):
+        with self.assertRaises(WinningMoneyException):
+            self.banditObj.setWinningCombs({(7, 7, 7): -100, (3, 5, 7): 200, (4, 3, 0): 500})
+
+    def test_addWinningComb(self):
+        comb = (7, 7, 7)
+        win = 1000
+        self.banditObj.addWinningComb(comb, win)
+        # win = 500
+        self.assertEqual(self.banditObj.combinations[comb], win)
+
+    def test_setPriceOfGame(self):
+        price = 10
+        self.banditObj.setPriceOfGame(price)
+        self.assertEqual(self.banditObj.price, price)
+
+    def test_startGame(self):
+        money = 100
+        self.banditObj.startGame(money)
+        self.assertEqual(self.banditObj.money, money)
+
+    def test_setProbabilities_NoneProbs(self):
+        probs = None
+        self.banditObj.setProbabilities(probs)
+        self.assertListEqual(self.banditObj.probabilities,
+                             [1 / self.banditObj.picturesNumb for _ in range(self.banditObj.picturesNumb)])
+
+    def test_setProbabilities(self):
+        probs = [0.1, ] * 7 + [0.3]
+        self.banditObj.setProbabilities(probs)
+        # probs[0] = 0.5
+        self.assertListEqual(list(self.banditObj.probabilities), probs)
+
+    def test_setProbabilities_exception_0(self):
+        probs = [0.1, ] * 7 + [0.4]
+        with self.assertRaises(ProbabilitiesSumException):
+            self.banditObj.setProbabilities(probs=probs)
+
+    def test_setProbabilities_exception_1(self):
+        probs = [0.1, ] * 8 + [0.2]
+        with self.assertRaises(ProbabilitiesArrayLengthException):
+            self.banditObj.setProbabilities(probs=probs)
+
+    def test_setProbabilitiesFromTxtFile(self):
+        filename = "backend/tests/test_probabilities.txt"
+        with open(filename, "r") as f:
+            probs = list(map(float, f.readline().split(" ")))
+        self.banditObj.setProbabilitiesFromTxtFile(filename)
+        self.assertListEqual(probs, self.banditObj.probabilities)
+
+    def test_setState_exception_0(self):
+        state = [[1, 2, 3], [2, 3, 4], [3, 4, 3]]
+        with self.assertRaises(SettingStateException):
+            self.banditObj.setState(state)
+
+    def test_turnColumn(self):
+        turnColumn = self.banditObj.turnColumn(0)
+        self.assertNotIn(-1, turnColumn)
+        self.assertEqual(self.banditObj.rowsNumb, len(turnColumn))
+
+
+if __name__ == '__main__':
+    main()
+
+
+
+
+
+
+
+
+
+

Classes

+
+
+class OneHandBanditTest +(methodName='runTest') +
+
+

A class whose instances are single test cases.

+

By default, the test code itself should be placed in a method named +'runTest'.

+

If the fixture may be used for many test cases, create as +many test methods as are needed. When instantiating such a TestCase +subclass, specify in the constructor arguments the name of the test method +that the instance is to execute.

+

Test authors should subclass TestCase for their own tests. Construction +and deconstruction of the test's environment ('fixture') can be +implemented by overriding the 'setUp' and 'tearDown' methods respectively.

+

If it is necessary to override the init method, the base class +init method must always be called. It is important that subclasses +should not change the signature of their init method, since instances +of the classes are instantiated automatically by parts of the framework +in order to be run.

+

When subclassing TestCase, you can set these attributes: +* failureException: determines which exception will be raised when +the instance's assertion methods fail; test methods raising this +exception will be deemed to have 'failed' rather than 'errored'. +* longMessage: determines whether long messages (including repr of +objects used in assert methods) will be printed on failure in addition +to any explicit message passed. +* maxDiff: sets the maximum length of a diff in failure messages +by assert methods using difflib. It is looked up as an instance +attribute so can be configured by individual tests if required.

+

Create an instance of the class that will use the named test +method when executed. Raises a ValueError if the instance does +not have a method with the specified name.

+
+ +Expand source code + +
class OneHandBanditTest(TestCase):
+    def setUp(self):
+        self.banditObj = OneHandBandit(3, 3, 8)
+
+    def test_setWinningCombs(self):
+        combs = {(7, 7, 7): 100, (3, 5, 7): 200, (4, 3, 0): 500}
+        self.banditObj.setWinningCombs(combs)
+        # combs[(4,3,0)] = 5
+        self.assertDictEqual(combs, self.banditObj.combinations)
+
+    def test_setWinningCombs_exception_0(self):
+        with self.assertRaises(CombinationLengthException):
+            self.banditObj.setWinningCombs({(7, 7, 7): 100, (3, 5, 7): 200, (4, 3, 0, 0): 500})
+
+    def test_setWinningCombs_exception_1(self):
+        with self.assertRaises(CombinationValuesException):
+            self.banditObj.setWinningCombs({(7, 7, 8): 100, (3, 5, 7): 200, (4, 3, 0): 500})
+
+    def test_setWinningCombs_exception_2(self):
+        with self.assertRaises(WinningMoneyException):
+            self.banditObj.setWinningCombs({(7, 7, 7): -100, (3, 5, 7): 200, (4, 3, 0): 500})
+
+    def test_addWinningComb(self):
+        comb = (7, 7, 7)
+        win = 1000
+        self.banditObj.addWinningComb(comb, win)
+        # win = 500
+        self.assertEqual(self.banditObj.combinations[comb], win)
+
+    def test_setPriceOfGame(self):
+        price = 10
+        self.banditObj.setPriceOfGame(price)
+        self.assertEqual(self.banditObj.price, price)
+
+    def test_startGame(self):
+        money = 100
+        self.banditObj.startGame(money)
+        self.assertEqual(self.banditObj.money, money)
+
+    def test_setProbabilities_NoneProbs(self):
+        probs = None
+        self.banditObj.setProbabilities(probs)
+        self.assertListEqual(self.banditObj.probabilities,
+                             [1 / self.banditObj.picturesNumb for _ in range(self.banditObj.picturesNumb)])
+
+    def test_setProbabilities(self):
+        probs = [0.1, ] * 7 + [0.3]
+        self.banditObj.setProbabilities(probs)
+        # probs[0] = 0.5
+        self.assertListEqual(list(self.banditObj.probabilities), probs)
+
+    def test_setProbabilities_exception_0(self):
+        probs = [0.1, ] * 7 + [0.4]
+        with self.assertRaises(ProbabilitiesSumException):
+            self.banditObj.setProbabilities(probs=probs)
+
+    def test_setProbabilities_exception_1(self):
+        probs = [0.1, ] * 8 + [0.2]
+        with self.assertRaises(ProbabilitiesArrayLengthException):
+            self.banditObj.setProbabilities(probs=probs)
+
+    def test_setProbabilitiesFromTxtFile(self):
+        filename = "backend/tests/test_probabilities.txt"
+        with open(filename, "r") as f:
+            probs = list(map(float, f.readline().split(" ")))
+        self.banditObj.setProbabilitiesFromTxtFile(filename)
+        self.assertListEqual(probs, self.banditObj.probabilities)
+
+    def test_setState_exception_0(self):
+        state = [[1, 2, 3], [2, 3, 4], [3, 4, 3]]
+        with self.assertRaises(SettingStateException):
+            self.banditObj.setState(state)
+
+    def test_turnColumn(self):
+        turnColumn = self.banditObj.turnColumn(0)
+        self.assertNotIn(-1, turnColumn)
+        self.assertEqual(self.banditObj.rowsNumb, len(turnColumn))
+
+

Ancestors

+
    +
  • unittest.case.TestCase
  • +
+

Methods

+
+
+def setUp(self) +
+
+

Hook method for setting up the test fixture before exercising it.

+
+ +Expand source code + +
def setUp(self):
+    self.banditObj = OneHandBandit(3, 3, 8)
+
+
+
+def test_addWinningComb(self) +
+
+
+
+ +Expand source code + +
def test_addWinningComb(self):
+    comb = (7, 7, 7)
+    win = 1000
+    self.banditObj.addWinningComb(comb, win)
+    # win = 500
+    self.assertEqual(self.banditObj.combinations[comb], win)
+
+
+
+def test_setPriceOfGame(self) +
+
+
+
+ +Expand source code + +
def test_setPriceOfGame(self):
+    price = 10
+    self.banditObj.setPriceOfGame(price)
+    self.assertEqual(self.banditObj.price, price)
+
+
+
+def test_setProbabilities(self) +
+
+
+
+ +Expand source code + +
def test_setProbabilities(self):
+    probs = [0.1, ] * 7 + [0.3]
+    self.banditObj.setProbabilities(probs)
+    # probs[0] = 0.5
+    self.assertListEqual(list(self.banditObj.probabilities), probs)
+
+
+
+def test_setProbabilitiesFromTxtFile(self) +
+
+
+
+ +Expand source code + +
def test_setProbabilitiesFromTxtFile(self):
+    filename = "backend/tests/test_probabilities.txt"
+    with open(filename, "r") as f:
+        probs = list(map(float, f.readline().split(" ")))
+    self.banditObj.setProbabilitiesFromTxtFile(filename)
+    self.assertListEqual(probs, self.banditObj.probabilities)
+
+
+
+def test_setProbabilities_NoneProbs(self) +
+
+
+
+ +Expand source code + +
def test_setProbabilities_NoneProbs(self):
+    probs = None
+    self.banditObj.setProbabilities(probs)
+    self.assertListEqual(self.banditObj.probabilities,
+                         [1 / self.banditObj.picturesNumb for _ in range(self.banditObj.picturesNumb)])
+
+
+
+def test_setProbabilities_exception_0(self) +
+
+
+
+ +Expand source code + +
def test_setProbabilities_exception_0(self):
+    probs = [0.1, ] * 7 + [0.4]
+    with self.assertRaises(ProbabilitiesSumException):
+        self.banditObj.setProbabilities(probs=probs)
+
+
+
+def test_setProbabilities_exception_1(self) +
+
+
+
+ +Expand source code + +
def test_setProbabilities_exception_1(self):
+    probs = [0.1, ] * 8 + [0.2]
+    with self.assertRaises(ProbabilitiesArrayLengthException):
+        self.banditObj.setProbabilities(probs=probs)
+
+
+
+def test_setState_exception_0(self) +
+
+
+
+ +Expand source code + +
def test_setState_exception_0(self):
+    state = [[1, 2, 3], [2, 3, 4], [3, 4, 3]]
+    with self.assertRaises(SettingStateException):
+        self.banditObj.setState(state)
+
+
+
+def test_setWinningCombs(self) +
+
+
+
+ +Expand source code + +
def test_setWinningCombs(self):
+    combs = {(7, 7, 7): 100, (3, 5, 7): 200, (4, 3, 0): 500}
+    self.banditObj.setWinningCombs(combs)
+    # combs[(4,3,0)] = 5
+    self.assertDictEqual(combs, self.banditObj.combinations)
+
+
+
+def test_setWinningCombs_exception_0(self) +
+
+
+
+ +Expand source code + +
def test_setWinningCombs_exception_0(self):
+    with self.assertRaises(CombinationLengthException):
+        self.banditObj.setWinningCombs({(7, 7, 7): 100, (3, 5, 7): 200, (4, 3, 0, 0): 500})
+
+
+
+def test_setWinningCombs_exception_1(self) +
+
+
+
+ +Expand source code + +
def test_setWinningCombs_exception_1(self):
+    with self.assertRaises(CombinationValuesException):
+        self.banditObj.setWinningCombs({(7, 7, 8): 100, (3, 5, 7): 200, (4, 3, 0): 500})
+
+
+
+def test_setWinningCombs_exception_2(self) +
+
+
+
+ +Expand source code + +
def test_setWinningCombs_exception_2(self):
+    with self.assertRaises(WinningMoneyException):
+        self.banditObj.setWinningCombs({(7, 7, 7): -100, (3, 5, 7): 200, (4, 3, 0): 500})
+
+
+
+def test_startGame(self) +
+
+
+
+ +Expand source code + +
def test_startGame(self):
+    money = 100
+    self.banditObj.startGame(money)
+    self.assertEqual(self.banditObj.money, money)
+
+
+
+def test_turnColumn(self) +
+
+
+
+ +Expand source code + +
def test_turnColumn(self):
+    turnColumn = self.banditObj.turnColumn(0)
+    self.assertNotIn(-1, turnColumn)
+    self.assertEqual(self.banditObj.rowsNumb, len(turnColumn))
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/backend/doc/backend/tests/index.html b/backend/doc/backend/tests/index.html new file mode 100644 index 0000000..f2028a2 --- /dev/null +++ b/backend/doc/backend/tests/index.html @@ -0,0 +1,65 @@ + + + + + + +backend.tests API documentation + + + + + + + + + + + +
+
+
+

Namespace backend.tests

+
+
+
+
+

Sub-modules

+
+
backend.tests.OneHandBandit_test
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/backend/doc/backend/wsgi.html b/backend/doc/backend/wsgi.html new file mode 100644 index 0000000..01994b6 --- /dev/null +++ b/backend/doc/backend/wsgi.html @@ -0,0 +1,159 @@ + + + + + + +backend.wsgi API documentation + + + + + + + + + + + +
+
+
+

Module backend.wsgi

+
+
+
+ +Expand source code + +
from flask import Flask, Response
+
+from backend.Bandit import OneHandBandit
+
+app = Flask(__name__)
+
+# відображення індексу картинки до назви картинки
+dict_ = {0: "at_at",
+         1: "c3po",
+         2: "darth_vader",
+         3: "death_star",
+         4: "falcon",
+         5: "r2d2"}
+
+wastedMoney = 0
+wonMoney = 0
+
+
+@app.route("/getState", methods=["GET", ])
+def getState():
+    """
+    обробляє запит сайту щодо даних: currentState, wonMoney, returnCoef
+    Returns:
+        об'єкт класу Response, який містить всі необхідні дані, а саме: currentState, wonMoney, returnCoef
+    """
+    global wastedMoney, wonMoney
+    bandit.startGame(bandit.price)
+    won = bandit.play(n=1)[0]
+
+    wastedMoney = wastedMoney + bandit.price
+    wonMoney = wonMoney + won
+    returnCoef = (wonMoney / wastedMoney)
+    print(wonMoney)
+    print(wastedMoney)
+
+    currentState = bandit.getState().T
+
+    currentState = [dict_[int(i)] for i in currentState.flatten()]
+    print(','.join(currentState) + ',' + str(won) + ',' + str(round(returnCoef, 4)))
+    resp = Response(','.join(currentState) + ',' + str(won) + ',' + str(round(returnCoef, 4)))
+    resp.headers['Access-Control-Allow-Origin'] = '*'
+    return resp
+
+
+if __name__ == '__main__':
+    bandit = OneHandBandit(4, 3, 5)
+
+    # l = bandit.genBaseProbabilities(bandit.picturesNumb, returnCoef)
+    # bandit.setProbabilities(l)
+    bandit.setProbabilitiesFromTxtFile("probabilities.txt")
+
+    bandit.setPriceOfGame(10)
+    bandit.setWinningCombs({(0, 0, 0, 0): 15, (1, 1, 1, 1): 15, (2, 2, 2, 2): 15, (3, 3, 3, 3): 15, (1, 2, 3, 4): 20, })
+    bandit.addWinningComb((4, 4, 4, 4), 15)
+
+    app.run(debug=True, host="0.0.0.0")
+
+
+
+
+
+
+
+

Functions

+
+
+def getState() +
+
+

обробляє запит сайту щодо даних: currentState, wonMoney, returnCoef

+

Returns

+

об'єкт класу Response, який містить всі необхідні дані, а саме: currentState, wonMoney, returnCoef

+
+ +Expand source code + +
@app.route("/getState", methods=["GET", ])
+def getState():
+    """
+    обробляє запит сайту щодо даних: currentState, wonMoney, returnCoef
+    Returns:
+        об'єкт класу Response, який містить всі необхідні дані, а саме: currentState, wonMoney, returnCoef
+    """
+    global wastedMoney, wonMoney
+    bandit.startGame(bandit.price)
+    won = bandit.play(n=1)[0]
+
+    wastedMoney = wastedMoney + bandit.price
+    wonMoney = wonMoney + won
+    returnCoef = (wonMoney / wastedMoney)
+    print(wonMoney)
+    print(wastedMoney)
+
+    currentState = bandit.getState().T
+
+    currentState = [dict_[int(i)] for i in currentState.flatten()]
+    print(','.join(currentState) + ',' + str(won) + ',' + str(round(returnCoef, 4)))
+    resp = Response(','.join(currentState) + ',' + str(won) + ',' + str(round(returnCoef, 4)))
+    resp.headers['Access-Control-Allow-Origin'] = '*'
+    return resp
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/backend/probabilities.txt b/backend/probabilities.txt new file mode 100644 index 0000000..1800424 --- /dev/null +++ b/backend/probabilities.txt @@ -0,0 +1 @@ +0.008082330274997888 0.09521255517386346 0.0007740974727614077 0.05778087326126356 0.8381501438171137 \ No newline at end of file diff --git a/backend/test.py b/backend/test.py new file mode 100644 index 0000000..b98671a --- /dev/null +++ b/backend/test.py @@ -0,0 +1,160 @@ +import time + +from backend.Bandit import OneHandBandit +import numpy as np +import matplotlib.pyplot as plt + + +def test1(): + """ + 5 разів рахує ймовірності для + двох заданих коефіцієнтів віддачі {0.9, 0.95}; + + при різній кількості ігор: {1000, 3000, 5000, 10000}; + + при різних початкових ймовірностях: {для ймовірностей с дуже великим значенням фітнесу, для рандомних ймовірностей}. + + Записує час за який було обраховано кожний набір ймовірностей та коефіцієнт віддачі на цих ймовірностях, + обрахованний на 20000 ігор. + """ + p2 = OneHandBandit(4, 3, 5) + p2.setPriceOfGame(10) + p2.setWinningCombs({(0, 0, 0, 0): 15, (1, 1, 1, 1): 15, (2, 2, 2, 2): 15, (3, 3, 3, 3): 15, (1, 2, 3, 4): 20, }) + p2.addWinningComb((4, 4, 4, 4), 15) + # p2.addWinningComb((1, 1, 2, 2), 15) + # p2.addWinningComb((2, 2, 3, 3), 15) + # p2.addWinningComb((3, 3, 4, 4), 15) + np.random.seed(42) + + # print("\ngamesCount=", 1000, "----------------\n") + # test11(p2, gamesCount=1000) + # + # print("\ngamesCount=", 3000, "----------------\n") + # test11(p2, gamesCount=3000) + # + print("\ngamesCount=", 5000, "----------------\n") + test11(p2, gamesCount=5000) + + print("\ngamesCount=", 10000, "---------------\n") + test11(p2, gamesCount=10000) + + +def test11(bandit, gamesCount): + print("\nstarts with bad values --------------\n") + population = [[0.34620235, 0.07168648, 0.06275534, 0.51076111, 0.00859472], + [0.48455469, 0.29974074, 0.18556755, 0.02237517, 0.00776185], + [0.4076457, 0.24869838, 0.14315961, 0.03328834000000005, 0.16720797], + [0.15385934, 0.64327094, 0.14050975, 0.03888020000000005, 0.02347977], + [0.32335616, 0.08463666, 0.28513406, 0.04416111, 0.26271201], + [0.43279837, 0.25248431, 0.05619025, 0.2325301, 0.02599697], ] + + print("\ngoal value: 0.9\n") + badFitnessVals = [bandit.genAlgo.fitness_func(i, 0.9, gamesCount=gamesCount) for i in population] + print(f"{badFitnessVals=}") + test12(bandit, 0.9, gamesCount, population=population) + + print("\ngoal value: 0.95\n") + badFitnessVals = [bandit.genAlgo.fitness_func(i, 0.95, gamesCount=gamesCount) for i in population] + print(f"{badFitnessVals=}") + test12(bandit, 0.95, gamesCount, population=population) + + print("\nstart with random values ------------\n") + print("\ngoal value: 0.9\n") + test12(bandit, 0.9, gamesCount) + + print("\ngoal value: 0.95\n") + test12(bandit, 0.95, gamesCount) + + +def test12(bandit, goalValue, gamesCount, population=None): + timeVals = [] + returnCoefVals = [] + for i in range(5): + print(i, end=" ") + t0 = time.time() + probs = bandit.genAlgo.geneticAlgorithm(5, goalValue, population=population, gamesCount=gamesCount, ) + dt = time.time() - t0 + timeVals.append(dt) + returnCoefVals.append(bandit.getReturnCoefWithNCores(gamesCount=20000, probs=probs)) + + timeVals = np.array(timeVals) + returnCoefVals = np.array(returnCoefVals) + print("For 200 iterations:") + print(f"min time: {min(timeVals)}, max time: {max(timeVals)}, mean time: {timeVals.mean()}") + print(f"{timeVals=}") + print(f"min returnCoefVals: {min(returnCoefVals)}, max returnCoefVals: {max(returnCoefVals)}, " + f"mean returnCoefVals: {returnCoefVals.mean()}") + print(f"{returnCoefVals=}") + + +def test2(): + """ + малює графік збіжності коефіцієнту віддачі автомата до певного числа при збільшенні номера гри + """ + p2 = OneHandBandit(4, 3, 5) + p2.setPriceOfGame(10) + p2.setWinningCombs({(0, 0, 0, 0): 15, (1, 1, 1, 1): 15, (2, 2, 2, 2): 15, (3, 3, 3, 3): 15, (1, 2, 3, 4): 20, }) + p2.addWinningComb((4, 4, 4, 4), 15) + + # np.random.seed(42) + # probs = p2.genBaseProbabilities(5, 0.9, debug=True) + # print(f"{probs=}") + # print("fitness val for probs and goalValue=0.9:", p2.genAlgo.fitness_func(probs=probs, goalValue=0.9)) + # print("return coef (goalValue):", p2.getReturnCoefWithNCores(probs=probs, gamesCount=10 ** 4)) + probs = [0.81674399, 0.10322578, 0.06974247, 0.0050837, 0.00520406] + gamesCount = 5000 + meanRCs = p2.getReturnCoef(probs=probs, gamesCount=gamesCount, isGraph=True) + plt.plot(list(range(1, gamesCount + 1)), meanRCs) + plt.xlabel("Номер гри") + plt.ylabel("Середнє значення коефіцієнту віддачі") + plt.plot([0, gamesCount], [0.9, 0.9], color="yellow") + plt.annotate("0.9", (gamesCount - 5, 0.9)) + + plt.plot([gamesCount - 10, gamesCount - 10], [0.9, meanRCs[-1]], color="red") + plt.annotate(f"differance={round(abs(0.9 - meanRCs[-1]), 4)}", (gamesCount - 2 * 10 ** 3, 0.8)) + plt.show() + + gamesCount = 10000 + meanRCs = p2.getReturnCoef(probs=probs, gamesCount=gamesCount, isGraph=True) + plt.plot(list(range(1, gamesCount + 1)), meanRCs) + plt.xlabel("Номер гри") + plt.ylabel("Середнє значення коефіцієнту віддачі") + plt.plot([0, gamesCount], [0.9, 0.9], color="yellow") + plt.annotate("0.9", (gamesCount - 5, 0.9)) + + plt.plot([gamesCount - 10, gamesCount - 10], [0.9, meanRCs[-1]], color="red") + plt.annotate(f"differance={round(abs(0.9 - meanRCs[-1]), 4)}", (gamesCount - 2 * 10 ** 3, 0.8)) + plt.show() + + +def test3(): + """ + функція, яка малює графік роботі генетичного алгоритму + """ + p2 = OneHandBandit(4, 3, 5) + p2.setPriceOfGame(10) + p2.setWinningCombs({(0, 0, 0, 0): 15, (1, 1, 1, 1): 15, (2, 2, 2, 2): 15, (3, 3, 3, 3): 15, (1, 2, 3, 4): 20, }) + p2.addWinningComb((4, 4, 4, 4), 15) + returnCoef = 0.95 + returnCoefsInGenerations = p2.genAlgo.geneticAlgorithm(p2.picturesNumb, returnCoef, isGraph=True, debug=True) + plt.ylabel("коефіцієт віддачі") + plt.xlabel("покоління") + xs = [i * (len(returnCoefsInGenerations[0]) + 3) - 6 for i in range(1, len(returnCoefsInGenerations) + 1)] + labels = list(range(len(returnCoefsInGenerations))) + plt.xticks(xs, labels) + plt.plot([0, len(returnCoefsInGenerations) * (len(returnCoefsInGenerations[0]) + 3)], [returnCoef, returnCoef], + color="red") + + + for idx, returnCoefs in enumerate(returnCoefsInGenerations): + plt.plot([(len(returnCoefs) + 3) * idx + i for i in range(len(returnCoefs))], returnCoefs, "*") + x0 = (len(returnCoefs) + 3) * idx + len(returnCoefs) + 1 + plt.plot([x0, x0], [0, 1], color="green") + + plt.show() + + +if __name__ == '__main__': + # test1() + # test2() + test3() diff --git a/backend/test_files/test1.txt b/backend/test_files/test1.txt new file mode 100644 index 0000000..8ca7a2e --- /dev/null +++ b/backend/test_files/test1.txt @@ -0,0 +1,183 @@ +файл містить вивід функції test1 з backend/test.py + +gamesCount= 1000 ---------------- + + +starts with bad values -------------- + + +goal value: 0.9 + +badFitnessVals=[0.6717221828490433, 0.7435447673045121, 0.8069808646350106, 0.5758327427356486, 0.8300023623907394, 0.8033723127805339] +0 1 2 3 4 For 5 iterations: +min time: 149.7915756702423, max time: 694.9565689563751, mean time: 329.48206744194033 +timeVals=array([149.79157567, 236.70668268, 282.7156949 , 694.95656896, + 283.239815 ]) +min returnCoefVals: 0.21132464820694963, max returnCoefVals: 0.9001994202418744, mean returnCoefVals: 0.7395649235088427 +returnCoefVals=array([0.84720295, 0.87837432, 0.90019942, 0.21132465, 0.86072328]) + +goal value: 0.95 + +badFitnessVals=[0.7405031892274981, 0.7116406803685329, 0.8484467280888258, 0.6157158043940467, 0.8754488542404913, 0.8051263879045594] +0 1 2 3 4 For 5 iterations: +min time: 82.46748614311218, max time: 406.40651512145996, mean time: 207.6385106086731 +timeVals=array([137.2547853 , 288.5302372 , 123.53352928, 82.46748614, + 406.40651512]) +min returnCoefVals: 0.9144017550180804, max returnCoefVals: 0.9886492847185767, mean returnCoefVals: 0.9567253771853947 +returnCoefVals=array([0.91440176, 0.95005252, 0.97335005, 0.95717328, 0.98864928]) + +start with random values ------------ + + +goal value: 0.9 + +0 1 2 3 4 For 5 iterations: +min time: 34.59854745864868, max time: 109.6488606929779, mean time: 63.13373408317566 +timeVals=array([ 61.54462576, 68.5349226 , 34.59854746, 109.64886069, + 41.34171391]) +min returnCoefVals: 0.8632494283911148, max returnCoefVals: 0.9590031566874829, mean returnCoefVals: 0.919180040942625 +returnCoefVals=array([0.93047641, 0.94697408, 0.95900316, 0.89619713, 0.86324943]) + +goal value: 0.95 + +0 1 2 3 4 For 5 iterations: +min time: 13.632518291473389, max time: 247.90915441513062, mean time: 116.95825953483582 +timeVals=array([226.62800479, 62.0311029 , 13.63251829, 247.90915442, + 34.59051728]) +min returnCoefVals: 0.8923253419794754, max returnCoefVals: 1.0209758382135063, mean returnCoefVals: 0.9596896151384253 +returnCoefVals=array([1.02097584, 0.96634935, 0.92910023, 0.89232534, 0.98969732]) + +gamesCount= 3000 ---------------- + + +starts with bad values -------------- + + +goal value: 0.9 + +badFitnessVals=[0.6725, 0.6955, 0.8046666666666666, 0.5608333333333333, 0.8101666666666667, 0.766] +0 1 2 3 4 For 5 iterations: +min time: 112.19143176078796, max time: 474.1458783149719, mean time: 270.50732202529906 +timeVals=array([474.14587831, 112.19143176, 259.00318789, 357.30249095, + 149.89362121]) +min returnCoefVals: 0.8640750956654886, max returnCoefVals: 0.9093508301354873, mean returnCoefVals: 0.8949254100822376 +returnCoefVals=array([0.90680014, 0.90935083, 0.90570142, 0.8640751 , 0.88869956]) + +goal value: 0.95 + +badFitnessVals=[0.7595, 0.7188333333333333, 0.8651666666666666, 0.5965, 0.8733333333333333, 0.8278333333333333] +0 1 2 3 4 For 5 iterations: +min time: 210.90868711471558, max time: 533.5097453594208, mean time: 326.5112485408783 +timeVals=array([358.59228086, 286.59976077, 242.94576859, 533.50974536, + 210.90868711]) +min returnCoefVals: 0.908023283587729, max returnCoefVals: 0.9917260766556416, mean returnCoefVals: 0.9559916201515204 +returnCoefVals=array([0.97305303, 0.90802328, 0.9738796 , 0.93327611, 0.99172608]) + +start with random values ------------ + + +goal value: 0.9 + +0 1 2 3 4 For 5 iterations: +min time: 62.77412390708923, max time: 405.95093607902527, mean time: 209.34744334220886 +timeVals=array([ 62.77412391, 99.4375124 , 109.37965012, 405.95093608, + 369.19499421]) +min returnCoefVals: 0.8451757544570048, max returnCoefVals: 0.9333002265743816, mean returnCoefVals: 0.8891703173315832 +returnCoefVals=array([0.87562351, 0.84517575, 0.93330023, 0.90952487, 0.88222723]) + +goal value: 0.95 + +0 1 2 3 4 For 5 iterations: +min time: 127.80276036262512, max time: 500.08165740966797, mean time: 296.5248766899109 +timeVals=array([500.08165741, 307.40381241, 228.15567279, 319.18048048, + 127.80276036]) +min returnCoefVals: 0.9428745198357916, max returnCoefVals: 0.968400453238727, mean returnCoefVals: 0.9552203946031785 +returnCoefVals=array([0.96840045, 0.96179907, 0.94287452, 0.95475228, 0.94827564]) + +gamesCount= 5000 ---------------- + + +starts with bad values -------------- + + +goal value: 0.9 + +badFitnessVals=[0.6693011336271035, 0.6738115605523647, 0.806394893286656, 0.574381129697388, 0.8220963834635651, 0.7539071916191626] +0 1 2 3 4 For 200 iterations: +min time: 220.82359957695007, max time: 2249.837205886841, mean time: 1102.7905313968658 +timeVals=array([2249.83720589, 220.82359958, 1453.57248044, 1260.42282152, + 329.29654956]) +min returnCoefVals: 0.8743758258884359, max returnCoefVals: 0.9390986325019308, mean returnCoefVals: 0.9008993013244353 +returnCoefVals=array([0.92549864, 0.88500009, 0.93909863, 0.87437583, 0.88052332]) + +goal value: 0.95 + +badFitnessVals=[0.727081970512756, 0.7366138071995264, 0.865297017010397, 0.6100981710049289, 0.8684978182889922, 0.815291386015484] +0 1 2 3 4 For 200 iterations: +min time: 131.77604126930237, max time: 436.46209478378296, mean time: 266.3572997570038 +timeVals=array([131.77604127, 284.70347071, 163.83466268, 436.46209478, + 315.01022935]) +min returnCoefVals: 0.9266001412734978, max returnCoefVals: 0.9694254957729657, mean returnCoefVals: 0.9486550169259804 +returnCoefVals=array([0.92660014, 0.9694255 , 0.94094781, 0.95400108, 0.95230055]) + +start with random values ------------ + + +goal value: 0.9 + +0 1 2 3 4 For 200 iterations: +min time: 66.20206689834595, max time: 1455.0551316738129, mean time: 624.9127726554871 +timeVals=array([ 148.31742382, 1455.05513167, 66.2020669 , 893.35809159, + 561.63114929]) +min returnCoefVals: 0.8802232772002837, max returnCoefVals: 0.9276979122200076, mean returnCoefVals: 0.893969891741798 +returnCoefVals=array([0.88022328, 0.92769791, 0.88427587, 0.89025177, 0.88740063]) + +goal value: 0.95 + +0 1 2 3 4 For 200 iterations: +min time: 65.85777378082275, max time: 1811.382836818695, mean time: 890.9894399642944 +timeVals=array([ 65.85777378, 703.63702512, 1811.38283682, 1488.73205781, + 385.33750629]) +min returnCoefVals: 0.92187436884618, max returnCoefVals: 0.9880531681354042, mean returnCoefVals: 0.952495670856441 +returnCoefVals=array([0.94959848, 0.92187437, 0.98805317, 0.9480258 , 0.95492653]) + +gamesCount= 10000 --------------- + + +starts with bad values -------------- + + +goal value: 0.9 + +badFitnessVals=[0.6734093637454982, 0.684498949280311, 0.8029534568318346, 0.57559682555657, 0.814001049521605, 0.7723481608212147] +0 1 2 3 4 For 200 iterations: +min time: 273.2618703842163, max time: 852.4494953155518, mean time: 537.1537239074707 +timeVals=array([852.44949532, 273.26187038, 320.71004343, 592.26527667, + 647.08193374]) +min returnCoefVals: 0.8873242721573508, max returnCoefVals: 0.9011484056951423, mean returnCoefVals: 0.8919290219876561 +returnCoefVals=array([0.89144937, 0.88815012, 0.90114841, 0.88732427, 0.89157294]) + +goal value: 0.95 + +badFitnessVals=[0.7272468867786634, 0.7320543187334814, 0.8504465858199567, 0.6214343821360879, 0.8657467178488161, 0.8229499584264843] +0 1 2 3 4 For 200 iterations: +min time: 144.8367953300476, max time: 1645.4430599212646, mean time: 875.4130566120148 +timeVals=array([ 498.60299063, 144.83679533, 1645.44305992, 834.15014529, + 1254.03229189]) +min returnCoefVals: 0.9138756229446034, max returnCoefVals: 0.9596222815684867, mean returnCoefVals: 0.9410536271018406 +returnCoefVals=array([0.95962228, 0.95557516, 0.92887361, 0.91387562, 0.94732146]) + +start with random values ------------ + + +goal value: 0.9 + +0 1 2 3 4 For 200 iterations: +min time: 44.28335762023926, max time: 1528.0146882534027, mean time: 673.5708031177521 +timeVals=array([ 493.48089433, 378.16429234, 1528.01468825, 923.91078305, + 44.28335762]) +min returnCoefVals: 0.8981776513483433, max returnCoefVals: 0.9134251281200084, mean returnCoefVals: 0.9065753456904911 +returnCoefVals=array([0.90422577, 0.90482422, 0.91222396, 0.91342513, 0.89817765]) + +goal value: 0.95 + diff --git a/backend/test_files/test2_10000GamesCount.png b/backend/test_files/test2_10000GamesCount.png new file mode 100644 index 0000000..dcbc8a9 Binary files /dev/null and b/backend/test_files/test2_10000GamesCount.png differ diff --git a/backend/test_files/test2_5000GamesCount.png b/backend/test_files/test2_5000GamesCount.png new file mode 100644 index 0000000..5bf5805 Binary files /dev/null and b/backend/test_files/test2_5000GamesCount.png differ diff --git a/backend/test_files/test3_returnCoef_0_9.png b/backend/test_files/test3_returnCoef_0_9.png new file mode 100644 index 0000000..848e3ef Binary files /dev/null and b/backend/test_files/test3_returnCoef_0_9.png differ diff --git a/backend/test_files/test3_returnCoef_0_9_5.png b/backend/test_files/test3_returnCoef_0_9_5.png new file mode 100644 index 0000000..3cc42d8 Binary files /dev/null and b/backend/test_files/test3_returnCoef_0_9_5.png differ diff --git a/tests/OneHandBandit_test.py b/backend/tests/OneHandBandit_test.py similarity index 95% rename from tests/OneHandBandit_test.py rename to backend/tests/OneHandBandit_test.py index e1fc2c5..c248e5d 100644 --- a/tests/OneHandBandit_test.py +++ b/backend/tests/OneHandBandit_test.py @@ -1,7 +1,7 @@ from unittest import TestCase, main -from Bandit import OneHandBandit -from CustomExceptions import * +from backend.Bandit import OneHandBandit +from backend.CustomExceptions import * class OneHandBanditTest(TestCase): @@ -66,7 +66,7 @@ def test_setProbabilities_exception_1(self): self.banditObj.setProbabilities(probs=probs) def test_setProbabilitiesFromTxtFile(self): - filename = "tests/test_probabilities.txt" + filename = "backend/tests/test_probabilities.txt" with open(filename, "r") as f: probs = list(map(float, f.readline().split(" "))) self.banditObj.setProbabilitiesFromTxtFile(filename) diff --git a/tests/test_probabilities.txt b/backend/tests/test_probabilities.txt similarity index 100% rename from tests/test_probabilities.txt rename to backend/tests/test_probabilities.txt diff --git a/backend/wsgi.py b/backend/wsgi.py new file mode 100644 index 0000000..ade27a4 --- /dev/null +++ b/backend/wsgi.py @@ -0,0 +1,56 @@ +from flask import Flask, Response + +from backend.Bandit import OneHandBandit + +app = Flask(__name__) + +# відображення індексу картинки до назви картинки +dict_ = {0: "at_at", + 1: "c3po", + 2: "darth_vader", + 3: "death_star", + 4: "falcon", + 5: "r2d2"} + +wastedMoney = 0 +wonMoney = 0 + + +@app.route("/getState", methods=["GET", ]) +def getState(): + """ + обробляє запит сайту щодо даних: currentState, wonMoney, returnCoef + Returns: + об'єкт класу Response, який містить всі необхідні дані, а саме: currentState, wonMoney, returnCoef + """ + global wastedMoney, wonMoney + bandit.startGame(bandit.price) + won = bandit.play(n=1)[0] + + wastedMoney = wastedMoney + bandit.price + wonMoney = wonMoney + won + returnCoef = (wonMoney / wastedMoney) + print(wonMoney) + print(wastedMoney) + + currentState = bandit.getState().T + + currentState = [dict_[int(i)] for i in currentState.flatten()] + print(','.join(currentState) + ',' + str(won) + ',' + str(round(returnCoef, 4))) + resp = Response(','.join(currentState) + ',' + str(won) + ',' + str(round(returnCoef, 4))) + resp.headers['Access-Control-Allow-Origin'] = '*' + return resp + + +if __name__ == '__main__': + bandit = OneHandBandit(4, 3, 5) + + # l = bandit.genBaseProbabilities(bandit.picturesNumb, returnCoef) + # bandit.setProbabilities(l) + bandit.setProbabilitiesFromTxtFile("probabilities.txt") + + bandit.setPriceOfGame(10) + bandit.setWinningCombs({(0, 0, 0, 0): 15, (1, 1, 1, 1): 15, (2, 2, 2, 2): 15, (3, 3, 3, 3): 15, (1, 2, 3, 4): 20, }) + bandit.addWinningComb((4, 4, 4, 4), 15) + + app.run(debug=True, host="0.0.0.0") diff --git a/probabilities.txt b/probabilities.txt deleted file mode 100644 index 038682f..0000000 --- a/probabilities.txt +++ /dev/null @@ -1 +0,0 @@ -0.1 0.1 0.1 0.1 0.6 \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..0a2be8e --- /dev/null +++ b/requirements.txt @@ -0,0 +1,89 @@ +argon2-cffi==21.3.0 +argon2-cffi-bindings==21.2.0 +asttokens==2.0.5 +astunparse==1.6.3 +attrs==21.4.0 +backcall==0.2.0 +beautifulsoup4==4.11.1 +bleach==5.0.0 +cffi==1.15.0 +click==8.1.3 +colorama==0.4.4 +coverage==6.4.1 +cycler==0.11.0 +debugpy==1.6.0 +decorator==5.1.1 +defusedxml==0.7.1 +entrypoints==0.4 +executing==0.8.3 +fastjsonschema==2.15.3 +Flask==2.1.2 +fonttools==4.33.3 +importlib-metadata==4.11.4 +importlib-resources==5.7.1 +ipykernel==6.14.0 +ipython==8.4.0 +ipython-genutils==0.2.0 +ipywidgets==7.7.0 +itsdangerous==2.1.2 +jedi==0.18.1 +Jinja2==3.1.2 +jsonschema==4.6.0 +jupyter==1.0.0 +jupyter-client==7.3.4 +jupyter-console==6.4.3 +jupyter-core==4.10.0 +jupyterlab-pygments==0.2.2 +jupyterlab-widgets==1.1.0 +kiwisolver==1.4.3 +Mako==1.2.0 +Markdown==3.3.7 +MarkupSafe==2.1.1 +matplotlib==3.5.2 +matplotlib-inline==0.1.3 +mistune==0.8.4 +nbclient==0.6.4 +nbconvert==6.5.0 +nbformat==5.4.0 +nest-asyncio==1.5.5 +notebook==6.4.12 +numpy==1.22.4 +packaging==21.3 +pandocfilters==1.5.0 +parso==0.8.3 +pdoc==12.0.2 +pdoc3==0.10.0 +pickleshare==0.7.5 +Pillow==9.1.1 +pipfile==0.0.2 +prometheus-client==0.14.1 +prompt-toolkit==3.0.29 +psutil==5.9.1 +pure-eval==0.2.2 +pycparser==2.21 +Pygments==2.12.0 +Pyment==0.3.3 +pyparsing==3.0.9 +pyrsistent==0.18.1 +python-dateutil==2.8.2 +pywin32==304 +pywinpty==2.0.5 +pyzmq==23.1.0 +qtconsole==5.3.1 +QtPy==2.1.0 +scipy==1.8.1 +Send2Trash==1.8.0 +six==1.16.0 +soupsieve==2.3.2.post1 +stack-data==0.2.0 +terminado==0.15.0 +tinycss2==1.1.1 +toml==0.10.2 +tornado==6.1 +tqdm==4.64.0 +traitlets==5.2.2.post1 +wcwidth==0.2.5 +webencodings==0.5.1 +Werkzeug==2.1.2 +widgetsnbextension==3.6.0 +zipp==3.8.0 diff --git a/user_interface/.babelrc b/user_interface/.babelrc new file mode 100644 index 0000000..1320b9a --- /dev/null +++ b/user_interface/.babelrc @@ -0,0 +1,3 @@ +{ + "presets": ["@babel/preset-env"] +} diff --git a/user_interface/.prettierrc b/user_interface/.prettierrc new file mode 100644 index 0000000..222861c --- /dev/null +++ b/user_interface/.prettierrc @@ -0,0 +1,4 @@ +{ + "tabWidth": 2, + "useTabs": false +} diff --git a/user_interface/.travis.yml b/user_interface/.travis.yml new file mode 100644 index 0000000..6ea8fb5 --- /dev/null +++ b/user_interface/.travis.yml @@ -0,0 +1,13 @@ +language: node_js +node_js: + - "lts/*" +script: + - npm run build +deploy: + provider: pages + skip_cleanup: true + github_token: $GITHUB_TOKEN # Set in the settings page of your repository, as a secure variable + keep_history: true + on: + branch: master + local_dir: ./dist diff --git a/user_interface/package-lock.json b/user_interface/package-lock.json new file mode 100644 index 0000000..3b3ebaf --- /dev/null +++ b/user_interface/package-lock.json @@ -0,0 +1,5216 @@ +{ + "name": "html5-slot-machine", + "requires": true, + "lockfileVersion": 1, + "dependencies": { + "@ampproject/remapping": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.1.2.tgz", + "integrity": "sha512-hoyByceqwKirw7w3Z7gnIIZC3Wx3J484Y3L/cMpXFbr7d9ZQj2mODrirNzcJa+SM3UlpWXYvKV4RlRpFXlWgXg==", + "dev": true, + "requires": { + "@jridgewell/trace-mapping": "^0.3.0" + } + }, + "@babel/code-frame": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz", + "integrity": "sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==", + "dev": true, + "requires": { + "@babel/highlight": "^7.16.7" + } + }, + "@babel/compat-data": { + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.17.7.tgz", + "integrity": "sha512-p8pdE6j0a29TNGebNm7NzYZWB3xVZJBZ7XGs42uAKzQo8VQ3F0By/cQCtUEABwIqw5zo6WA4NbmxsfzADzMKnQ==", + "dev": true + }, + "@babel/core": { + "version": "7.17.8", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.17.8.tgz", + "integrity": "sha512-OdQDV/7cRBtJHLSOBqqbYNkOcydOgnX59TZx4puf41fzcVtN3e/4yqY8lMQsK+5X2lJtAdmA+6OHqsj1hBJ4IQ==", + "dev": true, + "requires": { + "@ampproject/remapping": "^2.1.0", + "@babel/code-frame": "^7.16.7", + "@babel/generator": "^7.17.7", + "@babel/helper-compilation-targets": "^7.17.7", + "@babel/helper-module-transforms": "^7.17.7", + "@babel/helpers": "^7.17.8", + "@babel/parser": "^7.17.8", + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.17.3", + "@babel/types": "^7.17.0", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.1.2", + "semver": "^6.3.0" + } + }, + "@babel/generator": { + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.7.tgz", + "integrity": "sha512-oLcVCTeIFadUoArDTwpluncplrYBmTCCZZgXCbgNGvOBBiSDDK3eWO4b/+eOTli5tKv1lg+a5/NAXg+nTcei1w==", + "dev": true, + "requires": { + "@babel/types": "^7.17.0", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + } + }, + "@babel/helper-annotate-as-pure": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz", + "integrity": "sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw==", + "dev": true, + "requires": { + "@babel/types": "^7.16.7" + } + }, + "@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.16.7.tgz", + "integrity": "sha512-C6FdbRaxYjwVu/geKW4ZeQ0Q31AftgRcdSnZ5/jsH6BzCJbtvXvhpfkbkThYSuutZA7nCXpPR6AD9zd1dprMkA==", + "dev": true, + "requires": { + "@babel/helper-explode-assignable-expression": "^7.16.7", + "@babel/types": "^7.16.7" + } + }, + "@babel/helper-compilation-targets": { + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.17.7.tgz", + "integrity": "sha512-UFzlz2jjd8kroj0hmCFV5zr+tQPi1dpC2cRsDV/3IEW8bJfCPrPpmcSN6ZS8RqIq4LXcmpipCQFPddyFA5Yc7w==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.17.7", + "@babel/helper-validator-option": "^7.16.7", + "browserslist": "^4.17.5", + "semver": "^6.3.0" + } + }, + "@babel/helper-create-class-features-plugin": { + "version": "7.17.6", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.17.6.tgz", + "integrity": "sha512-SogLLSxXm2OkBbSsHZMM4tUi8fUzjs63AT/d0YQIzr6GSd8Hxsbk2KYDX0k0DweAzGMj/YWeiCsorIdtdcW8Eg==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.16.7", + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-function-name": "^7.16.7", + "@babel/helper-member-expression-to-functions": "^7.16.7", + "@babel/helper-optimise-call-expression": "^7.16.7", + "@babel/helper-replace-supers": "^7.16.7", + "@babel/helper-split-export-declaration": "^7.16.7" + } + }, + "@babel/helper-create-regexp-features-plugin": { + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.17.0.tgz", + "integrity": "sha512-awO2So99wG6KnlE+TPs6rn83gCz5WlEePJDTnLEqbchMVrBeAujURVphRdigsk094VhvZehFoNOihSlcBjwsXA==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.16.7", + "regexpu-core": "^5.0.1" + } + }, + "@babel/helper-define-polyfill-provider": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.1.tgz", + "integrity": "sha512-J9hGMpJQmtWmj46B3kBHmL38UhJGhYX7eqkcq+2gsstyYt341HmPeWspihX43yVRA0mS+8GGk2Gckc7bY/HCmA==", + "dev": true, + "requires": { + "@babel/helper-compilation-targets": "^7.13.0", + "@babel/helper-module-imports": "^7.12.13", + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/traverse": "^7.13.0", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2", + "semver": "^6.1.2" + } + }, + "@babel/helper-environment-visitor": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz", + "integrity": "sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag==", + "dev": true, + "requires": { + "@babel/types": "^7.16.7" + } + }, + "@babel/helper-explode-assignable-expression": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.16.7.tgz", + "integrity": "sha512-KyUenhWMC8VrxzkGP0Jizjo4/Zx+1nNZhgocs+gLzyZyB8SHidhoq9KK/8Ato4anhwsivfkBLftky7gvzbZMtQ==", + "dev": true, + "requires": { + "@babel/types": "^7.16.7" + } + }, + "@babel/helper-function-name": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.7.tgz", + "integrity": "sha512-QfDfEnIUyyBSR3HtrtGECuZ6DAyCkYFp7GHl75vFtTnn6pjKeK0T1DB5lLkFvBea8MdaiUABx3osbgLyInoejA==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.16.7", + "@babel/template": "^7.16.7", + "@babel/types": "^7.16.7" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.7.tgz", + "integrity": "sha512-flc+RLSOBXzNzVhcLu6ujeHUrD6tANAOU5ojrRx/as+tbzf8+stUCj7+IfRRoAbEZqj/ahXEMsjhOhgeZsrnTw==", + "dev": true, + "requires": { + "@babel/types": "^7.16.7" + } + }, + "@babel/helper-hoist-variables": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz", + "integrity": "sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg==", + "dev": true, + "requires": { + "@babel/types": "^7.16.7" + } + }, + "@babel/helper-member-expression-to-functions": { + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.17.7.tgz", + "integrity": "sha512-thxXgnQ8qQ11W2wVUObIqDL4p148VMxkt5T/qpN5k2fboRyzFGFmKsTGViquyM5QHKUy48OZoca8kw4ajaDPyw==", + "dev": true, + "requires": { + "@babel/types": "^7.17.0" + } + }, + "@babel/helper-module-imports": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz", + "integrity": "sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==", + "dev": true, + "requires": { + "@babel/types": "^7.16.7" + } + }, + "@babel/helper-module-transforms": { + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.17.7.tgz", + "integrity": "sha512-VmZD99F3gNTYB7fJRDTi+u6l/zxY0BE6OIxPSU7a50s6ZUQkHwSDmV92FfM+oCG0pZRVojGYhkR8I0OGeCVREw==", + "dev": true, + "requires": { + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-module-imports": "^7.16.7", + "@babel/helper-simple-access": "^7.17.7", + "@babel/helper-split-export-declaration": "^7.16.7", + "@babel/helper-validator-identifier": "^7.16.7", + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.17.3", + "@babel/types": "^7.17.0" + } + }, + "@babel/helper-optimise-call-expression": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.16.7.tgz", + "integrity": "sha512-EtgBhg7rd/JcnpZFXpBy0ze1YRfdm7BnBX4uKMBd3ixa3RGAE002JZB66FJyNH7g0F38U05pXmA5P8cBh7z+1w==", + "dev": true, + "requires": { + "@babel/types": "^7.16.7" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz", + "integrity": "sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA==", + "dev": true + }, + "@babel/helper-remap-async-to-generator": { + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.16.8.tgz", + "integrity": "sha512-fm0gH7Flb8H51LqJHy3HJ3wnE1+qtYR2A99K06ahwrawLdOFsCEWjZOrYricXJHoPSudNKxrMBUPEIPxiIIvBw==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.16.7", + "@babel/helper-wrap-function": "^7.16.8", + "@babel/types": "^7.16.8" + } + }, + "@babel/helper-replace-supers": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.16.7.tgz", + "integrity": "sha512-y9vsWilTNaVnVh6xiJfABzsNpgDPKev9HnAgz6Gb1p6UUwf9NepdlsV7VXGCftJM+jqD5f7JIEubcpLjZj5dBw==", + "dev": true, + "requires": { + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-member-expression-to-functions": "^7.16.7", + "@babel/helper-optimise-call-expression": "^7.16.7", + "@babel/traverse": "^7.16.7", + "@babel/types": "^7.16.7" + } + }, + "@babel/helper-simple-access": { + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.17.7.tgz", + "integrity": "sha512-txyMCGroZ96i+Pxr3Je3lzEJjqwaRC9buMUgtomcrLe5Nd0+fk1h0LLA+ixUF5OW7AhHuQ7Es1WcQJZmZsz2XA==", + "dev": true, + "requires": { + "@babel/types": "^7.17.0" + } + }, + "@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.16.0.tgz", + "integrity": "sha512-+il1gTy0oHwUsBQZyJvukbB4vPMdcYBrFHa0Uc4AizLxbq6BOYC51Rv4tWocX9BLBDLZ4kc6qUFpQ6HRgL+3zw==", + "dev": true, + "requires": { + "@babel/types": "^7.16.0" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz", + "integrity": "sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw==", + "dev": true, + "requires": { + "@babel/types": "^7.16.7" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz", + "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==", + "dev": true + }, + "@babel/helper-validator-option": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz", + "integrity": "sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ==", + "dev": true + }, + "@babel/helper-wrap-function": { + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.16.8.tgz", + "integrity": "sha512-8RpyRVIAW1RcDDGTA+GpPAwV22wXCfKOoM9bet6TLkGIFTkRQSkH1nMQ5Yet4MpoXe1ZwHPVtNasc2w0uZMqnw==", + "dev": true, + "requires": { + "@babel/helper-function-name": "^7.16.7", + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.16.8", + "@babel/types": "^7.16.8" + } + }, + "@babel/helpers": { + "version": "7.17.8", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.17.8.tgz", + "integrity": "sha512-QcL86FGxpfSJwGtAvv4iG93UL6bmqBdmoVY0CMCU2g+oD2ezQse3PT5Pa+jiD6LJndBQi0EDlpzOWNlLuhz5gw==", + "dev": true, + "requires": { + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.17.3", + "@babel/types": "^7.17.0" + } + }, + "@babel/highlight": { + "version": "7.16.10", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.10.tgz", + "integrity": "sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.16.7", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.17.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.8.tgz", + "integrity": "sha512-BoHhDJrJXqcg+ZL16Xv39H9n+AqJ4pcDrQBGZN+wHxIysrLZ3/ECwCBUch/1zUNhnsXULcONU3Ei5Hmkfk6kiQ==", + "dev": true + }, + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.16.7.tgz", + "integrity": "sha512-anv/DObl7waiGEnC24O9zqL0pSuI9hljihqiDuFHC8d7/bjr/4RLGPWuc8rYOff/QPzbEPSkzG8wGG9aDuhHRg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.16.7.tgz", + "integrity": "sha512-di8vUHRdf+4aJ7ltXhaDbPoszdkh59AQtJM5soLsuHpQJdFQZOA4uGj0V2u/CZ8bJ/u8ULDL5yq6FO/bCXnKHw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0", + "@babel/plugin-proposal-optional-chaining": "^7.16.7" + } + }, + "@babel/plugin-proposal-async-generator-functions": { + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.16.8.tgz", + "integrity": "sha512-71YHIvMuiuqWJQkebWJtdhQTfd4Q4mF76q2IX37uZPkG9+olBxsX+rH1vkhFto4UeJZ9dPY2s+mDvhDm1u2BGQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-remap-async-to-generator": "^7.16.8", + "@babel/plugin-syntax-async-generators": "^7.8.4" + } + }, + "@babel/plugin-proposal-class-properties": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.16.7.tgz", + "integrity": "sha512-IobU0Xme31ewjYOShSIqd/ZGM/r/cuOz2z0MDbNrhF5FW+ZVgi0f2lyeoj9KFPDOAqsYxmLWZte1WOwlvY9aww==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-proposal-class-static-block": { + "version": "7.17.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.17.6.tgz", + "integrity": "sha512-X/tididvL2zbs7jZCeeRJ8167U/+Ac135AM6jCAx6gYXDUviZV5Ku9UDvWS2NCuWlFjIRXklYhwo6HhAC7ETnA==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.17.6", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-class-static-block": "^7.14.5" + } + }, + "@babel/plugin-proposal-dynamic-import": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.16.7.tgz", + "integrity": "sha512-I8SW9Ho3/8DRSdmDdH3gORdyUuYnk1m4cMxUAdu5oy4n3OfN8flDEH+d60iG7dUfi0KkYwSvoalHzzdRzpWHTg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-dynamic-import": "^7.8.3" + } + }, + "@babel/plugin-proposal-export-namespace-from": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.16.7.tgz", + "integrity": "sha512-ZxdtqDXLRGBL64ocZcs7ovt71L3jhC1RGSyR996svrCi3PYqHNkb3SwPJCs8RIzD86s+WPpt2S73+EHCGO+NUA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3" + } + }, + "@babel/plugin-proposal-json-strings": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.16.7.tgz", + "integrity": "sha512-lNZ3EEggsGY78JavgbHsK9u5P3pQaW7k4axlgFLYkMd7UBsiNahCITShLjNQschPyjtO6dADrL24757IdhBrsQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-json-strings": "^7.8.3" + } + }, + "@babel/plugin-proposal-logical-assignment-operators": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.16.7.tgz", + "integrity": "sha512-K3XzyZJGQCr00+EtYtrDjmwX7o7PLK6U9bi1nCwkQioRFVUv6dJoxbQjtWVtP+bCPy82bONBKG8NPyQ4+i6yjg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" + } + }, + "@babel/plugin-proposal-nullish-coalescing-operator": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.16.7.tgz", + "integrity": "sha512-aUOrYU3EVtjf62jQrCj63pYZ7k6vns2h/DQvHPWGmsJRYzWXZ6/AsfgpiRy6XiuIDADhJzP2Q9MwSMKauBQ+UQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" + } + }, + "@babel/plugin-proposal-numeric-separator": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.16.7.tgz", + "integrity": "sha512-vQgPMknOIgiuVqbokToyXbkY/OmmjAzr/0lhSIbG/KmnzXPGwW/AdhdKpi+O4X/VkWiWjnkKOBiqJrTaC98VKw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-numeric-separator": "^7.10.4" + } + }, + "@babel/plugin-proposal-object-rest-spread": { + "version": "7.17.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.17.3.tgz", + "integrity": "sha512-yuL5iQA/TbZn+RGAfxQXfi7CNLmKi1f8zInn4IgobuCWcAb7i+zj4TYzQ9l8cEzVyJ89PDGuqxK1xZpUDISesw==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.17.0", + "@babel/helper-compilation-targets": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-transform-parameters": "^7.16.7" + } + }, + "@babel/plugin-proposal-optional-catch-binding": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.16.7.tgz", + "integrity": "sha512-eMOH/L4OvWSZAE1VkHbr1vckLG1WUcHGJSLqqQwl2GaUqG6QjddvrOaTUMNYiv77H5IKPMZ9U9P7EaHwvAShfA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" + } + }, + "@babel/plugin-proposal-optional-chaining": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.16.7.tgz", + "integrity": "sha512-eC3xy+ZrUcBtP7x+sq62Q/HYd674pPTb/77XZMb5wbDPGWIdUbSr4Agr052+zaUPSb+gGRnjxXfKFvx5iMJ+DA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0", + "@babel/plugin-syntax-optional-chaining": "^7.8.3" + } + }, + "@babel/plugin-proposal-private-methods": { + "version": "7.16.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.16.11.tgz", + "integrity": "sha512-F/2uAkPlXDr8+BHpZvo19w3hLFKge+k75XUprE6jaqKxjGkSYcK+4c+bup5PdW/7W/Rpjwql7FTVEDW+fRAQsw==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.16.10", + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-proposal-private-property-in-object": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.16.7.tgz", + "integrity": "sha512-rMQkjcOFbm+ufe3bTZLyOfsOUOxyvLXZJCTARhJr+8UMSoZmqTe1K1BgkFcrW37rAchWg57yI69ORxiWvUINuQ==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.16.7", + "@babel/helper-create-class-features-plugin": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5" + } + }, + "@babel/plugin-proposal-unicode-property-regex": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.16.7.tgz", + "integrity": "sha512-QRK0YI/40VLhNVGIjRNAAQkEHws0cswSdFFjpFyt943YmJIU1da9uW63Iu6NFV6CxTZW5eTDCrwZUstBWgp/Rg==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.12.13" + } + }, + "@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-syntax-dynamic-import": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", + "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-export-namespace-from": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", + "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-transform-arrow-functions": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.16.7.tgz", + "integrity": "sha512-9ffkFFMbvzTvv+7dTp/66xvZAWASuPD5Tl9LK3Z9vhOmANo6j94rik+5YMBt4CwHVMWLWpMsriIc2zsa3WW3xQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-async-to-generator": { + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.16.8.tgz", + "integrity": "sha512-MtmUmTJQHCnyJVrScNzNlofQJ3dLFuobYn3mwOTKHnSCMtbNsqvF71GQmJfFjdrXSsAA7iysFmYWw4bXZ20hOg==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-remap-async-to-generator": "^7.16.8" + } + }, + "@babel/plugin-transform-block-scoped-functions": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.16.7.tgz", + "integrity": "sha512-JUuzlzmF40Z9cXyytcbZEZKckgrQzChbQJw/5PuEHYeqzCsvebDx0K0jWnIIVcmmDOAVctCgnYs0pMcrYj2zJg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-block-scoping": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.16.7.tgz", + "integrity": "sha512-ObZev2nxVAYA4bhyusELdo9hb3H+A56bxH3FZMbEImZFiEDYVHXQSJ1hQKFlDnlt8G9bBrCZ5ZpURZUrV4G5qQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-classes": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.16.7.tgz", + "integrity": "sha512-WY7og38SFAGYRe64BrjKf8OrE6ulEHtr5jEYaZMwox9KebgqPi67Zqz8K53EKk1fFEJgm96r32rkKZ3qA2nCWQ==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.16.7", + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-function-name": "^7.16.7", + "@babel/helper-optimise-call-expression": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-replace-supers": "^7.16.7", + "@babel/helper-split-export-declaration": "^7.16.7", + "globals": "^11.1.0" + } + }, + "@babel/plugin-transform-computed-properties": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.16.7.tgz", + "integrity": "sha512-gN72G9bcmenVILj//sv1zLNaPyYcOzUho2lIJBMh/iakJ9ygCo/hEF9cpGb61SCMEDxbbyBoVQxrt+bWKu5KGw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-destructuring": { + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.17.7.tgz", + "integrity": "sha512-XVh0r5yq9sLR4vZ6eVZe8FKfIcSgaTBxVBRSYokRj2qksf6QerYnTxz9/GTuKTH/n/HwLP7t6gtlybHetJ/6hQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-dotall-regex": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.16.7.tgz", + "integrity": "sha512-Lyttaao2SjZF6Pf4vk1dVKv8YypMpomAbygW+mU5cYP3S5cWTfCJjG8xV6CFdzGFlfWK81IjL9viiTvpb6G7gQ==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-duplicate-keys": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.16.7.tgz", + "integrity": "sha512-03DvpbRfvWIXyK0/6QiR1KMTWeT6OcQ7tbhjrXyFS02kjuX/mu5Bvnh5SDSWHxyawit2g5aWhKwI86EE7GUnTw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-exponentiation-operator": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.16.7.tgz", + "integrity": "sha512-8UYLSlyLgRixQvlYH3J2ekXFHDFLQutdy7FfFAMm3CPZ6q9wHCwnUyiXpQCe3gVVnQlHc5nsuiEVziteRNTXEA==", + "dev": true, + "requires": { + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-for-of": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.16.7.tgz", + "integrity": "sha512-/QZm9W92Ptpw7sjI9Nx1mbcsWz33+l8kuMIQnDwgQBG5s3fAfQvkRjQ7NqXhtNcKOnPkdICmUHyCaWW06HCsqg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-function-name": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.16.7.tgz", + "integrity": "sha512-SU/C68YVwTRxqWj5kgsbKINakGag0KTgq9f2iZEXdStoAbOzLHEBRYzImmA6yFo8YZhJVflvXmIHUO7GWHmxxA==", + "dev": true, + "requires": { + "@babel/helper-compilation-targets": "^7.16.7", + "@babel/helper-function-name": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-literals": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.16.7.tgz", + "integrity": "sha512-6tH8RTpTWI0s2sV6uq3e/C9wPo4PTqqZps4uF0kzQ9/xPLFQtipynvmT1g/dOfEJ+0EQsHhkQ/zyRId8J2b8zQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-member-expression-literals": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.16.7.tgz", + "integrity": "sha512-mBruRMbktKQwbxaJof32LT9KLy2f3gH+27a5XSuXo6h7R3vqltl0PgZ80C8ZMKw98Bf8bqt6BEVi3svOh2PzMw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-modules-amd": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.16.7.tgz", + "integrity": "sha512-KaaEtgBL7FKYwjJ/teH63oAmE3lP34N3kshz8mm4VMAw7U3PxjVwwUmxEFksbgsNUaO3wId9R2AVQYSEGRa2+g==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "babel-plugin-dynamic-import-node": "^2.3.3" + } + }, + "@babel/plugin-transform-modules-commonjs": { + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.17.7.tgz", + "integrity": "sha512-ITPmR2V7MqioMJyrxUo2onHNC3e+MvfFiFIR0RP21d3PtlVb6sfzoxNKiphSZUOM9hEIdzCcZe83ieX3yoqjUA==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.17.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-simple-access": "^7.17.7", + "babel-plugin-dynamic-import-node": "^2.3.3" + } + }, + "@babel/plugin-transform-modules-systemjs": { + "version": "7.17.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.17.8.tgz", + "integrity": "sha512-39reIkMTUVagzgA5x88zDYXPCMT6lcaRKs1+S9K6NKBPErbgO/w/kP8GlNQTC87b412ZTlmNgr3k2JrWgHH+Bw==", + "dev": true, + "requires": { + "@babel/helper-hoist-variables": "^7.16.7", + "@babel/helper-module-transforms": "^7.17.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-validator-identifier": "^7.16.7", + "babel-plugin-dynamic-import-node": "^2.3.3" + } + }, + "@babel/plugin-transform-modules-umd": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.16.7.tgz", + "integrity": "sha512-EMh7uolsC8O4xhudF2F6wedbSHm1HHZ0C6aJ7K67zcDNidMzVcxWdGr+htW9n21klm+bOn+Rx4CBsAntZd3rEQ==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.16.8.tgz", + "integrity": "sha512-j3Jw+n5PvpmhRR+mrgIh04puSANCk/T/UA3m3P1MjJkhlK906+ApHhDIqBQDdOgL/r1UYpz4GNclTXxyZrYGSw==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.16.7" + } + }, + "@babel/plugin-transform-new-target": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.16.7.tgz", + "integrity": "sha512-xiLDzWNMfKoGOpc6t3U+etCE2yRnn3SM09BXqWPIZOBpL2gvVrBWUKnsJx0K/ADi5F5YC5f8APFfWrz25TdlGg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-object-super": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.16.7.tgz", + "integrity": "sha512-14J1feiQVWaGvRxj2WjyMuXS2jsBkgB3MdSN5HuC2G5nRspa5RK9COcs82Pwy5BuGcjb+fYaUj94mYcOj7rCvw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-replace-supers": "^7.16.7" + } + }, + "@babel/plugin-transform-parameters": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.16.7.tgz", + "integrity": "sha512-AT3MufQ7zZEhU2hwOA11axBnExW0Lszu4RL/tAlUJBuNoRak+wehQW8h6KcXOcgjY42fHtDxswuMhMjFEuv/aw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-property-literals": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.16.7.tgz", + "integrity": "sha512-z4FGr9NMGdoIl1RqavCqGG+ZuYjfZ/hkCIeuH6Do7tXmSm0ls11nYVSJqFEUOSJbDab5wC6lRE/w6YjVcr6Hqw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-regenerator": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.16.7.tgz", + "integrity": "sha512-mF7jOgGYCkSJagJ6XCujSQg+6xC1M77/03K2oBmVJWoFGNUtnVJO4WHKJk3dnPC8HCcj4xBQP1Egm8DWh3Pb3Q==", + "dev": true, + "requires": { + "regenerator-transform": "^0.14.2" + } + }, + "@babel/plugin-transform-reserved-words": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.16.7.tgz", + "integrity": "sha512-KQzzDnZ9hWQBjwi5lpY5v9shmm6IVG0U9pB18zvMu2i4H90xpT4gmqwPYsn8rObiadYe2M0gmgsiOIF5A/2rtg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-shorthand-properties": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.16.7.tgz", + "integrity": "sha512-hah2+FEnoRoATdIb05IOXf+4GzXYTq75TVhIn1PewihbpyrNWUt2JbudKQOETWw6QpLe+AIUpJ5MVLYTQbeeUg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-spread": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.16.7.tgz", + "integrity": "sha512-+pjJpgAngb53L0iaA5gU/1MLXJIfXcYepLgXB3esVRf4fqmj8f2cxM3/FKaHsZms08hFQJkFccEWuIpm429TXg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0" + } + }, + "@babel/plugin-transform-sticky-regex": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.16.7.tgz", + "integrity": "sha512-NJa0Bd/87QV5NZZzTuZG5BPJjLYadeSZ9fO6oOUoL4iQx+9EEuw/eEM92SrsT19Yc2jgB1u1hsjqDtH02c3Drw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-template-literals": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.16.7.tgz", + "integrity": "sha512-VwbkDDUeenlIjmfNeDX/V0aWrQH2QiVyJtwymVQSzItFDTpxfyJh3EVaQiS0rIN/CqbLGr0VcGmuwyTdZtdIsA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-typeof-symbol": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.16.7.tgz", + "integrity": "sha512-p2rOixCKRJzpg9JB4gjnG4gjWkWa89ZoYUnl9snJ1cWIcTH/hvxZqfO+WjG6T8DRBpctEol5jw1O5rA8gkCokQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-unicode-escapes": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.16.7.tgz", + "integrity": "sha512-TAV5IGahIz3yZ9/Hfv35TV2xEm+kaBDaZQCn2S/hG9/CZ0DktxJv9eKfPc7yYCvOYR4JGx1h8C+jcSOvgaaI/Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-unicode-regex": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.16.7.tgz", + "integrity": "sha512-oC5tYYKw56HO75KZVLQ+R/Nl3Hro9kf8iG0hXoaHP7tjAyCpvqBiSNe6vGrZni1Z6MggmUOC6A7VP7AVmw225Q==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/preset-env": { + "version": "7.16.11", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.16.11.tgz", + "integrity": "sha512-qcmWG8R7ZW6WBRPZK//y+E3Cli151B20W1Rv7ln27vuPaXU/8TKms6jFdiJtF7UDTxcrb7mZd88tAeK9LjdT8g==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.16.8", + "@babel/helper-compilation-targets": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-validator-option": "^7.16.7", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.16.7", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.16.7", + "@babel/plugin-proposal-async-generator-functions": "^7.16.8", + "@babel/plugin-proposal-class-properties": "^7.16.7", + "@babel/plugin-proposal-class-static-block": "^7.16.7", + "@babel/plugin-proposal-dynamic-import": "^7.16.7", + "@babel/plugin-proposal-export-namespace-from": "^7.16.7", + "@babel/plugin-proposal-json-strings": "^7.16.7", + "@babel/plugin-proposal-logical-assignment-operators": "^7.16.7", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.16.7", + "@babel/plugin-proposal-numeric-separator": "^7.16.7", + "@babel/plugin-proposal-object-rest-spread": "^7.16.7", + "@babel/plugin-proposal-optional-catch-binding": "^7.16.7", + "@babel/plugin-proposal-optional-chaining": "^7.16.7", + "@babel/plugin-proposal-private-methods": "^7.16.11", + "@babel/plugin-proposal-private-property-in-object": "^7.16.7", + "@babel/plugin-proposal-unicode-property-regex": "^7.16.7", + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5", + "@babel/plugin-transform-arrow-functions": "^7.16.7", + "@babel/plugin-transform-async-to-generator": "^7.16.8", + "@babel/plugin-transform-block-scoped-functions": "^7.16.7", + "@babel/plugin-transform-block-scoping": "^7.16.7", + "@babel/plugin-transform-classes": "^7.16.7", + "@babel/plugin-transform-computed-properties": "^7.16.7", + "@babel/plugin-transform-destructuring": "^7.16.7", + "@babel/plugin-transform-dotall-regex": "^7.16.7", + "@babel/plugin-transform-duplicate-keys": "^7.16.7", + "@babel/plugin-transform-exponentiation-operator": "^7.16.7", + "@babel/plugin-transform-for-of": "^7.16.7", + "@babel/plugin-transform-function-name": "^7.16.7", + "@babel/plugin-transform-literals": "^7.16.7", + "@babel/plugin-transform-member-expression-literals": "^7.16.7", + "@babel/plugin-transform-modules-amd": "^7.16.7", + "@babel/plugin-transform-modules-commonjs": "^7.16.8", + "@babel/plugin-transform-modules-systemjs": "^7.16.7", + "@babel/plugin-transform-modules-umd": "^7.16.7", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.16.8", + "@babel/plugin-transform-new-target": "^7.16.7", + "@babel/plugin-transform-object-super": "^7.16.7", + "@babel/plugin-transform-parameters": "^7.16.7", + "@babel/plugin-transform-property-literals": "^7.16.7", + "@babel/plugin-transform-regenerator": "^7.16.7", + "@babel/plugin-transform-reserved-words": "^7.16.7", + "@babel/plugin-transform-shorthand-properties": "^7.16.7", + "@babel/plugin-transform-spread": "^7.16.7", + "@babel/plugin-transform-sticky-regex": "^7.16.7", + "@babel/plugin-transform-template-literals": "^7.16.7", + "@babel/plugin-transform-typeof-symbol": "^7.16.7", + "@babel/plugin-transform-unicode-escapes": "^7.16.7", + "@babel/plugin-transform-unicode-regex": "^7.16.7", + "@babel/preset-modules": "^0.1.5", + "@babel/types": "^7.16.8", + "babel-plugin-polyfill-corejs2": "^0.3.0", + "babel-plugin-polyfill-corejs3": "^0.5.0", + "babel-plugin-polyfill-regenerator": "^0.3.0", + "core-js-compat": "^3.20.2", + "semver": "^6.3.0" + } + }, + "@babel/preset-modules": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.5.tgz", + "integrity": "sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", + "@babel/plugin-transform-dotall-regex": "^7.4.4", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + } + }, + "@babel/runtime": { + "version": "7.17.8", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.8.tgz", + "integrity": "sha512-dQpEpK0O9o6lj6oPu0gRDbbnk+4LeHlNcBpspf6Olzt3GIX4P1lWF1gS+pHLDFlaJvbR6q7jCfQ08zA4QJBnmA==", + "dev": true, + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "@babel/template": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz", + "integrity": "sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.16.7", + "@babel/parser": "^7.16.7", + "@babel/types": "^7.16.7" + } + }, + "@babel/traverse": { + "version": "7.17.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.17.3.tgz", + "integrity": "sha512-5irClVky7TxRWIRtxlh2WPUUOLhcPN06AGgaQSB8AEwuyEBgJVuJ5imdHm5zxk8w0QS5T+tDfnDxAlhWjpb7cw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.16.7", + "@babel/generator": "^7.17.3", + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-function-name": "^7.16.7", + "@babel/helper-hoist-variables": "^7.16.7", + "@babel/helper-split-export-declaration": "^7.16.7", + "@babel/parser": "^7.17.3", + "@babel/types": "^7.17.0", + "debug": "^4.1.0", + "globals": "^11.1.0" + } + }, + "@babel/types": { + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.17.0.tgz", + "integrity": "sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.16.7", + "to-fast-properties": "^2.0.0" + } + }, + "@discoveryjs/json-ext": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", + "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", + "dev": true + }, + "@jridgewell/resolve-uri": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz", + "integrity": "sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew==", + "dev": true + }, + "@jridgewell/sourcemap-codec": { + "version": "1.4.11", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz", + "integrity": "sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg==", + "dev": true + }, + "@jridgewell/trace-mapping": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.4.tgz", + "integrity": "sha512-vFv9ttIedivx0ux3QSjhgtCVjPZd5l46ZOMDSCwnH1yUO2e964gO8LZGyv2QkqcgR6TnBU1v+1IFqmeoG+0UJQ==", + "dev": true, + "requires": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true + }, + "@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "requires": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + } + }, + "@types/body-parser": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", + "dev": true, + "requires": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "@types/bonjour": { + "version": "3.5.10", + "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.10.tgz", + "integrity": "sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/connect": { + "version": "3.4.35", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", + "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/connect-history-api-fallback": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.5.tgz", + "integrity": "sha512-h8QJa8xSb1WD4fpKBDcATDNGXghFj6/3GRWG6dhmRcu0RX1Ubasur2Uvx5aeEwlf0MwblEC2bMzzMQntxnw/Cw==", + "dev": true, + "requires": { + "@types/express-serve-static-core": "*", + "@types/node": "*" + } + }, + "@types/eslint": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.1.tgz", + "integrity": "sha512-GE44+DNEyxxh2Kc6ro/VkIj+9ma0pO0bwv9+uHSyBrikYOHr8zYcdPvnBOp1aw8s+CjRvuSx7CyWqRrNFQ59mA==", + "dev": true, + "requires": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "@types/eslint-scope": { + "version": "3.7.3", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.3.tgz", + "integrity": "sha512-PB3ldyrcnAicT35TWPs5IcwKD8S333HMaa2VVv4+wdvebJkjWuW/xESoB8IwRcog8HYVYamb1g/R31Qv5Bx03g==", + "dev": true, + "requires": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "@types/estree": { + "version": "0.0.51", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz", + "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==", + "dev": true + }, + "@types/express": { + "version": "4.17.13", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz", + "integrity": "sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA==", + "dev": true, + "requires": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.18", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "@types/express-serve-static-core": { + "version": "4.17.28", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.28.tgz", + "integrity": "sha512-P1BJAEAW3E2DJUlkgq4tOL3RyMunoWXqbSCygWo5ZIWTjUgN1YnaXWW4VWl/oc8vs/XoYibEGBKP0uZyF4AHig==", + "dev": true, + "requires": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*" + } + }, + "@types/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==", + "dev": true, + "requires": { + "@types/minimatch": "*", + "@types/node": "*" + } + }, + "@types/html-minifier-terser": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", + "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==", + "dev": true + }, + "@types/http-proxy": { + "version": "1.17.8", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.8.tgz", + "integrity": "sha512-5kPLG5BKpWYkw/LVOGWpiq3nEVqxiN32rTgI53Sk12/xHFQ2rG3ehI9IO+O3W2QoKeyB92dJkoka8SUm6BX1pA==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/json-schema": { + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "dev": true + }, + "@types/mime": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", + "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==", + "dev": true + }, + "@types/minimatch": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz", + "integrity": "sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==", + "dev": true + }, + "@types/node": { + "version": "17.0.23", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.23.tgz", + "integrity": "sha512-UxDxWn7dl97rKVeVS61vErvw086aCYhDLyvRQZ5Rk65rZKepaFdm53GeqXaKBuOhED4e9uWq34IC3TdSdJJ2Gw==", + "dev": true + }, + "@types/qs": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==", + "dev": true + }, + "@types/range-parser": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", + "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==", + "dev": true + }, + "@types/retry": { + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.1.tgz", + "integrity": "sha512-xoDlM2S4ortawSWORYqsdU+2rxdh4LRW9ytc3zmT37RIKQh6IHyKwwtKhKis9ah8ol07DCkZxPt8BBvPjC6v4g==", + "dev": true + }, + "@types/serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha512-d/Hs3nWDxNL2xAczmOVZNj92YZCS6RGxfBPjKzuu/XirCgXdpKEb88dYNbrYGint6IVWLNP+yonwVAuRC0T2Dg==", + "dev": true, + "requires": { + "@types/express": "*" + } + }, + "@types/serve-static": { + "version": "1.13.10", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.10.tgz", + "integrity": "sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ==", + "dev": true, + "requires": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "@types/sockjs": { + "version": "0.3.33", + "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.33.tgz", + "integrity": "sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/ws": { + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz", + "integrity": "sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@webassemblyjs/ast": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", + "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", + "dev": true, + "requires": { + "@webassemblyjs/helper-numbers": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1" + } + }, + "@webassemblyjs/floating-point-hex-parser": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", + "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==", + "dev": true + }, + "@webassemblyjs/helper-api-error": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", + "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==", + "dev": true + }, + "@webassemblyjs/helper-buffer": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", + "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==", + "dev": true + }, + "@webassemblyjs/helper-numbers": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", + "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", + "dev": true, + "requires": { + "@webassemblyjs/floating-point-hex-parser": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/helper-wasm-bytecode": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", + "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==", + "dev": true + }, + "@webassemblyjs/helper-wasm-section": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", + "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1" + } + }, + "@webassemblyjs/ieee754": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", + "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", + "dev": true, + "requires": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "@webassemblyjs/leb128": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", + "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", + "dev": true, + "requires": { + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/utf8": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", + "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==", + "dev": true + }, + "@webassemblyjs/wasm-edit": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", + "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/helper-wasm-section": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-opt": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "@webassemblyjs/wast-printer": "1.11.1" + } + }, + "@webassemblyjs/wasm-gen": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", + "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" + } + }, + "@webassemblyjs/wasm-opt": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", + "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1" + } + }, + "@webassemblyjs/wasm-parser": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", + "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" + } + }, + "@webassemblyjs/wast-printer": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", + "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@xtuc/long": "4.2.2" + } + }, + "@webpack-cli/configtest": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.1.1.tgz", + "integrity": "sha512-1FBc1f9G4P/AxMqIgfZgeOTuRnwZMten8E7zap5zgpPInnCrP8D4Q81+4CWIch8i/Nf7nXjP0v6CjjbHOrXhKg==", + "dev": true + }, + "@webpack-cli/info": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.4.1.tgz", + "integrity": "sha512-PKVGmazEq3oAo46Q63tpMr4HipI3OPfP7LiNOEJg963RMgT0rqheag28NCML0o3GIzA3DmxP1ZIAv9oTX1CUIA==", + "dev": true, + "requires": { + "envinfo": "^7.7.3" + } + }, + "@webpack-cli/serve": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.6.1.tgz", + "integrity": "sha512-gNGTiTrjEVQ0OcVnzsRSqTxaBSr+dmTfm+qJsCDluky8uhdLWep7Gcr62QsAKHTMxjCS/8nEITsmFAhfIx+QSw==", + "dev": true + }, + "@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true + }, + "@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true + }, + "accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dev": true, + "requires": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + } + }, + "acorn": { + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", + "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", + "dev": true + }, + "acorn-import-assertions": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", + "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", + "dev": true + }, + "aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "requires": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + } + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dev": true, + "requires": { + "ajv": "^8.0.0" + }, + "dependencies": { + "ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + } + } + }, + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true + }, + "ansi-html-community": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", + "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==", + "dev": true + }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "array-differ": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-3.0.0.tgz", + "integrity": "sha512-THtfYS6KtME/yIAhKjZ2ul7XI96lQGHRputJQHO80LAWQnuGP4iCIN8vdMRboGbIEYBwU33q8Tch1os2+X0kMg==", + "dev": true + }, + "array-flatten": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", + "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==", + "dev": true + }, + "array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "dev": true, + "requires": { + "array-uniq": "^1.0.1" + } + }, + "array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", + "dev": true + }, + "arrify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz", + "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==", + "dev": true + }, + "async": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", + "dev": true, + "requires": { + "lodash": "^4.17.14" + } + }, + "babel-loader": { + "version": "8.2.4", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.4.tgz", + "integrity": "sha512-8dytA3gcvPPPv4Grjhnt8b5IIiTcq/zeXOPk4iTYI0SVXcsmuGg7JtBRDp8S9X+gJfhQ8ektjXZlDu1Bb33U8A==", + "dev": true, + "requires": { + "find-cache-dir": "^3.3.1", + "loader-utils": "^2.0.0", + "make-dir": "^3.1.0", + "schema-utils": "^2.6.5" + } + }, + "babel-plugin-dynamic-import-node": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", + "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", + "dev": true, + "requires": { + "object.assign": "^4.1.0" + } + }, + "babel-plugin-polyfill-corejs2": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.1.tgz", + "integrity": "sha512-v7/T6EQcNfVLfcN2X8Lulb7DjprieyLWJK/zOWH5DUYcAgex9sP3h25Q+DLsX9TloXe3y1O8l2q2Jv9q8UVB9w==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.13.11", + "@babel/helper-define-polyfill-provider": "^0.3.1", + "semver": "^6.1.1" + } + }, + "babel-plugin-polyfill-corejs3": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.2.tgz", + "integrity": "sha512-G3uJih0XWiID451fpeFaYGVuxHEjzKTHtc9uGFEjR6hHrvNzeS/PX+LLLcetJcytsB5m4j+K3o/EpXJNb/5IEQ==", + "dev": true, + "requires": { + "@babel/helper-define-polyfill-provider": "^0.3.1", + "core-js-compat": "^3.21.0" + } + }, + "babel-plugin-polyfill-regenerator": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.3.1.tgz", + "integrity": "sha512-Y2B06tvgHYt1x0yz17jGkGeeMr5FeKUu+ASJ+N6nB5lQ8Dapfg42i0OVrf8PNGJ3zKL4A23snMi1IRwrqqND7A==", + "dev": true, + "requires": { + "@babel/helper-define-polyfill-provider": "^0.3.1" + } + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=", + "dev": true + }, + "big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "dev": true + }, + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true + }, + "body-parser": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-SAAwOxgoCKMGs9uUAUFHygfLAyaniaoun6I8mFY9pRAJL9+Kec34aU+oIjDhTycub1jozEfEwx1W1IuOYxVSFw==", + "dev": true, + "requires": { + "bytes": "3.1.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.8.1", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.9.7", + "raw-body": "2.4.3", + "type-is": "~1.6.18" + }, + "dependencies": { + "bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "bonjour": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", + "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=", + "dev": true, + "requires": { + "array-flatten": "^2.1.0", + "deep-equal": "^1.0.1", + "dns-equal": "^1.0.0", + "dns-txt": "^2.0.2", + "multicast-dns": "^6.0.1", + "multicast-dns-service-types": "^1.1.0" + } + }, + "boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "browserslist": { + "version": "4.20.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.20.2.tgz", + "integrity": "sha512-CQOBCqp/9pDvDbx3xfMi+86pr4KXIf2FDkTTdeuYw8OxS9t898LA1Khq57gtufFILXpfgsSx5woNgsBgvGjpsA==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001317", + "electron-to-chromium": "^1.4.84", + "escalade": "^3.1.1", + "node-releases": "^2.0.2", + "picocolors": "^1.0.0" + } + }, + "buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "buffer-indexof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz", + "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==", + "dev": true + }, + "bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", + "dev": true + }, + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, + "camel-case": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", + "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", + "dev": true, + "requires": { + "pascal-case": "^3.1.2", + "tslib": "^2.0.3" + } + }, + "caniuse-lite": { + "version": "1.0.30001320", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001320.tgz", + "integrity": "sha512-MWPzG54AGdo3nWx7zHZTefseM5Y1ccM7hlQKHRqJkPozUaw3hNbBTMmLn16GG2FUzjR13Cr3NPfhIieX5PzXDA==", + "dev": true + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "requires": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + } + }, + "chrome-trace-event": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", + "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", + "dev": true + }, + "clean-css": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.2.4.tgz", + "integrity": "sha512-nKseG8wCzEuji/4yrgM/5cthL9oTDc5UOQyFMvW/Q53oP6gLH690o1NbuTh6Y18nujr7BxlsFuS7gXLnLzKJGg==", + "dev": true, + "requires": { + "source-map": "~0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true + }, + "clean-webpack-plugin": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/clean-webpack-plugin/-/clean-webpack-plugin-4.0.0.tgz", + "integrity": "sha512-WuWE1nyTNAyW5T7oNyys2EN0cfP2fdRxhxnIQWiAp0bMabPdHhoGxM8A6YL2GhqwgrPnnaemVE7nv5XJ2Fhh2w==", + "dev": true, + "requires": { + "del": "^4.1.1" + } + }, + "clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "colorette": { + "version": "2.0.16", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.16.tgz", + "integrity": "sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g==", + "dev": true + }, + "commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "dev": true + }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "dev": true + }, + "compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "dev": true, + "requires": { + "mime-db": ">= 1.43.0 < 2" + } + }, + "compression": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", + "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "dev": true, + "requires": { + "accepts": "~1.3.5", + "bytes": "3.0.0", + "compressible": "~2.0.16", + "debug": "2.6.9", + "on-headers": "~1.0.2", + "safe-buffer": "5.1.2", + "vary": "~1.1.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "connect-history-api-fallback": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz", + "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==", + "dev": true + }, + "content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dev": true, + "requires": { + "safe-buffer": "5.2.1" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + } + } + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "dev": true + }, + "convert-source-map": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", + "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.1" + } + }, + "cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", + "dev": true + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", + "dev": true + }, + "core-js-compat": { + "version": "3.21.1", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.21.1.tgz", + "integrity": "sha512-gbgX5AUvMb8gwxC7FLVWYT7Kkgu/y7+h/h1X43yJkNqhlK2fuYyQimqvKGNZFAY6CKii/GFKJ2cp/1/42TN36g==", + "dev": true, + "requires": { + "browserslist": "^4.19.1", + "semver": "7.0.0" + }, + "dependencies": { + "semver": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", + "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", + "dev": true + } + } + }, + "core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "css-loader": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.7.1.tgz", + "integrity": "sha512-yB5CNFa14MbPJcomwNh3wLThtkZgcNyI2bNMRt8iE5Z8Vwl7f8vQXFAzn2HDOJvtDq2NTZBUGMSUNNyrv3/+cw==", + "dev": true, + "requires": { + "icss-utils": "^5.1.0", + "postcss": "^8.4.7", + "postcss-modules-extract-imports": "^3.0.0", + "postcss-modules-local-by-default": "^4.0.0", + "postcss-modules-scope": "^3.0.0", + "postcss-modules-values": "^4.0.0", + "postcss-value-parser": "^4.2.0", + "semver": "^7.3.5" + }, + "dependencies": { + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "css-select": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", + "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", + "dev": true, + "requires": { + "boolbase": "^1.0.0", + "css-what": "^6.0.1", + "domhandler": "^4.3.1", + "domutils": "^2.8.0", + "nth-check": "^2.0.1" + } + }, + "css-what": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.0.1.tgz", + "integrity": "sha512-z93ZGFLNc6yaoXAmVhqoSIb+BduplteCt1fepvwhBUQK6MNE4g6fgjpuZKJKp0esUe+vXWlIkwZZjNWoOKw0ZA==", + "dev": true + }, + "cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true + }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "deep-equal": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", + "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==", + "dev": true, + "requires": { + "is-arguments": "^1.0.4", + "is-date-object": "^1.0.1", + "is-regex": "^1.0.4", + "object-is": "^1.0.1", + "object-keys": "^1.1.1", + "regexp.prototype.flags": "^1.2.0" + } + }, + "default-gateway": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", + "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==", + "dev": true, + "requires": { + "execa": "^5.0.0" + }, + "dependencies": { + "execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + } + }, + "get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true + }, + "human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true + } + } + }, + "define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "dev": true + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dev": true, + "requires": { + "object-keys": "^1.0.12" + } + }, + "del": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/del/-/del-4.1.1.tgz", + "integrity": "sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==", + "dev": true, + "requires": { + "@types/glob": "^7.1.1", + "globby": "^6.1.0", + "is-path-cwd": "^2.0.0", + "is-path-in-cwd": "^2.0.0", + "p-map": "^2.0.0", + "pify": "^4.0.1", + "rimraf": "^2.6.3" + } + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "dev": true + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", + "dev": true + }, + "detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", + "dev": true + }, + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "requires": { + "path-type": "^4.0.0" + } + }, + "dns-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", + "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=", + "dev": true + }, + "dns-packet": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.4.tgz", + "integrity": "sha512-BQ6F4vycLXBvdrJZ6S3gZewt6rcrks9KBgM9vrhW+knGRqc8uEdT7fuCwloc7nny5xNoMJ17HGH0R/6fpo8ECA==", + "dev": true, + "requires": { + "ip": "^1.1.0", + "safe-buffer": "^5.0.1" + } + }, + "dns-txt": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz", + "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=", + "dev": true, + "requires": { + "buffer-indexof": "^1.0.0" + } + }, + "dom-converter": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", + "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==", + "dev": true, + "requires": { + "utila": "~0.4" + } + }, + "dom-serializer": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz", + "integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==", + "dev": true, + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + } + }, + "domelementtype": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", + "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==", + "dev": true + }, + "domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "dev": true, + "requires": { + "domelementtype": "^2.2.0" + } + }, + "domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "dev": true, + "requires": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + } + }, + "dot-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", + "dev": true, + "requires": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", + "dev": true + }, + "electron-to-chromium": { + "version": "1.4.96", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.96.tgz", + "integrity": "sha512-DPNjvNGPabv6FcyjzLAN4C0psN/GgD9rSGvMTuv81SeXG/EX3mCz0wiw9N1tUEnfQXYCJi3H8M0oFPRziZh7rw==", + "dev": true + }, + "emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "dev": true + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "dev": true + }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "requires": { + "once": "^1.4.0" + } + }, + "enhanced-resolve": { + "version": "5.9.2", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.9.2.tgz", + "integrity": "sha512-GIm3fQfwLJ8YZx2smuHpBKkXC1yOk+OBEmKckVyL0i/ea8mqDEykK3ld5dgH1QYPNyT/lIllxV2LULnxCHaHkA==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + } + }, + "entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "dev": true + }, + "envinfo": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz", + "integrity": "sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==", + "dev": true + }, + "es-module-lexer": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", + "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", + "dev": true + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + } + }, + "esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "requires": { + "estraverse": "^5.2.0" + }, + "dependencies": { + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + } + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", + "dev": true + }, + "eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "dev": true + }, + "events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true + }, + "execa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", + "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" + } + }, + "express": { + "version": "4.17.3", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.3.tgz", + "integrity": "sha512-yuSQpz5I+Ch7gFrPCk4/c+dIBKlQUxtgwqzph132bsT6qhuzss6I8cLJQz7B3rFblzd6wtcI0ZbGltH/C4LjUg==", + "dev": true, + "requires": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.19.2", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.4.2", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.9.7", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.17.2", + "serve-static": "1.14.2", + "setprototypeof": "1.2.0", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "dependencies": { + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", + "dev": true + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + } + } + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "fast-glob": { + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", + "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + } + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "fastest-levenshtein": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz", + "integrity": "sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow==", + "dev": true + }, + "fastq": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "dev": true, + "requires": { + "reusify": "^1.0.4" + } + }, + "faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "dev": true, + "requires": { + "websocket-driver": ">=0.5.1" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "dev": true, + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "find-cache-dir": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + } + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "follow-redirects": { + "version": "1.14.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz", + "integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==", + "dev": true + }, + "forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "dev": true + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", + "dev": true + }, + "fs-monkey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz", + "integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==", + "dev": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true + }, + "get-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + } + }, + "get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true + }, + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true + }, + "globby": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", + "dev": true, + "requires": { + "array-union": "^1.0.1", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + } + } + }, + "graceful-fs": { + "version": "4.2.9", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", + "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==", + "dev": true + }, + "handle-thing": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", + "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", + "dev": true + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true + }, + "has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dev": true, + "requires": { + "has-symbols": "^1.0.2" + } + }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true + }, + "hpack.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + } + } + }, + "html-entities": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.3.tgz", + "integrity": "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==", + "dev": true + }, + "html-minifier-terser": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", + "integrity": "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==", + "dev": true, + "requires": { + "camel-case": "^4.1.2", + "clean-css": "^5.2.2", + "commander": "^8.3.0", + "he": "^1.2.0", + "param-case": "^3.0.4", + "relateurl": "^0.2.7", + "terser": "^5.10.0" + } + }, + "html-webpack-plugin": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.5.0.tgz", + "integrity": "sha512-sy88PC2cRTVxvETRgUHFrL4No3UxvcH8G1NepGhqaTT+GXN2kTamqasot0inS5hXeg1cMbFDt27zzo9p35lZVw==", + "dev": true, + "requires": { + "@types/html-minifier-terser": "^6.0.0", + "html-minifier-terser": "^6.0.2", + "lodash": "^4.17.21", + "pretty-error": "^4.0.0", + "tapable": "^2.0.0" + } + }, + "htmlparser2": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", + "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", + "dev": true, + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^4.0.0", + "domutils": "^2.5.2", + "entities": "^2.0.0" + } + }, + "http-deceiver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=", + "dev": true + }, + "http-errors": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", + "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", + "dev": true, + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.1" + } + }, + "http-parser-js": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.6.tgz", + "integrity": "sha512-vDlkRPDJn93swjcjqMSaGSPABbIarsr1TLAui/gLDXzV5VsJNdXNzMYDyNBLQkjWQCJ1uizu8T2oDMhmGt0PRA==", + "dev": true + }, + "http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "dev": true, + "requires": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + } + }, + "http-proxy-middleware": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.4.tgz", + "integrity": "sha512-m/4FxX17SUvz4lJ5WPXOHDUuCwIqXLfLHs1s0uZ3oYjhoXlx9csYxaOa0ElDEJ+h8Q4iJ1s+lTMbiCa4EXIJqg==", + "dev": true, + "requires": { + "@types/http-proxy": "^1.17.8", + "http-proxy": "^1.18.1", + "is-glob": "^4.0.1", + "is-plain-obj": "^3.0.0", + "micromatch": "^4.0.2" + } + }, + "human-signals": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", + "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", + "dev": true + }, + "husky": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/husky/-/husky-7.0.4.tgz", + "integrity": "sha512-vbaCKN2QLtP/vD4yvs6iz6hBEo6wkSzs8HpRah1Z6aGmF2KW5PdYuAd7uX5a+OyBZHBhd+TFLqgjUgytQr4RvQ==", + "dev": true + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "icss-utils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", + "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", + "dev": true + }, + "ignore": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "dev": true + }, + "import-local": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", + "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", + "dev": true, + "requires": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + } + }, + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "interpret": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", + "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==", + "dev": true + }, + "ip": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", + "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=", + "dev": true + }, + "ipaddr.js": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz", + "integrity": "sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng==", + "dev": true + }, + "is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-core-module": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz", + "integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "is-path-cwd": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", + "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", + "dev": true + }, + "is-path-in-cwd": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz", + "integrity": "sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ==", + "dev": true, + "requires": { + "is-path-inside": "^2.1.0" + } + }, + "is-path-inside": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-2.1.0.tgz", + "integrity": "sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==", + "dev": true, + "requires": { + "path-is-inside": "^1.0.2" + } + }, + "is-plain-obj": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", + "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", + "dev": true + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true + }, + "is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "requires": { + "is-docker": "^2.0.0" + } + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + }, + "jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "dev": true, + "requires": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "dependencies": { + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true + }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "json5": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", + "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", + "dev": true + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true + }, + "loader-runner": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.2.0.tgz", + "integrity": "sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw==", + "dev": true + }, + "loader-utils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz", + "integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=", + "dev": true + }, + "lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "dev": true, + "requires": { + "tslib": "^2.0.3" + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "requires": { + "semver": "^6.0.0" + } + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", + "dev": true + }, + "memfs": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.1.tgz", + "integrity": "sha512-1c9VPVvW5P7I85c35zAdEr1TD5+F11IToIHIlrVIcflfnzPkJa0ZoYEoEdYDP8KgPFoSZ/opDrUsAoZWym3mtw==", + "dev": true, + "requires": { + "fs-monkey": "1.0.3" + } + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", + "dev": true + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "dev": true + }, + "micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "requires": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + } + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true + }, + "mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true + }, + "mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "requires": { + "mime-db": "1.52.0" + } + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + }, + "mini-css-extract-plugin": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.6.0.tgz", + "integrity": "sha512-ndG8nxCEnAemsg4FSgS+yNyHKgkTB4nPKqCOgh65j3/30qqC5RaSQQXMm++Y6sb6E1zRSxPkztj9fqxhS1Eo6w==", + "dev": true, + "requires": { + "schema-utils": "^4.0.0" + }, + "dependencies": { + "ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.3" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + } + } + } + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", + "dev": true + }, + "mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "requires": { + "minimist": "^1.2.6" + } + }, + "mri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", + "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", + "dev": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "multicast-dns": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz", + "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==", + "dev": true, + "requires": { + "dns-packet": "^1.3.1", + "thunky": "^1.0.2" + } + }, + "multicast-dns-service-types": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", + "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=", + "dev": true + }, + "multimatch": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/multimatch/-/multimatch-4.0.0.tgz", + "integrity": "sha512-lDmx79y1z6i7RNx0ZGCPq1bzJ6ZoDDKbvh7jxr9SJcWLkShMzXrHbYVpTdnhNM5MXpDUxCQ4DgqVttVXlBgiBQ==", + "dev": true, + "requires": { + "@types/minimatch": "^3.0.3", + "array-differ": "^3.0.0", + "array-union": "^2.1.0", + "arrify": "^2.0.1", + "minimatch": "^3.0.4" + }, + "dependencies": { + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true + } + } + }, + "nanoid": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.2.tgz", + "integrity": "sha512-CuHBogktKwpm5g2sRgv83jEy2ijFzBwMoYA60orPDR7ynsLijJDqgsi4RDGj3OJpy3Ieb+LYwiRmIOGyytgITA==", + "dev": true + }, + "negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "dev": true + }, + "neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true + }, + "no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "dev": true, + "requires": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, + "node-forge": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.0.tgz", + "integrity": "sha512-08ARB91bUi6zNKzVmaj3QO7cr397uiDT2nJ63cHjyNtCTWIgvS47j3eT0WfzUwS9+6Z5YshRaoasFkXCKrIYbA==", + "dev": true + }, + "node-releases": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.2.tgz", + "integrity": "sha512-XxYDdcQ6eKqp/YjI+tb2C5WM2LgjnZrfYg4vgQt49EK268b6gYCHsBLrK2qvJo4FmCtqmKezb0WZFK4fkrZNsg==", + "dev": true + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "requires": { + "path-key": "^3.0.0" + } + }, + "nth-check": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.1.tgz", + "integrity": "sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w==", + "dev": true, + "requires": { + "boolbase": "^1.0.0" + } + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + }, + "object-is": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", + "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + }, + "object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + } + }, + "obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", + "dev": true + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "dev": true, + "requires": { + "ee-first": "1.1.1" + } + }, + "on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "open": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz", + "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==", + "dev": true, + "requires": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "p-map": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", + "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", + "dev": true + }, + "p-retry": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.1.tgz", + "integrity": "sha512-e2xXGNhZOZ0lfgR9kL34iGlU8N/KO0xZnQxVEwdeOvpqNDQfdnxIYizvWtK8RglUa3bGqI8g0R/BdfzLMxRkiA==", + "dev": true, + "requires": { + "@types/retry": "^0.12.0", + "retry": "^0.13.1" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "param-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", + "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", + "dev": true, + "requires": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true + }, + "pascal-case": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", + "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", + "dev": true, + "requires": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", + "dev": true + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", + "dev": true + }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true + }, + "picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true, + "requires": { + "pinkie": "^2.0.0" + } + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "requires": { + "find-up": "^4.0.0" + } + }, + "portfinder": { + "version": "1.0.28", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz", + "integrity": "sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA==", + "dev": true, + "requires": { + "async": "^2.6.2", + "debug": "^3.1.1", + "mkdirp": "^0.5.5" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + } + } + }, + "postcss": { + "version": "8.4.12", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.12.tgz", + "integrity": "sha512-lg6eITwYe9v6Hr5CncVbK70SoioNQIq81nsaG86ev5hAidQvmOeETBqs7jm43K2F5/Ley3ytDtriImV6TpNiSg==", + "dev": true, + "requires": { + "nanoid": "^3.3.1", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + } + }, + "postcss-modules-extract-imports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", + "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", + "dev": true + }, + "postcss-modules-local-by-default": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz", + "integrity": "sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ==", + "dev": true, + "requires": { + "icss-utils": "^5.0.0", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.1.0" + } + }, + "postcss-modules-scope": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz", + "integrity": "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==", + "dev": true, + "requires": { + "postcss-selector-parser": "^6.0.4" + } + }, + "postcss-modules-values": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", + "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", + "dev": true, + "requires": { + "icss-utils": "^5.0.0" + } + }, + "postcss-selector-parser": { + "version": "6.0.9", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.9.tgz", + "integrity": "sha512-UO3SgnZOVTwu4kyLR22UQ1xZh086RyNZppb7lLAKBFK8a32ttG5i87Y/P3+2bRSjZNyJ1B7hfFNo273tKe9YxQ==", + "dev": true, + "requires": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + } + }, + "postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + }, + "prettier": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.6.1.tgz", + "integrity": "sha512-8UVbTBYGwN37Bs9LERmxCPjdvPxlEowx2urIL6urHzdb3SDq4B/Z6xLFCblrSnE4iKWcS6ziJ3aOYrc1kz/E2A==", + "dev": true + }, + "pretty-error": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz", + "integrity": "sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==", + "dev": true, + "requires": { + "lodash": "^4.17.20", + "renderkid": "^3.0.0" + } + }, + "pretty-quick": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/pretty-quick/-/pretty-quick-3.1.3.tgz", + "integrity": "sha512-kOCi2FJabvuh1as9enxYmrnBC6tVMoVOenMaBqRfsvBHB0cbpYHjdQEpSglpASDFEXVwplpcGR4CLEaisYAFcA==", + "dev": true, + "requires": { + "chalk": "^3.0.0", + "execa": "^4.0.0", + "find-up": "^4.1.0", + "ignore": "^5.1.4", + "mri": "^1.1.5", + "multimatch": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dev": true, + "requires": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "dependencies": { + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true + } + } + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + }, + "qs": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw==", + "dev": true + }, + "queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true + }, + "raw-body": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.3.tgz", + "integrity": "sha512-UlTNLIcu0uzb4D2f4WltY6cVjLi+/jEN4lgEUj3E04tpMDpUlkBo/eSn6zou9hum2VMNpCCUone0O0WeJim07g==", + "dev": true, + "requires": { + "bytes": "3.1.2", + "http-errors": "1.8.1", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "dependencies": { + "bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true + } + } + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "requires": { + "picomatch": "^2.2.1" + } + }, + "rechoir": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz", + "integrity": "sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==", + "dev": true, + "requires": { + "resolve": "^1.9.0" + } + }, + "regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", + "dev": true + }, + "regenerate-unicode-properties": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.0.1.tgz", + "integrity": "sha512-vn5DU6yg6h8hP/2OkQo3K7uVILvY4iu0oI4t3HFa81UPkhGJwkRwM10JEc3upjdhHjs/k8GJY1sRBhk5sr69Bw==", + "dev": true, + "requires": { + "regenerate": "^1.4.2" + } + }, + "regenerator-runtime": { + "version": "0.13.9", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", + "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==", + "dev": true + }, + "regenerator-transform": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.5.tgz", + "integrity": "sha512-eOf6vka5IO151Jfsw2NO9WpGX58W6wWmefK3I1zEGr0lOD0u8rwPaNqQL1aRxUaxLeKO3ArNh3VYg1KbaD+FFw==", + "dev": true, + "requires": { + "@babel/runtime": "^7.8.4" + } + }, + "regexp.prototype.flags": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.1.tgz", + "integrity": "sha512-pMR7hBVUUGI7PMA37m2ofIdQCsomVnas+Jn5UPGAHQ+/LlwKm/aTLJHdasmHRzlfeZwHiAOaRSo2rbBDm3nNUQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, + "regexpu-core": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.0.1.tgz", + "integrity": "sha512-CriEZlrKK9VJw/xQGJpQM5rY88BtuL8DM+AEwvcThHilbxiTAy8vq4iJnd2tqq8wLmjbGZzP7ZcKFjbGkmEFrw==", + "dev": true, + "requires": { + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.0.1", + "regjsgen": "^0.6.0", + "regjsparser": "^0.8.2", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.0.0" + } + }, + "regjsgen": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.6.0.tgz", + "integrity": "sha512-ozE883Uigtqj3bx7OhL1KNbCzGyW2NQZPl6Hs09WTvCuZD5sTI4JY58bkbQWa/Y9hxIsvJ3M8Nbf7j54IqeZbA==", + "dev": true + }, + "regjsparser": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.8.4.tgz", + "integrity": "sha512-J3LABycON/VNEu3abOviqGHuB/LOtOQj8SKmfP9anY5GfAVw/SPjwzSjxGjbZXIxbGfqTHtJw58C2Li/WkStmA==", + "dev": true, + "requires": { + "jsesc": "~0.5.0" + }, + "dependencies": { + "jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", + "dev": true + } + } + }, + "relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=", + "dev": true + }, + "renderkid": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz", + "integrity": "sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==", + "dev": true, + "requires": { + "css-select": "^4.1.3", + "dom-converter": "^0.2.0", + "htmlparser2": "^6.1.0", + "lodash": "^4.17.21", + "strip-ansi": "^6.0.1" + } + }, + "require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true + }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", + "dev": true + }, + "resolve": { + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", + "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", + "dev": true, + "requires": { + "is-core-module": "^2.8.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + } + }, + "resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "requires": { + "resolve-from": "^5.0.0" + } + }, + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true + }, + "retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "dev": true + }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true + }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "requires": { + "queue-microtask": "^1.2.2" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "schema-utils": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", + "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.5", + "ajv": "^6.12.4", + "ajv-keywords": "^3.5.2" + } + }, + "select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=", + "dev": true + }, + "selfsigned": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.0.1.tgz", + "integrity": "sha512-LmME957M1zOsUhG+67rAjKfiWFox3SBxE/yymatMZsAx+oMrJ0YQ8AToOnyCm7xbeg2ep37IHLxdu0o2MavQOQ==", + "dev": true, + "requires": { + "node-forge": "^1" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "send": { + "version": "0.17.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.2.tgz", + "integrity": "sha512-UJYB6wFSJE3G00nEivR5rgWp8c2xXvJ3OPWPhmuteU0IKj8nKbG3DrjiOmLwpnHGYWAVwA69zmTm++YG0Hmwww==", + "dev": true, + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "1.8.1", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + } + } + }, + "serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + }, + "serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=", + "dev": true, + "requires": { + "accepts": "~1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", + "dev": true, + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "dev": true + } + } + }, + "serve-static": { + "version": "1.14.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.2.tgz", + "integrity": "sha512-+TMNA9AFxUEGuC0z2mevogSnn9MXKb4fa7ngeRMJaaGv8vTwnIEkKi+QGvPt33HSnf8pRS+WGM0EbMtCJLKMBQ==", + "dev": true, + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.2" + } + }, + "setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true + }, + "shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dev": true, + "requires": { + "kind-of": "^6.0.2" + } + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "sockjs": { + "version": "0.3.24", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", + "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", + "dev": true, + "requires": { + "faye-websocket": "^0.11.3", + "uuid": "^8.3.2", + "websocket-driver": "^0.7.4" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + }, + "source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "dev": true + }, + "source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "spdy": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", + "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", + "dev": true, + "requires": { + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" + } + }, + "spdy-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", + "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", + "dev": true, + "requires": { + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" + } + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "dev": true + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true + }, + "style-loader": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.1.tgz", + "integrity": "sha512-GPcQ+LDJbrcxHORTRes6Jy2sfvK2kS6hpSfI/fXhPt+spVzxF6LJ1dHLN9zIGmVaaP044YKaIatFaufENRiDoQ==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true + }, + "tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "dev": true + }, + "terser": { + "version": "5.12.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.12.1.tgz", + "integrity": "sha512-NXbs+7nisos5E+yXwAD+y7zrcTkMqb0dEJxIGtSKPdCBzopf7ni4odPul2aechpV7EXNvOudYOX2bb5tln1jbQ==", + "dev": true, + "requires": { + "acorn": "^8.5.0", + "commander": "^2.20.0", + "source-map": "~0.7.2", + "source-map-support": "~0.5.20" + }, + "dependencies": { + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", + "dev": true + } + } + }, + "terser-webpack-plugin": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.1.tgz", + "integrity": "sha512-GvlZdT6wPQKbDNW/GDQzZFg/j4vKU96yl2q6mcUkzKOgW4gwf1Z8cZToUCrz31XHlPWH8MVb1r2tFtdDtTGJ7g==", + "dev": true, + "requires": { + "jest-worker": "^27.4.5", + "schema-utils": "^3.1.1", + "serialize-javascript": "^6.0.0", + "source-map": "^0.6.1", + "terser": "^5.7.2" + }, + "dependencies": { + "schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "thunky": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", + "dev": true + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dev": true + }, + "tslib": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", + "dev": true + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dev": true, + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "unicode-canonical-property-names-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", + "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==", + "dev": true + }, + "unicode-match-property-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "dev": true, + "requires": { + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + } + }, + "unicode-match-property-value-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz", + "integrity": "sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw==", + "dev": true + }, + "unicode-property-aliases-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.0.0.tgz", + "integrity": "sha512-5Zfuy9q/DFr4tfO7ZPeVXb1aPoeQSdeFMLpYuFebehDAhbuevLs5yxSZmIFN1tP5F9Wl4IpJrYojg85/zgyZHQ==", + "dev": true + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "dev": true + }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "utila": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", + "integrity": "sha1-ihagXURWV6Oupe7MWxKk+lN5dyw=", + "dev": true + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", + "dev": true + }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", + "dev": true + }, + "watchpack": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.3.1.tgz", + "integrity": "sha512-x0t0JuydIo8qCNctdDrn1OzH/qDzk2+rdCOC3YzumZ42fiMqmQ7T3xQurykYMhYfHaPHTp4ZxAx2NfUo1K6QaA==", + "dev": true, + "requires": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + } + }, + "wbuf": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", + "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", + "dev": true, + "requires": { + "minimalistic-assert": "^1.0.0" + } + }, + "webpack": { + "version": "5.70.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.70.0.tgz", + "integrity": "sha512-ZMWWy8CeuTTjCxbeaQI21xSswseF2oNOwc70QSKNePvmxE7XW36i7vpBMYZFAUHPwQiEbNGCEYIOOlyRbdGmxw==", + "dev": true, + "requires": { + "@types/eslint-scope": "^3.7.3", + "@types/estree": "^0.0.51", + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/wasm-edit": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "acorn": "^8.4.1", + "acorn-import-assertions": "^1.7.6", + "browserslist": "^4.14.5", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.9.2", + "es-module-lexer": "^0.9.0", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.9", + "json-parse-better-errors": "^1.0.2", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.1.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.1.3", + "watchpack": "^2.3.1", + "webpack-sources": "^3.2.3" + }, + "dependencies": { + "schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + } + } + }, + "webpack-cli": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.9.2.tgz", + "integrity": "sha512-m3/AACnBBzK/kMTcxWHcZFPrw/eQuY4Df1TxvIWfWM2x7mRqBQCqKEd96oCUa9jkapLBaFfRce33eGDb4Pr7YQ==", + "dev": true, + "requires": { + "@discoveryjs/json-ext": "^0.5.0", + "@webpack-cli/configtest": "^1.1.1", + "@webpack-cli/info": "^1.4.1", + "@webpack-cli/serve": "^1.6.1", + "colorette": "^2.0.14", + "commander": "^7.0.0", + "execa": "^5.0.0", + "fastest-levenshtein": "^1.0.12", + "import-local": "^3.0.2", + "interpret": "^2.2.0", + "rechoir": "^0.7.0", + "webpack-merge": "^5.7.3" + }, + "dependencies": { + "commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true + }, + "execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + } + }, + "get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true + }, + "human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true + } + } + }, + "webpack-dev-middleware": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.1.tgz", + "integrity": "sha512-81EujCKkyles2wphtdrnPg/QqegC/AtqNH//mQkBYSMqwFVCQrxM6ktB2O/SPlZy7LqeEfTbV3cZARGQz6umhg==", + "dev": true, + "requires": { + "colorette": "^2.0.10", + "memfs": "^3.4.1", + "mime-types": "^2.1.31", + "range-parser": "^1.2.1", + "schema-utils": "^4.0.0" + }, + "dependencies": { + "ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.3" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + } + } + } + }, + "webpack-dev-server": { + "version": "4.7.4", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.7.4.tgz", + "integrity": "sha512-nfdsb02Zi2qzkNmgtZjkrMOcXnYZ6FLKcQwpxT7MvmHKc+oTtDsBju8j+NMyAygZ9GW1jMEUpy3itHtqgEhe1A==", + "dev": true, + "requires": { + "@types/bonjour": "^3.5.9", + "@types/connect-history-api-fallback": "^1.3.5", + "@types/express": "^4.17.13", + "@types/serve-index": "^1.9.1", + "@types/sockjs": "^0.3.33", + "@types/ws": "^8.2.2", + "ansi-html-community": "^0.0.8", + "bonjour": "^3.5.0", + "chokidar": "^3.5.3", + "colorette": "^2.0.10", + "compression": "^1.7.4", + "connect-history-api-fallback": "^1.6.0", + "default-gateway": "^6.0.3", + "del": "^6.0.0", + "express": "^4.17.1", + "graceful-fs": "^4.2.6", + "html-entities": "^2.3.2", + "http-proxy-middleware": "^2.0.0", + "ipaddr.js": "^2.0.1", + "open": "^8.0.9", + "p-retry": "^4.5.0", + "portfinder": "^1.0.28", + "schema-utils": "^4.0.0", + "selfsigned": "^2.0.0", + "serve-index": "^1.9.1", + "sockjs": "^0.3.21", + "spdy": "^4.0.2", + "strip-ansi": "^7.0.0", + "webpack-dev-middleware": "^5.3.1", + "ws": "^8.4.2" + }, + "dependencies": { + "ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.3" + } + }, + "ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true + }, + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true + }, + "del": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/del/-/del-6.0.0.tgz", + "integrity": "sha512-1shh9DQ23L16oXSZKB2JxpL7iMy2E0S9d517ptA1P8iw0alkPtQcrKH7ru31rYtKwF499HkTu+DRzq3TCKDFRQ==", + "dev": true, + "requires": { + "globby": "^11.0.1", + "graceful-fs": "^4.2.4", + "is-glob": "^4.0.1", + "is-path-cwd": "^2.2.0", + "is-path-inside": "^3.0.2", + "p-map": "^4.0.0", + "rimraf": "^3.0.2", + "slash": "^3.0.0" + } + }, + "globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + } + }, + "is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, + "requires": { + "aggregate-error": "^3.0.0" + } + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + } + }, + "strip-ansi": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", + "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "dev": true, + "requires": { + "ansi-regex": "^6.0.1" + } + } + } + }, + "webpack-merge": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz", + "integrity": "sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==", + "dev": true, + "requires": { + "clone-deep": "^4.0.1", + "wildcard": "^2.0.0" + } + }, + "webpack-sources": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "dev": true + }, + "websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "dev": true, + "requires": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + } + }, + "websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "dev": true + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "wildcard": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz", + "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==", + "dev": true + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "ws": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz", + "integrity": "sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==", + "dev": true + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } +} diff --git a/user_interface/package.json b/user_interface/package.json new file mode 100644 index 0000000..0d4721b --- /dev/null +++ b/user_interface/package.json @@ -0,0 +1,23 @@ +{ + "name": "html5-slot-machine", + "description": "Modern casino slot machine game using only plain JavaScript (Web Animations API)", + "scripts": { + "start": "webpack serve --mode development" + }, + "devDependencies": { + "@babel/core": "^7.15.0", + "@babel/preset-env": "^7.15.0", + "babel-loader": "^8.1.0", + "clean-webpack-plugin": "^4.0.0", + "css-loader": "^6.4.0", + "html-webpack-plugin": "^5.3.2", + "husky": "^7.0.1", + "mini-css-extract-plugin": "^2.4.3", + "prettier": "^2.3.2", + "pretty-quick": "^3.1.1", + "style-loader": "^3.2.1", + "webpack": "^5.49.0", + "webpack-cli": "^4.7.2", + "webpack-dev-server": "^4.3.1" + } +} diff --git a/user_interface/src/assets/bg.jpg b/user_interface/src/assets/bg.jpg new file mode 100644 index 0000000..df3ba0d Binary files /dev/null and b/user_interface/src/assets/bg.jpg differ diff --git a/user_interface/src/assets/bg_small.jpg b/user_interface/src/assets/bg_small.jpg new file mode 100644 index 0000000..df3ba0d Binary files /dev/null and b/user_interface/src/assets/bg_small.jpg differ diff --git a/user_interface/src/assets/symbols/License and Author Info.pdf b/user_interface/src/assets/symbols/License and Author Info.pdf new file mode 100644 index 0000000..a281f47 Binary files /dev/null and b/user_interface/src/assets/symbols/License and Author Info.pdf differ diff --git a/user_interface/src/assets/symbols/at_at.png b/user_interface/src/assets/symbols/at_at.png new file mode 100644 index 0000000..48b0d53 Binary files /dev/null and b/user_interface/src/assets/symbols/at_at.png differ diff --git a/user_interface/src/assets/symbols/c3po.png b/user_interface/src/assets/symbols/c3po.png new file mode 100644 index 0000000..56faf51 Binary files /dev/null and b/user_interface/src/assets/symbols/c3po.png differ diff --git a/user_interface/src/assets/symbols/darth_vader.png b/user_interface/src/assets/symbols/darth_vader.png new file mode 100644 index 0000000..4d77f82 Binary files /dev/null and b/user_interface/src/assets/symbols/darth_vader.png differ diff --git a/user_interface/src/assets/symbols/death_star.png b/user_interface/src/assets/symbols/death_star.png new file mode 100644 index 0000000..454b04e Binary files /dev/null and b/user_interface/src/assets/symbols/death_star.png differ diff --git a/user_interface/src/assets/symbols/falcon.png b/user_interface/src/assets/symbols/falcon.png new file mode 100644 index 0000000..32b05f7 Binary files /dev/null and b/user_interface/src/assets/symbols/falcon.png differ diff --git a/user_interface/src/assets/symbols/r2d2.png b/user_interface/src/assets/symbols/r2d2.png new file mode 100644 index 0000000..8f8122a Binary files /dev/null and b/user_interface/src/assets/symbols/r2d2.png differ diff --git a/user_interface/src/assets/symbols/stormtrooper.png b/user_interface/src/assets/symbols/stormtrooper.png new file mode 100644 index 0000000..6815f6b Binary files /dev/null and b/user_interface/src/assets/symbols/stormtrooper.png differ diff --git a/user_interface/src/assets/symbols/tie_ln.png b/user_interface/src/assets/symbols/tie_ln.png new file mode 100644 index 0000000..4380dfa Binary files /dev/null and b/user_interface/src/assets/symbols/tie_ln.png differ diff --git a/user_interface/src/assets/symbols/yoda.png b/user_interface/src/assets/symbols/yoda.png new file mode 100644 index 0000000..e67a02b Binary files /dev/null and b/user_interface/src/assets/symbols/yoda.png differ diff --git a/user_interface/src/css/style.css b/user_interface/src/css/style.css new file mode 100644 index 0000000..a5ee414 --- /dev/null +++ b/user_interface/src/css/style.css @@ -0,0 +1,104 @@ +body { + width: 100vw; + height: 100vh; + padding: 1rem; + background-image: url("../assets/bg.jpg"); + background-size: cover; + display: flex; + align-items: center; + justify-content: center; + font-size: 24px; + font-family: "Roboto Condensed", sans-serif; + /*opacity: 0.4;*/ +} + +@media only screen and (max-width: 1200px) { +body { + background-image: url("../assets/bg_small.jpg"); +} +} + +#reels { + display: flex; + width: 100vw; + height: calc((3 / 4) * 100vw); + max-height: calc(90vh - 50px - 40px); + max-width: calc((4 / 3) * (90vh - 50px - 40px)); + margin-left: 700px; + /*opacity: 0.9;*/ +} + +.reel { + overflow: hidden; + width: 30%; + height: 100%; + /*opacity: 0.5;*/ +} + +.reel > .icons > img { + width: calc(100% + 6px); + margin: -3px 0 0 -3px; + height: auto; + /*opacity: 0.5;*/ + + /* enable gpu accelaration, fixes iOS flicker */ + transform: translate3d(0, 0, 0); +} + +#controls { + background-color: rgba(255, 255, 255, 0.7); + height: 50px; + display: flex; + justify-content: space-between; + align-items: center; + padding: 5px 30px; +} + +#controls label { + display: flex; + align-items: center; + margin: 0; + /*opacity: 0.5;*/ + +} + +#controls label input { + margin-right: 5px; + /*opacity: 0.5;*/ +} + +input[type="checkbox"] { + width: 40px; + height: 40px; +} + +#jackpot { + color: #c7f7ff; + font-size: 40px; + text-align: center; + margin-left: 700px; +} + +#slot.inverted .reel { + transform: scaleY(-1); +} + +#slot.inverted .reel > .icons > img { + transform: scaleY(-1); +} + +#win { + position: absolute; + margin: 0 0 0 170px; +} + +#spin_cost { + margin: 0 0 0 70px; + color: rgb(116, 116, 116); + position: absolute; +} + +#returnCoef { + margin: 0 0 0 250px; + position: absolute; +} \ No newline at end of file diff --git a/user_interface/src/index.html b/user_interface/src/index.html new file mode 100644 index 0000000..3512aaf --- /dev/null +++ b/user_interface/src/index.html @@ -0,0 +1,42 @@ + + + + HTML 5 Slot Machine + + + + + +
+
Jackpot: соточка за курсач
+
+
+
+
+
+ +
+
+ + +

COST: 10

+

WIN: 0

+

Return coefficient: 0.0

+ + +
+
+ + + + \ No newline at end of file diff --git a/user_interface/src/js/Reel.js b/user_interface/src/js/Reel.js new file mode 100644 index 0000000..3f5d554 --- /dev/null +++ b/user_interface/src/js/Reel.js @@ -0,0 +1,76 @@ +import Symbol from "./Symbol.js"; + +export default class Reel { + constructor(reelContainer, idx, initialSymbols) { + this.reelContainer = reelContainer; + this.idx = idx; + + this.symbolContainer = document.createElement("div"); + this.symbolContainer.classList.add("icons"); + this.reelContainer.appendChild(this.symbolContainer); + + this.animation = this.symbolContainer.animate( + [ + { transform: "none", filter: "blur(0)" }, + { filter: "blur(2px)", offset: 0.5 }, + { + transform: `translateY(-${ + ((Math.floor(this.factor) * 10) / + (3 + Math.floor(this.factor) * 10)) * + 100 + }%)`, + filter: "blur(0)", + }, + ], + { + duration: this.factor * 1000, + easing: "ease-in-out", + } + ); + this.animation.cancel(); + + initialSymbols.forEach((symbol) => + this.symbolContainer.appendChild(new Symbol(symbol).img) + ); + } + + get factor() { + return 1 + Math.pow(this.idx / 2, 2); + } + + renderSymbols(nextSymbols) { + const fragment = document.createDocumentFragment(); + + for (let i = 3; i < 3 + Math.floor(this.factor) * 10; i++) { + const icon = new Symbol( + i >= 10 * Math.floor(this.factor) - 2 + ? nextSymbols[i - Math.floor(this.factor) * 10] + : undefined + ); + fragment.appendChild(icon.img); + } + + this.symbolContainer.appendChild(fragment); + } + + spin() { + const animationPromise = new Promise( + (resolve) => (this.animation.onfinish = resolve) + ); + const timeoutPromise = new Promise((resolve) => + setTimeout(resolve, this.factor * 1000) + ); + + this.animation.play(); + + return Promise.race([animationPromise, timeoutPromise]).then(() => { + if (this.animation.playState !== "finished") this.animation.finish(); + + const max = this.symbolContainer.children.length - 3; + + for (let i = 0; i < max; i++) { + this.symbolContainer.firstChild.remove(); + } + }); + } +} diff --git a/user_interface/src/js/Slot.js b/user_interface/src/js/Slot.js new file mode 100644 index 0000000..034961d --- /dev/null +++ b/user_interface/src/js/Slot.js @@ -0,0 +1,119 @@ +import Reel from "./Reel.js"; +import Symbol from "./Symbol.js"; + + +function getData() { + let symbols = [], won, returnCoef; + + $.ajax({ + type: "GET", + url: "http://127.0.0.1:5000/getState", + async: false, + // data: { param: input }, + success: function(response) { + let arr = response.split(','); + returnCoef = parseFloat(arr.pop()) + won = arr.pop(); + symbols = []; + + for (let i = 0, k = -1; i < arr.length; i++) { + if (i % 3 === 0) { + k++; + symbols[k] = []; + } + + symbols[k].push(arr[i]); + } + } + }); + + return [symbols, won, returnCoef]; +} + + +export default class Slot { + + constructor(domElement, config = {}) { + Symbol.preload(); + + this.won = 0; + this.returnCoef = 0.0; + + this.currentSymbols = [ + ["death_star", "death_star", "death_star"], + ["death_star", "death_star", "death_star"], + ["death_star", "death_star", "death_star"], + ["death_star", "death_star", "death_star"], + // ["death_star", "death_star", "death_star"], + ]; + + this.nextSymbols = [ + ["death_star", "death_star", "death_star"], + ["death_star", "death_star", "death_star"], + ["death_star", "death_star", "death_star"], + ["death_star", "death_star", "death_star"], + // ["death_star", "death_star", "death_star"], + ]; + + this.container = domElement; + + this.reels = Array.from(this.container.getElementsByClassName("reel")).map( + (reelContainer, idx) => + new Reel(reelContainer, idx, this.currentSymbols[idx]) + ); + + this.spinButton = document.getElementById("spin"); + this.spinButton.addEventListener("click", () => this.spin()); + + this.autoPlayCheckbox = document.getElementById("autoplay"); + + if (config.inverted) { + this.container.classList.add("inverted"); + } + + this.config = config; + } + + + spin() { + this.currentSymbols = this.nextSymbols; + + const resp = getData(); + + this.nextSymbols = resp[0]; + this.won = resp[1]; + this.returnCoef = resp[2]; + + this.onSpinStart(this.nextSymbols); + + + + return Promise.all( + this.reels.map((reel) => { + reel.renderSymbols(this.nextSymbols[reel.idx]); + return reel.spin(); + }) + ).then(() => this.onSpinEnd(this.nextSymbols)); + } + + + onSpinStart(symbols) { + this.spinButton.disabled = true; + + this.config.onSpinStart?.(symbols); + } + + + onSpinEnd(symbols) { + this.spinButton.disabled = false; + + this.config.onSpinEnd?.(symbols); + + document.getElementById("win").innerText = "WIN: " + this.won; + document.getElementById("returnCoef").innerText = "Return coefficient: " + this.returnCoef; + + if (this.autoPlayCheckbox.checked) { + return window.setTimeout(() => this.spin(), 200); + } + } +} diff --git a/user_interface/src/js/Symbol.js b/user_interface/src/js/Symbol.js new file mode 100644 index 0000000..7e306ef --- /dev/null +++ b/user_interface/src/js/Symbol.js @@ -0,0 +1,35 @@ +const cache = {}; + +export default class Symbol { + constructor(name = Symbol.random()) { + this.name = name; + + if (cache[name]) { + this.img = cache[name].cloneNode(); + } else { + this.img = new Image(); + this.img.src = require(`../assets/symbols/${name}.png`); + + cache[name] = this.img; + } + } + + static preload() { + Symbol.symbols.forEach((symbol) => new Symbol(symbol)); + } + + static get symbols() { + return [ + "at_at", + "c3po", + "darth_vader", + "death_star", + "falcon", + "r2d2", + ]; + } + + static random() { + return this.symbols[Math.floor(Math.random() * this.symbols.length)]; + } +} diff --git a/user_interface/src/js/index.js b/user_interface/src/js/index.js new file mode 100644 index 0000000..7e9d43e --- /dev/null +++ b/user_interface/src/js/index.js @@ -0,0 +1,13 @@ +import Slot from "./Slot.js"; + +const config = { + inverted: false, // true: reels spin from top to bottom; false: reels spin from bottom to top + onSpinStart: (symbols) => { + console.log("onSpinStart", symbols); + }, + onSpinEnd: (symbols) => { + console.log("onSpinEnd", symbols); + }, +}; + +const slot = new Slot(document.getElementById("slot"), config); diff --git a/user_interface/webpack.config.js b/user_interface/webpack.config.js new file mode 100644 index 0000000..7b55ac7 --- /dev/null +++ b/user_interface/webpack.config.js @@ -0,0 +1,42 @@ +const path = require("path"); + +const { CleanWebpackPlugin } = require("clean-webpack-plugin"); +const HtmlWebpackPlugin = require("html-webpack-plugin"); +const MiniCssExtractPlugin = require("mini-css-extract-plugin"); + +module.exports = { + entry: ["./src/js/index.js", "./src/css/style.css"], + output: { + filename: "bundle.[contenthash].js", + path: path.resolve(__dirname, "dist"), + }, + performance: { + hints: false, + }, + plugins: [ + new CleanWebpackPlugin(), + new HtmlWebpackPlugin({ + template: "./src/index.html", + }), + new MiniCssExtractPlugin({ + filename: "main.[contenthash].css", + }), + ], + module: { + rules: [ + { + test: /\.css$/i, + use: [MiniCssExtractPlugin.loader, "css-loader"], + }, + { + test: /\.(png|jpg|gif|svg)$/, + type: "asset/resource", + }, + { + test: /\.js$/, + exclude: /node_modules/, + use: "babel-loader", + }, + ], + }, +};