Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Start working on a proper test of the functionality #35

Merged
merged 5 commits into from
Jun 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,13 @@ jobs:
checks:
name: Check Python ${{ matrix.python-version }} on ${{ matrix.runs-on }}
runs-on: ${{ matrix.runs-on }}
timeout-minutes: 15
needs: [pre-commit]
strategy:
fail-fast: false
matrix:
python-version: ["3.8", "3.11"]
runs-on: [ubuntu-latest, macos-latest]
runs-on: [ubuntu-latest]

steps:
- uses: actions/checkout@v3
Expand Down
13 changes: 8 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,25 @@ steps:
- clone the repo
- install the module in a virtual environment with `pip install -e .`
- initialise submodules with `git submodule update --init --recursive`
- if you're having problems, you can directly clone poke-env with
`git clone https://github.com/aoifehughes/poke-env.git`
- cd into `poke-env` and run `pip install -e .`
- cd back to the root directory and clone the `pokemon-showdown` repo with
`git clone https://github.com/smogon/pokemon-showdown`
- install with `npm install pokemon-showdown` in the `pokemon-showdown`
directory
- if you don't have node, install it with `brew install node` (mac) or
`sudo apt install nodejs` (linux)
- install with `npm install` in the `pokemon-showdown` directory
- start a local server with `node pokemon-showdown start --no-security` in the
`pokemon-showdown` directory
- cd back to the root directory and run `python run.py` to run the demo!
- to run a small demo, you should be able to just run `p2lab` from the command
line!

or via Docker:

`docker build -t p2:latest .` `docker run -it p2:latest`
`docker exec -it your_container_id /bin/bash`

then go to the P2 root and run the script. Run docker build with `--no-cache` to
rebuild with newer versions of the repos.
Run docker build with `--no-cache` to rebuild with newer versions of the repos.

## Components

Expand Down
9 changes: 3 additions & 6 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ name = "p2lab"
authors = [
{ name = "Scientists of the P2 Laboratory", email = "[email protected]" },
]
description = "a secret package for genetic optimisation of pokemon teams"
description = "a package for genetic optimisation of pokemon teams"
readme = "README.md"
requires-python = ">=3.8"
classifiers = [
Expand Down Expand Up @@ -37,14 +37,10 @@ dependencies = [
]

[project.optional-dependencies]

test = [
"pytest >=6",
"pytest-cov >=3",
]
dev = [
"pytest >=6",
"pytest-cov >=3",
"pytest-asyncio",
]
docs = [
"sphinx>=4.0",
Expand All @@ -64,6 +60,7 @@ version.path = "src/p2lab/__init__.py"
envs.default.dependencies = [
"pytest",
"pytest-cov",
"pytest-asyncio",
]


Expand Down
94 changes: 48 additions & 46 deletions src/p2lab/genetic/genetic.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,54 +115,56 @@ async def genetic_algorithm(

# Genetic Loop
for i in range(num_generations):
# Step 1: selection
# An odd number of teams is an edge case for crossover, as it uses 2 teams
# at a time. This adds an extra team to selection if we are using crossover.
# the extra team will be removed by the crossover function.
extra = num_teams % 2 - mutate_with_fitness

# Returns new fitnesses in case we are doing fitness-mutate
new_teams, new_fitness = selection(
teams=teams,
fitness=fitness,
num_teams=num_teams + extra,
)

# Step 2: crossover
# Only do this step if not mutating with fitness, as fitness scores become
# invalid after crossover if doing so
if not mutate_with_fitness:
new_teams = crossover_fn(
teams=new_teams,
num_teams=num_teams,
num_pokemon=team_size,
crossover_prob=crossover_prob,
allow_all=allow_all,
# account for team size of 1
if team_size != 1:
# Step 1: selection
# An odd number of teams is an edge case for crossover, as it uses 2 teams
# at a time. This adds an extra team to selection if we are using crossover.
# the extra team will be removed by the crossover function.
extra = num_teams % 2 - mutate_with_fitness

# Returns new fitnesses in case we are doing fitness-mutate
new_teams, new_fitness = selection(
teams=teams,
fitness=fitness,
num_teams=num_teams + extra,
)

# Step 3: mutate
# If mutating with fitness, skip the crossover step. Otherwise, crossover +
# mutate.
if mutate_with_fitness:
teams = fitness_mutate(
teams=new_teams,
num_pokemon=team_size,
fitness=new_fitness,
pokemon_population=pokemon_pool,
allow_all=allow_all,
k=mutate_k,
)

else:
# Mutate the new teams
teams = mutate(
teams=new_teams,
num_pokemon=team_size,
mutate_prob=mutate_prob,
pokemon_population=pokemon_pool,
allow_all=allow_all,
k=mutate_k,
)
# Step 2: crossover
# Only do this step if not mutating with fitness, as fitness scores become
# invalid after crossover if doing so
if not mutate_with_fitness:
new_teams = crossover_fn(
teams=new_teams,
num_teams=num_teams,
num_pokemon=team_size,
crossover_prob=crossover_prob,
allow_all=allow_all,
)

# Step 3: mutate
# If mutating with fitness, skip the crossover step. Otherwise, crossover +
# mutate.
if mutate_with_fitness:
teams = fitness_mutate(
teams=new_teams,
num_pokemon=team_size,
fitness=new_fitness,
pokemon_population=pokemon_pool,
allow_all=allow_all,
k=mutate_k,
)

else:
# Mutate the new teams
teams = mutate(
teams=new_teams,
num_pokemon=team_size,
mutate_prob=mutate_prob,
pokemon_population=pokemon_pool,
allow_all=allow_all,
k=mutate_k,
)

# Generate matches from list of teams
matches = match_fn(teams)
Expand Down
18 changes: 0 additions & 18 deletions tests/test_matching.py

This file was deleted.

61 changes: 61 additions & 0 deletions tests/test_workflow.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
from __future__ import annotations

import numpy as np
import pytest

from p2lab.genetic.genetic import genetic_algorithm
from p2lab.genetic.operations import (
build_crossover_fn,
)
from p2lab.pokemon.premade import gen_1_pokemon
from p2lab.pokemon.teams import generate_teams, import_pool


@pytest.mark.asyncio()
@pytest.mark.parametrize(
("team_size", "crossover_fn"),
[
(1, None),
# (2, None),
# (6, locus_swap),
# (6, slot_swap),
# (6, sample_swap),
],
)
async def test_main_loop(team_size, crossover_fn):
num_teams = 10
# generate the pool
pool = import_pool(gen_1_pokemon())
seed_teams = generate_teams(pool, num_teams, team_size, unique=True)
crossover_fn = (
build_crossover_fn(crossover_fn) if crossover_fn is not None else None
)
# run the genetic algorithm
teams, fitnesses = await genetic_algorithm(
pokemon_pool=pool,
seed_teams=seed_teams,
num_teams=num_teams,
team_size=team_size,
num_generations=3,
progress_bars=True,
crossover_fn=crossover_fn,
mutate_with_fitness=crossover_fn is None,
mutate_k=1,
)

print("Best team:")
best_team = teams[np.argmax(fitnesses)]
fitness = fitnesses[np.argmax(fitnesses)]
for mon in best_team.pokemon:
print(mon.formatted)

print(f"Fitness: {fitness}")

print("Worst team:")

worst_team = teams[np.argmin(fitnesses)]
fitness = fitnesses[np.argmin(fitnesses)]
for mon in worst_team.pokemon:
print(mon.formatted)

print(f"Fitness: {fitness}")