Begin making the Big Hack look like real software

This commit is contained in:
Zoé Cassiopée Gauthier 2024-04-03 20:52:47 -04:00
parent 0e3011c406
commit 5ff176e1e2
4 changed files with 108 additions and 36 deletions

View File

@ -7,6 +7,7 @@ name = "pogo-scaled-estimators"
version = "1.0a1" version = "1.0a1"
dependencies = [ dependencies = [
"requests", "requests",
"rich",
] ]
requires-python = ">=3.12" requires-python = ">=3.12"
authors = [ authors = [
@ -99,3 +100,48 @@ ignore = [
"ISC001", # single-line-implicit-string-concatenation "ISC001", # single-line-implicit-string-concatenation
"ISC002", # multi-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"

View File

@ -12,20 +12,25 @@ import sqlite3
import time import time
import urllib.parse import urllib.parse
from pathlib import Path from pathlib import Path
from typing import final
import requests import requests
from rich.progress import Progress, TaskID
from pogo_scaled_estimators.utilities import format_move_name, format_pokemon_name
WEAKNESS = 1.6 WEAKNESS = 1.6
DOUBLE_WEAKNESS = WEAKNESS * WEAKNESS DOUBLE_WEAKNESS = WEAKNESS * WEAKNESS
@final
class Calculator: class Calculator:
def __init__(self, attacker_types): def __init__(self, attacker_types: list[str]) -> None:
self.db = sqlite3.connect("ase.db") self.db = sqlite3.connect("ase.db")
# db.set_trace_callback(print) # db.set_trace_callback(print)
with contextlib.suppress(sqlite3.OperationalError): 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)" "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 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") p = Path(f"./{name}.json")
if not p.exists(): if not p.exists():
response = requests.get(f"https://fight.pokebattler.com/{name}") response = requests.get(f"https://fight.pokebattler.com/{name}")
with p.open(mode="wb") as fp: with p.open(mode="wb") as fp:
fp.write(response.content) _ = fp.write(response.content)
with p.open() as fp: with p.open() as fp:
return json.load(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() raid_bosses = self._raid_bosses()
charged_moves = [ charged_moves = [
move["moveId"] for move in self.moves["move"] if "type" in move and move["type"] in self.attacker_types 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), (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": if raid_tier == "RAID_LEVEL_MEGA_5":
simplified_raid_tier = "RAID_LEVEL_MEGA" simplified_raid_tier = "RAID_LEVEL_MEGA"
elif raid_tier == "RAID_LEVEL_ULTRA_BEAST": 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"]) + 0.35 * sum(estimators["RAID_LEVEL_MEGA"]) / len(estimators["RAID_LEVEL_MEGA"])
) )
fast_move, charged_move = movesets[attacker] 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_tiers = []
raid_bosses = {} raid_bosses = {}
@ -140,7 +148,7 @@ class Calculator:
return raid_bosses 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()) pokemon_types = list(self.resists.keys())
defender_type_indices = ( defender_type_indices = (
pokemon_types.index(defender_types[0]), pokemon_types.index(defender_types[0]),
@ -164,7 +172,7 @@ class Calculator:
return False 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" base_url = "https://fight.pokebattler.com"
query_string = { query_string = {
"sort": "ESTIMATOR", "sort": "ESTIMATOR",
@ -196,16 +204,12 @@ class Calculator:
) )
estimator = res.fetchone() estimator = res.fetchone()
if estimator is not None: if estimator is not None:
print( self.progress.console.log(
defender, f'{format_pokemon_name(attacker["pokemonId"])} ({format_move_name(attacker_moves["move1"])}/{format_move_name(attacker_moves["move2"])}): {estimator[0]:.2f} (cached)'
attacker["pokemonId"],
attacker_moves["move1"],
attacker_moves["move2"],
estimator[0],
) )
continue continue
self.db.execute( _ = self.db.execute(
"INSERT INTO estimators(defender, raid_tier, attacker, level, quick_move, charged_move, estimator, party) VALUES(?, ?, ?, ?, ?, ?, ?, ?)", "INSERT INTO estimators(defender, raid_tier, attacker, level, quick_move, charged_move, estimator, party) VALUES(?, ?, ?, ?, ?, ?, ?, ?)",
( (
defender, defender,
@ -218,21 +222,24 @@ class Calculator:
party, party,
), ),
) )
print( self.progress.console.log(
defender, f'{format_pokemon_name(attacker["pokemonId"])} ({format_move_name(attacker_moves["move1"])}/{format_move_name(attacker_moves["move2"])}): {attacker_moves["result"]["estimator"]:.2f}'
attacker["pokemonId"],
attacker_moves["move1"],
attacker_moves["move2"],
attacker_moves["result"]["estimator"],
) )
if self._refresh_task is not None:
self.progress.update(self._refresh_task, advance=1)
self.db.commit() self.db.commit()
def refresh(self, level=40, party=1): 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 tier, defenders in self._raid_bosses().items():
for defender in defenders: for defender in defenders:
self.progress.update(self._refresh_task, description=f"vs {format_pokemon_name(defender)}...")
try: try:
self.load_estimators(tier, defender, level, party) self.load_estimators(tier, defender, level, party)
except json.decoder.JSONDecodeError: except json.decoder.JSONDecodeError:
time.sleep(30) time.sleep(30)
self.load_estimators(tier, defender, level, party) self.load_estimators(tier, defender, level, party)
time.sleep(30) time.sleep(1)

View File

@ -12,10 +12,10 @@ import pogo_scaled_estimators.calculator
def main_cli(): def main_cli():
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument("type", nargs="+", help="an attacker type") _ = parser.add_argument("type", nargs="+", help="an attacker type")
parser.add_argument("--level", type=int, default=40) _ = parser.add_argument("--level", type=int, default=40)
parser.add_argument("--party", type=int, default=1) _ = parser.add_argument("--party", type=int, default=1)
parser.add_argument("--refresh", action="store_true", default=False) _ = parser.add_argument("--refresh", action="store_true", default=False)
args = parser.parse_args() args = parser.parse_args()

View File

@ -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)