Skip to content

Commit

Permalink
Merge pull request #35 from alan-turing-institute/create-ci
Browse files Browse the repository at this point in the history
Start working on a proper test of the functionality
  • Loading branch information
phinate authored Jun 19, 2023
2 parents 2268101 + ed8b07e commit f36ab10
Show file tree
Hide file tree
Showing 6 changed files with 122 additions and 76 deletions.
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}")

0 comments on commit f36ab10

Please sign in to comment.