diff --git a/ examples/poke_1v1_fighter.py b/ examples/poke_1v1_fighter.py index e9a6428..6488023 100644 --- a/ examples/poke_1v1_fighter.py +++ b/ examples/poke_1v1_fighter.py @@ -1,9 +1,9 @@ # 1v1 fights for the pokemon for stats. # Author: Edmund Dable-Heath """ - Setting each of the 150 pokemon to fight each other pokemon until either a cutoff is - reached or the variation of the win ratio over a history of s fights falls below a - threshold. +Setting each of the 150 pokemon to fight each other pokemon until either a cutoff is +reached or the variation of the win ratio over a history of s fights falls below a +threshold. """ # Imports include reference to potential scripts diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d56be97..ccbab45 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -3,13 +3,13 @@ ci: autofix_commit_msg: "style: pre-commit fixes" repos: - - repo: https://github.com/psf/black - rev: "23.3.0" + - repo: https://github.com/psf/black-pre-commit-mirror + rev: "26.3.1" hooks: - id: black-jupyter - repo: https://github.com/pre-commit/pre-commit-hooks - rev: "v4.4.0" + rev: "v6.0.0" hooks: - id: check-added-large-files - id: check-case-conflict @@ -32,25 +32,25 @@ repos: - id: rst-inline-touching-normal - repo: https://github.com/pre-commit/mirrors-prettier - rev: "v2.7.1" + rev: "v4.0.0-alpha.8" hooks: - id: prettier types_or: [yaml, markdown, html, css, scss, javascript, json] args: [--prose-wrap=always] - repo: https://github.com/asottile/blacken-docs - rev: "1.13.0" + rev: "1.20.0" hooks: - id: blacken-docs additional_dependencies: [black==23.3.0] - - repo: https://github.com/charliermarsh/ruff-pre-commit - rev: "v0.0.264" + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: "v0.15.12" hooks: - id: ruff args: ["--fix", "--show-fixes"] - repo: https://github.com/shellcheck-py/shellcheck-py - rev: "v0.9.0.2" + rev: "v0.11.0.1" hooks: - id: shellcheck diff --git a/src/p2lab/__init__.py b/src/p2lab/__init__.py index 1bd7d76..e1ff5b5 100644 --- a/src/p2lab/__init__.py +++ b/src/p2lab/__init__.py @@ -4,9 +4,8 @@ p2lab: a package for genetic optimisation of pokemon teams """ - from __future__ import annotations __version__ = "0.1.0" -__all__ = ("__version__", "pokemon", "genetic") +__all__ = ("__version__", "genetic", "pokemon") diff --git a/src/p2lab/_compat/__init__.py b/src/p2lab/_compat/__init__.py index 1bb08df..dad6d7f 100644 --- a/src/p2lab/_compat/__init__.py +++ b/src/p2lab/_compat/__init__.py @@ -4,5 +4,4 @@ p2lab: a package for genetic optimisation of pokemon teams """ - from __future__ import annotations diff --git a/src/p2lab/_compat/typing.py b/src/p2lab/_compat/typing.py index d73b109..365da72 100644 --- a/src/p2lab/_compat/typing.py +++ b/src/p2lab/_compat/typing.py @@ -4,12 +4,11 @@ p2lab: a package for genetic optimisation of pokemon teams """ - from __future__ import annotations from typing import Literal, Protocol, runtime_checkable -__all__ = ["Protocol", "runtime_checkable", "Literal"] +__all__ = ["Literal", "Protocol", "runtime_checkable"] def __dir__() -> list[str]: diff --git a/src/p2lab/genetic/__init__.py b/src/p2lab/genetic/__init__.py index f7a0620..8388e4f 100644 --- a/src/p2lab/genetic/__init__.py +++ b/src/p2lab/genetic/__init__.py @@ -1,8 +1,8 @@ from __future__ import annotations __all__ = ( - "genetic_algorithm", "fitness", + "genetic_algorithm", "matching", "operations", ) diff --git a/src/p2lab/genetic/utils.py b/src/p2lab/genetic/utils.py index fe684da..71656c3 100644 --- a/src/p2lab/genetic/utils.py +++ b/src/p2lab/genetic/utils.py @@ -61,7 +61,7 @@ def crossover( team2_pokemon = team2_old.pokemon # Randomly pick locus - locus = random.sample(range(0, num_pokemon), k=1)[0] + locus = random.sample(range(num_pokemon), k=1)[0] # Check that the sliciing here works but you get the idea team1_new_pokemeon = team1_pokemon[0:locus] + team2_pokemon[locus:num_pokemon] diff --git a/src/p2lab/pokemon/__init__.py b/src/p2lab/pokemon/__init__.py index 5845c93..5e80e19 100644 --- a/src/p2lab/pokemon/__init__.py +++ b/src/p2lab/pokemon/__init__.py @@ -2,6 +2,6 @@ __all__ = ( "battles", - "teams", "premade", + "teams", ) diff --git a/src/p2lab/pokemon/pokefactory.py b/src/p2lab/pokemon/pokefactory.py index 9837e49..7994cc1 100644 --- a/src/p2lab/pokemon/pokefactory.py +++ b/src/p2lab/pokemon/pokefactory.py @@ -4,6 +4,7 @@ This is directly inspired by poke-env's diagostic_tools folder """ + from __future__ import annotations import numpy as np @@ -68,7 +69,7 @@ def make_pokemon(self, dexnum=None, generate_moveset=False, **kwargs): raise ValueError(msg) if dexnum is None: dexnum = np.random.choice(list(self.dex2mon.keys())) - if generate_moveset or "moves" not in kwargs.keys(): + if generate_moveset or "moves" not in kwargs: poss_moves = self.get_allowed_moves(dexnum) moves = ( np.random.choice(poss_moves, 4, replace=False) @@ -76,16 +77,16 @@ def make_pokemon(self, dexnum=None, generate_moveset=False, **kwargs): else poss_moves ) kwargs["moves"] = moves - if "ivs" not in kwargs.keys(): + if "ivs" not in kwargs: ivs = [31] * 6 kwargs["ivs"] = ivs - if "evs" not in kwargs.keys(): + if "evs" not in kwargs: # TODO: implement EV generation better evs = [510 // 6] * 6 kwargs["evs"] = evs - if "level" not in kwargs.keys(): + if "level" not in kwargs: kwargs["level"] = 100 - if "ability" not in kwargs.keys(): + if "ability" not in kwargs: kwargs["ability"] = np.random.choice( list(self.get_allowed_abilities(dexnum).values()) ) diff --git a/target_api.md b/target_api.md index 2884acd..1563888 100644 --- a/target_api.md +++ b/target_api.md @@ -7,8 +7,7 @@ running genetic algorithms to optimize teams. Thank you to github copilot for helping me write this document lmao ```python3 - -team = generate_team(format='gen8randombattle', seed=1234) +team = generate_team(format="gen8randombattle", seed=1234) # a team object contains a list of 6 pokemon objects with their moves, items, etc. # it's also tracking the (normalised) number of wins/losses/ties for this team. @@ -30,12 +29,12 @@ team = generate_team(format='gen8randombattle', seed=1234) # here's an idealised Bot class that uses these same env variables to run a showdown bot, but more cleanly: bot_1 = Bot( team, - strategy='safest', - websocket_uri='sim.psim.us:8000', - user='whimsicaldreams', - password='password', - bot_mode='CHALLENGE_USER', - format='gen3ou', + strategy="safest", + websocket_uri="sim.psim.us:8000", + user="whimsicaldreams", + password="password", + bot_mode="CHALLENGE_USER", + format="gen3ou", num_battles=1, save_replay=False, ) @@ -47,18 +46,19 @@ bot_1 = Bot( # now we can run a battle between two bots: bot_2 = Bot( team, - strategy='safest', - websocket_uri='sim.psim.us:8000', - user='melonchomper', - password='password', - bot_mode='CHALLENGE_USER', # this would need to be ACCEPT_CHALLENGE - format='gen3ou', + strategy="safest", + websocket_uri="sim.psim.us:8000", + user="melonchomper", + password="password", + bot_mode="CHALLENGE_USER", # this would need to be ACCEPT_CHALLENGE + format="gen3ou", num_battles=1, save_replay=False, ) # from a little google searching, it looks like we can run these bots in parallel using asyncio: + async def run_battle(bot_1, bot_2): # individually run the showdown bots at the same time, and wait for the results await asyncio.gather(bot_1.challenge(bot_2), bot_2.accept(bot_1)) @@ -66,6 +66,7 @@ async def run_battle(bot_1, bot_2): # update the win/loss/tie counts for each team ... + run_battle(bot_1, bot_2) # within this function, we'll need to: @@ -74,7 +75,20 @@ run_battle(bot_1, bot_2) # - update the win/loss/tie counts for each team # here's a more generic example, where we specify a number of battles for each team, and then run them all: -bots = {f'bot{i}':Bot(team, strategy='safest', websocket_uri='sim.psim.us:8000', user=f'bot{i}', password='password', bot_mode='CHALLENGE_USER' if i % 2 == 0 else 'ACCEPT_CHALLENGE', format='gen3ou', num_battles=10, save_replay=False) for i in range(10)} +bots = { + f"bot{i}": Bot( + team, + strategy="safest", + websocket_uri="sim.psim.us:8000", + user=f"bot{i}", + password="password", + bot_mode="CHALLENGE_USER" if i % 2 == 0 else "ACCEPT_CHALLENGE", + format="gen3ou", + num_battles=10, + save_replay=False, + ) + for i in range(10) +} # num_battles = 10 would mean that bot would challenge the same bot 10 times, so we can make every bot fight every other bot 10 times @@ -85,14 +99,18 @@ for bot_1, bot_2 in itertools.combinations(bots.values(), 2): if bot_1 != bot_2: try: # make sure bot_1 is the challenger and bot_2 is the acceptor - bot_1.bot_mode = 'CHALLENGE_USER' - bot_2.bot_mode = 'ACCEPT_CHALLENGE' + bot_1.bot_mode = "CHALLENGE_USER" + bot_2.bot_mode = "ACCEPT_CHALLENGE" # check if either bot is currently in a battle if bot_1.in_battle or bot_2.in_battle: - print(f'Bot {bot_1} or {bot_2} is currently in a battle, waiting for them to finish...') + print( + f"Bot {bot_1} or {bot_2} is currently in a battle, waiting for them to finish..." + ) while bot_1.in_battle or bot_2.in_battle: - await asyncio.sleep(1) # hopefully this will wait for the battle to finish + await asyncio.sleep( + 1 + ) # hopefully this will wait for the battle to finish # run the battle bot_1.in_battle = True @@ -105,7 +123,7 @@ for bot_1, bot_2 in itertools.combinations(bots.values(), 2): except Exception as e: print(e) - print(f'Error running battle between {bot_1} and {bot_2}') + print(f"Error running battle between {bot_1} and {bot_2}") # ensure we're only here once every bot has fought every other bot while any(bot.in_battle for bot in bots.values()): diff --git a/tests/conftest.py b/tests/conftest.py index 73a9cc6..109f8a7 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -5,11 +5,11 @@ from p2lab.pokemon import pokefactory, premade, teams -@pytest.fixture() +@pytest.fixture def default_factory(): return pokefactory.PokeFactory() -@pytest.fixture() +@pytest.fixture def gen_1_pool(): return teams.import_pool(premade.gen_1_pokemon()) diff --git a/tests/test_package.py b/tests/test_package.py index 8d53dac..ab8fe3a 100644 --- a/tests/test_package.py +++ b/tests/test_package.py @@ -10,8 +10,7 @@ def test_version(): @runtime_checkable class HasQuack(Protocol): - def quack(self) -> str: - ... + def quack(self) -> str: ... class Duck: diff --git a/timings.py b/timings.py index 5ef1a47..03ff5ed 100644 --- a/timings.py +++ b/timings.py @@ -5,11 +5,11 @@ from itertools import permutations import numpy as np +from p2lab.team import Team from p2lab.genetic.fitness import BTmodel, win_percentages from p2lab.genetic.matching import dense from p2lab.genetic.operations import build_crossover_fn, mutate, slot_swap -from p2lab.team import Team # Constants N_TEAM = 3