From 5ff176e1e2ef22070a2e1e2201589490d4229067 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zo=C3=A9=20Cassiop=C3=A9e=20Gauthier?= Date: Wed, 3 Apr 2024 20:52:47 -0400 Subject: [PATCH] Begin making the Big Hack look like real software --- pyproject.toml | 46 +++++++++++++++ src/pogo_scaled_estimators/calculator.py | 71 +++++++++++++----------- src/pogo_scaled_estimators/cli.py | 8 +-- src/pogo_scaled_estimators/utilities.py | 19 +++++++ 4 files changed, 108 insertions(+), 36 deletions(-) create mode 100644 src/pogo_scaled_estimators/utilities.py diff --git a/pyproject.toml b/pyproject.toml index e9dbb36..822bdf4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,6 +7,7 @@ name = "pogo-scaled-estimators" version = "1.0a1" dependencies = [ "requests", + "rich", ] requires-python = ">=3.12" authors = [ @@ -99,3 +100,48 @@ ignore = [ "ISC001", # single-line-implicit-string-concatenation "ISC002", # multi-line-implicit-string-concatenation ] + +[tool.pyright] +include = ["src/glimmer", "tests"] +exclude = ["**/__pycache__"] +reportMissingImports = true +reportMissingTypeStubs = false +pythonVersion = "3.12" +pythonPlatform = "Linux" +typeCheckingMode = "standard" +strictListInference = true +strictDictionaryInference = true +strictSetInference = true +reportAssertAlwaysTrue = "error" +reportInvalidStringEscapeSequence = "error" +reportSelfClsParameterName = "error" +reportConstantRedefinition = "error" +reportDeprecated = "error" +reportDuplicateImport = "error" +reportIncompatibleMethodOverride = "error" +reportIncompatibleVariableOverride = "error" +reportInconsistentConstructor = "error" +reportMatchNotExhaustive = "warning" +reportOverlappingOverload = "error" +reportMissingSuperCall = "error" +reportPrivateUsage = "warning" +reportTypeCommentUsage = "error" +reportUnnecessaryCast = "error" +reportUnnecessaryComparison = "error" +reportUnnecessaryContains = "error" +reportUnnecessaryIsInstance = "error" +reportUnusedClass = "warning" +reportUnusedImport = "warning" +reportUnusedFunction = "warning" +reportUnusedVariable = "warning" +reportUntypedBaseClass = "error" +reportUntypedClassDecorator = "error" +reportUntypedFunctionDecorator = "error" +reportUntypedNamedTuple = "error" +reportCallInDefaultInitializer = "error" +reportImplicitOverride = "error" +reportPropertyTypeMismatch = "warning" +reportShadowedImports = "warning" +reportUninitializedInstanceVariable = "warning" +reportUnnecessaryTypeIgnoreComment = "warning" +reportUnusedCallResult = "warning" diff --git a/src/pogo_scaled_estimators/calculator.py b/src/pogo_scaled_estimators/calculator.py index 14d6293..1438692 100644 --- a/src/pogo_scaled_estimators/calculator.py +++ b/src/pogo_scaled_estimators/calculator.py @@ -12,20 +12,25 @@ import sqlite3 import time import urllib.parse from pathlib import Path +from typing import final import requests +from rich.progress import Progress, TaskID + +from pogo_scaled_estimators.utilities import format_move_name, format_pokemon_name WEAKNESS = 1.6 DOUBLE_WEAKNESS = WEAKNESS * WEAKNESS +@final class Calculator: - def __init__(self, attacker_types): + def __init__(self, attacker_types: list[str]) -> None: self.db = sqlite3.connect("ase.db") # db.set_trace_callback(print) with contextlib.suppress(sqlite3.OperationalError): - self.db.execute( + _ = self.db.execute( "CREATE TABLE estimators(defender, raid_tier, attacker, level, quick_move, charged_move, estimator, party)" ) @@ -36,17 +41,20 @@ class Calculator: self.attacker_types = attacker_types - def _pokebattler_resource(self, name): + self.progress = Progress() + self._refresh_task: TaskID | None = None + + def _pokebattler_resource(self, name: str) -> dict: p = Path(f"./{name}.json") if not p.exists(): response = requests.get(f"https://fight.pokebattler.com/{name}") with p.open(mode="wb") as fp: - fp.write(response.content) + _ = fp.write(response.content) with p.open() as fp: return json.load(fp) - def calculate(self, level=40, party=1): + def calculate(self, level: int = 40, party: int = 1) -> None: raid_bosses = self._raid_bosses() charged_moves = [ move["moveId"] for move in self.moves["move"] if "type" in move and move["type"] in self.attacker_types @@ -80,7 +88,7 @@ class Calculator: """, (party, party, level), ) - for raid_tier, _defender, attacker, estimator, fast_move, charged_move in res.fetchall(): + for raid_tier, _, attacker, estimator, fast_move, charged_move in res.fetchall(): if raid_tier == "RAID_LEVEL_MEGA_5": simplified_raid_tier = "RAID_LEVEL_MEGA" elif raid_tier == "RAID_LEVEL_ULTRA_BEAST": @@ -100,9 +108,9 @@ class Calculator: + 0.35 * sum(estimators["RAID_LEVEL_MEGA"]) / len(estimators["RAID_LEVEL_MEGA"]) ) fast_move, charged_move = movesets[attacker] - print(f"{attacker},{ase},{fast_move},{charged_move}") + print(f"{attacker},{level},{ase},{fast_move},{charged_move}") - def _raid_bosses(self): + def _raid_bosses(self) -> dict: raid_tiers = [] raid_bosses = {} @@ -140,7 +148,7 @@ class Calculator: return raid_bosses - def _is_weak(self, attacker_type, defender_types): + def _is_weak(self, attacker_type: str, defender_types: tuple[str, str]) -> bool: pokemon_types = list(self.resists.keys()) defender_type_indices = ( pokemon_types.index(defender_types[0]), @@ -164,7 +172,7 @@ class Calculator: return False - def load_estimators(self, tier, defender, level, party): + def load_estimators(self, tier: str, defender: str, level: int, party: int) -> None: base_url = "https://fight.pokebattler.com" query_string = { "sort": "ESTIMATOR", @@ -196,16 +204,12 @@ class Calculator: ) estimator = res.fetchone() if estimator is not None: - print( - defender, - attacker["pokemonId"], - attacker_moves["move1"], - attacker_moves["move2"], - estimator[0], + self.progress.console.log( + f'{format_pokemon_name(attacker["pokemonId"])} ({format_move_name(attacker_moves["move1"])}/{format_move_name(attacker_moves["move2"])}): {estimator[0]:.2f} (cached)' ) continue - self.db.execute( + _ = self.db.execute( "INSERT INTO estimators(defender, raid_tier, attacker, level, quick_move, charged_move, estimator, party) VALUES(?, ?, ?, ?, ?, ?, ?, ?)", ( defender, @@ -218,21 +222,24 @@ class Calculator: party, ), ) - print( - defender, - attacker["pokemonId"], - attacker_moves["move1"], - attacker_moves["move2"], - attacker_moves["result"]["estimator"], + self.progress.console.log( + f'{format_pokemon_name(attacker["pokemonId"])} ({format_move_name(attacker_moves["move1"])}/{format_move_name(attacker_moves["move2"])}): {attacker_moves["result"]["estimator"]:.2f}' ) + + if self._refresh_task is not None: + self.progress.update(self._refresh_task, advance=1) self.db.commit() - def refresh(self, level=40, party=1): - for tier, defenders in self._raid_bosses().items(): - for defender in defenders: - try: - self.load_estimators(tier, defender, level, party) - except json.decoder.JSONDecodeError: - time.sleep(30) - self.load_estimators(tier, defender, level, party) - time.sleep(30) + def refresh(self, level: int = 40, party: int = 1) -> None: + with self.progress: + total_defenders = sum(len(defenders) for defenders in self._raid_bosses().values()) + self._refresh_task = self.progress.add_task("Working...", total=(total_defenders * 30)) + for tier, defenders in self._raid_bosses().items(): + for defender in defenders: + self.progress.update(self._refresh_task, description=f"vs {format_pokemon_name(defender)}...") + try: + self.load_estimators(tier, defender, level, party) + except json.decoder.JSONDecodeError: + time.sleep(30) + self.load_estimators(tier, defender, level, party) + time.sleep(1) diff --git a/src/pogo_scaled_estimators/cli.py b/src/pogo_scaled_estimators/cli.py index 35b1f28..309e7ad 100644 --- a/src/pogo_scaled_estimators/cli.py +++ b/src/pogo_scaled_estimators/cli.py @@ -12,10 +12,10 @@ import pogo_scaled_estimators.calculator def main_cli(): parser = argparse.ArgumentParser() - parser.add_argument("type", nargs="+", help="an attacker type") - parser.add_argument("--level", type=int, default=40) - parser.add_argument("--party", type=int, default=1) - parser.add_argument("--refresh", action="store_true", default=False) + _ = parser.add_argument("type", nargs="+", help="an attacker type") + _ = parser.add_argument("--level", type=int, default=40) + _ = parser.add_argument("--party", type=int, default=1) + _ = parser.add_argument("--refresh", action="store_true", default=False) args = parser.parse_args() diff --git a/src/pogo_scaled_estimators/utilities.py b/src/pogo_scaled_estimators/utilities.py new file mode 100644 index 0000000..e3d4add --- /dev/null +++ b/src/pogo_scaled_estimators/utilities.py @@ -0,0 +1,19 @@ +MINIMUM_SPECIAL_NAME_PARTS = 2 + + +def format_pokemon_name(name): + parts = [part.capitalize() for part in name.split("_")] + if parts[-1] == "Mega" or parts[-1] == "Primal": + parts = [parts[-1]] + parts[:-1] + if len(parts) > MINIMUM_SPECIAL_NAME_PARTS and parts[-2] == "Mega": + parts = [parts[-2]] + parts[:-2] + [parts[-1]] + if len(parts) > MINIMUM_SPECIAL_NAME_PARTS and parts[-2] == "Shadow": + parts = [parts[-2]] + parts[:-2] + return " ".join(parts) + + +def format_move_name(name): + parts = [part.capitalize() for part in name.split("_")] + if parts[-1] == "Fast": + parts = parts[:-1] + return " ".join(parts)