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"
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"

View File

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

View File

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

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)