Creating a custom teambuilder
The corresponding complete source code can be found here.
Best for: learning how to supply custom teams and teambuilders.
Prerequisites:
pip install poke-enva local Pokémon Showdown server running with
--no-security
If setup does not work as expected, revisit the getting started guide and confirm your local server is running.
In quickstart, we chose a team by passing a str containing the team we want to use as a showdown format team.
However, we might want to use different teams in different battles with the same agent, or use more complex mechanisms to generate and select teams. Teambuilder objects are meant for specifying teams in such a custom fashion.
This example demonstrates how to build two custom Teambuilders. First, we will build a Teambuilder for which we specify a pool of teams, and each game will be played using a team randomly selected from the pool.
In the second one, we will have a set of pokemons and randomly generate teams from this set.
Creating a custom Teambuilder
Teambuilder objects need to implement one method, yield_team, which will be called before each battle starts to define the team to use. This method must return a showdown packed-formatted string.
In this example, we will use a built-in helper functions to simplify this process.
Our custom Teambuilder will be initialized with a list of showdown formatted teams, and will use one of these team randomly for each battle.
To convert showdown formatted teams to the packed-formatted string, we’ll proceed in two steps:
Convert showdown formatted teams to lists of
TeambuilderPokemonobjects. These objects are used internally bypoke-envto describe pokemons used in a team in a flexible way. You can read more about them in teambuilder. This can be accomplished withTeambuilder’sparse_showdown_teammethod.Convert this list of
TeambuilderPokemonobjects into the required formatted string. This can be achieved withTeambuilder’sjoin_teammethod.
All in all, we get the following Teambuilder:
[1]:
import numpy as np
from poke_env.teambuilder import Teambuilder
class RandomTeamFromPool(Teambuilder):
def __init__(self, teams):
self.packed_teams = []
for team in teams:
parsed_team = self.parse_showdown_team(team)
packed_team = self.join_team(parsed_team)
self.packed_teams.append(packed_team)
def yield_team(self):
return np.random.choice(self.packed_teams)
We can instantiate this Teambuilder with a list of showdown formatted teams:
[2]:
team_1 = """
Goodra (M) @ Assault Vest
Ability: Sap Sipper
EVs: 248 HP / 252 SpA / 8 Spe
Modest Nature
IVs: 0 Atk
- Dragon Pulse
- Flamethrower
- Sludge Wave
- Thunderbolt
Sylveon (M) @ Leftovers
Ability: Pixilate
EVs: 248 HP / 244 Def / 16 SpD
Calm Nature
IVs: 0 Atk
- Hyper Voice
- Mystical Fire
- Protect
- Wish
Cinderace (M) @ Life Orb
Ability: Blaze
EVs: 252 Atk / 4 SpD / 252 Spe
Jolly Nature
- Pyro Ball
- Sucker Punch
- U-turn
- High Jump Kick
Toxtricity (M) @ Throat Spray
Ability: Punk Rock
EVs: 4 Atk / 252 SpA / 252 Spe
Rash Nature
- Overdrive
- Boomburst
- Shift Gear
- Fire Punch
Seismitoad (M) @ Leftovers
Ability: Water Absorb
EVs: 252 HP / 252 Def / 4 SpD
Relaxed Nature
- Stealth Rock
- Scald
- Earthquake
- Toxic
Corviknight (M) @ Leftovers
Ability: Pressure
EVs: 248 HP / 80 SpD / 180 Spe
Impish Nature
- Defog
- Brave Bird
- Roost
- U-turn
"""
team_2 = """
Togekiss @ Leftovers
Ability: Serene Grace
EVs: 248 HP / 8 SpA / 252 Spe
Timid Nature
IVs: 0 Atk
- Air Slash
- Nasty Plot
- Substitute
- Thunder Wave
Galvantula @ Focus Sash
Ability: Compound Eyes
EVs: 252 SpA / 4 SpD / 252 Spe
Timid Nature
IVs: 0 Atk
- Sticky Web
- Thunder Wave
- Thunder
- Energy Ball
Cloyster @ Leftovers
Ability: Skill Link
EVs: 252 Atk / 4 SpD / 252 Spe
Adamant Nature
- Icicle Spear
- Rock Blast
- Ice Shard
- Shell Smash
Sandaconda @ Focus Sash
Ability: Sand Spit
EVs: 252 Atk / 4 SpD / 252 Spe
Jolly Nature
- Stealth Rock
- Glare
- Earthquake
- Rock Tomb
Excadrill @ Focus Sash
Ability: Sand Rush
EVs: 252 Atk / 4 SpD / 252 Spe
Adamant Nature
- Iron Head
- Rock Slide
- Earthquake
- Rapid Spin
Cinccino @ Leftovers
Ability: Skill Link
EVs: 252 Atk / 4 Def / 252 Spe
Jolly Nature
- Bullet Seed
- Knock Off
- Rock Blast
- Tail Slap
"""
teams = [team_1, team_2]
custom_builder = RandomTeamFromPool(teams)
for _ in range(5):
print(custom_builder.yield_team())
Goodra||assaultvest|sapsipper|dragonpulse,flamethrower,sludgewave,thunderbolt|Modest|248,,,252,,8|M|,0,,,,|||]Sylveon||leftovers|pixilate|hypervoice,mysticalfire,protect,wish|Calm|248,,244,,16,|M|,0,,,,|||]Cinderace||lifeorb|blaze|pyroball,suckerpunch,uturn,highjumpkick|Jolly|,252,,,4,252|M||||]Toxtricity||throatspray|punkrock|overdrive,boomburst,shiftgear,firepunch|Rash|,4,,252,,252|M||||]Seismitoad||leftovers|waterabsorb|stealthrock,scald,earthquake,toxic|Relaxed|252,,252,,4,|M||||]Corviknight||leftovers|pressure|defog,bravebird,roost,uturn|Impish|248,,,,80,180|M||||
Goodra||assaultvest|sapsipper|dragonpulse,flamethrower,sludgewave,thunderbolt|Modest|248,,,252,,8|M|,0,,,,|||]Sylveon||leftovers|pixilate|hypervoice,mysticalfire,protect,wish|Calm|248,,244,,16,|M|,0,,,,|||]Cinderace||lifeorb|blaze|pyroball,suckerpunch,uturn,highjumpkick|Jolly|,252,,,4,252|M||||]Toxtricity||throatspray|punkrock|overdrive,boomburst,shiftgear,firepunch|Rash|,4,,252,,252|M||||]Seismitoad||leftovers|waterabsorb|stealthrock,scald,earthquake,toxic|Relaxed|252,,252,,4,|M||||]Corviknight||leftovers|pressure|defog,bravebird,roost,uturn|Impish|248,,,,80,180|M||||
Togekiss||leftovers|serenegrace|airslash,nastyplot,substitute,thunderwave|Timid|248,,,8,,252||,0,,,,|||]Galvantula||focussash|compoundeyes|stickyweb,thunderwave,thunder,energyball|Timid|,,,252,4,252||,0,,,,|||]Cloyster||leftovers|skilllink|iciclespear,rockblast,iceshard,shellsmash|Adamant|,252,,,4,252|||||]Sandaconda||focussash|sandspit|stealthrock,glare,earthquake,rocktomb|Jolly|,252,,,4,252|||||]Excadrill||focussash|sandrush|ironhead,rockslide,earthquake,rapidspin|Adamant|,252,,,4,252|||||]Cinccino||leftovers|skilllink|bulletseed,knockoff,rockblast,tailslap|Jolly|,252,4,,,252|||||
Togekiss||leftovers|serenegrace|airslash,nastyplot,substitute,thunderwave|Timid|248,,,8,,252||,0,,,,|||]Galvantula||focussash|compoundeyes|stickyweb,thunderwave,thunder,energyball|Timid|,,,252,4,252||,0,,,,|||]Cloyster||leftovers|skilllink|iciclespear,rockblast,iceshard,shellsmash|Adamant|,252,,,4,252|||||]Sandaconda||focussash|sandspit|stealthrock,glare,earthquake,rocktomb|Jolly|,252,,,4,252|||||]Excadrill||focussash|sandrush|ironhead,rockslide,earthquake,rapidspin|Adamant|,252,,,4,252|||||]Cinccino||leftovers|skilllink|bulletseed,knockoff,rockblast,tailslap|Jolly|,252,4,,,252|||||
Togekiss||leftovers|serenegrace|airslash,nastyplot,substitute,thunderwave|Timid|248,,,8,,252||,0,,,,|||]Galvantula||focussash|compoundeyes|stickyweb,thunderwave,thunder,energyball|Timid|,,,252,4,252||,0,,,,|||]Cloyster||leftovers|skilllink|iciclespear,rockblast,iceshard,shellsmash|Adamant|,252,,,4,252|||||]Sandaconda||focussash|sandspit|stealthrock,glare,earthquake,rocktomb|Jolly|,252,,,4,252|||||]Excadrill||focussash|sandrush|ironhead,rockslide,earthquake,rapidspin|Adamant|,252,,,4,252|||||]Cinccino||leftovers|skilllink|bulletseed,knockoff,rockblast,tailslap|Jolly|,252,4,,,252|||||
Our custom_builder can now be used! To use a Teambuilder with a given Player, just pass it in its constructor, with the team keyword.
[3]:
from poke_env.player import RandomPlayer
player_1 = RandomPlayer(
battle_format="gen8anythinggoes",
team=custom_builder,
max_concurrent_battles=10,
)
player_2 = RandomPlayer(
battle_format="gen8anythinggoes",
team=custom_builder,
max_concurrent_battles=10,
)
await player_1.battle_against(player_2, n_battles=1)
Building a team from a set of pokemons
In this example, we will build a Teambuilder that will randomly generate teams from a set of pokemons.
First, let’s reuse the teams above to create a list of pokemons in showdown format:
[4]:
pokemons = team_1.split("\n\n") + team_2.split("\n\n")
pokemons = [pokemon.strip() for pokemon in sorted(pokemons)]
pokemons
[4]:
['Goodra (M) @ Assault Vest\nAbility: Sap Sipper\nEVs: 248 HP / 252 SpA / 8 Spe\nModest Nature\nIVs: 0 Atk\n- Dragon Pulse\n- Flamethrower\n- Sludge Wave\n- Thunderbolt',
'Togekiss @ Leftovers\nAbility: Serene Grace\nEVs: 248 HP / 8 SpA / 252 Spe\nTimid Nature\nIVs: 0 Atk\n- Air Slash\n- Nasty Plot\n- Substitute\n- Thunder Wave',
'Cinccino @ Leftovers\nAbility: Skill Link\nEVs: 252 Atk / 4 Def / 252 Spe\nJolly Nature\n- Bullet Seed\n- Knock Off\n- Rock Blast\n- Tail Slap',
'Cinderace (M) @ Life Orb\nAbility: Blaze\nEVs: 252 Atk / 4 SpD / 252 Spe\nJolly Nature\n- Pyro Ball\n- Sucker Punch\n- U-turn\n- High Jump Kick',
'Cloyster @ Leftovers\nAbility: Skill Link\nEVs: 252 Atk / 4 SpD / 252 Spe\nAdamant Nature\n- Icicle Spear\n- Rock Blast\n- Ice Shard\n- Shell Smash',
'Corviknight (M) @ Leftovers\nAbility: Pressure\nEVs: 248 HP / 80 SpD / 180 Spe\nImpish Nature\n- Defog\n- Brave Bird\n- Roost\n- U-turn',
'Excadrill @ Focus Sash\nAbility: Sand Rush\nEVs: 252 Atk / 4 SpD / 252 Spe\nAdamant Nature\n- Iron Head\n- Rock Slide\n- Earthquake\n- Rapid Spin',
'Galvantula @ Focus Sash\nAbility: Compound Eyes\nEVs: 252 SpA / 4 SpD / 252 Spe\nTimid Nature\nIVs: 0 Atk\n- Sticky Web\n- Thunder Wave\n- Thunder\n- Energy Ball',
'Sandaconda @ Focus Sash\nAbility: Sand Spit\nEVs: 252 Atk / 4 SpD / 252 Spe\nJolly Nature\n- Stealth Rock\n- Glare\n- Earthquake\n- Rock Tomb',
'Seismitoad (M) @ Leftovers\nAbility: Water Absorb\nEVs: 252 HP / 252 Def / 4 SpD\nRelaxed Nature\n- Stealth Rock\n- Scald\n- Earthquake\n- Toxic',
'Sylveon (M) @ Leftovers\nAbility: Pixilate\nEVs: 248 HP / 244 Def / 16 SpD\nCalm Nature\nIVs: 0 Atk\n- Hyper Voice\n- Mystical Fire\n- Protect\n- Wish',
'Toxtricity (M) @ Throat Spray\nAbility: Punk Rock\nEVs: 4 Atk / 252 SpA / 252 Spe\nRash Nature\n- Overdrive\n- Boomburst\n- Shift Gear\n- Fire Punch']
Now, we can create a Teambuilder that will randomly generate teams from this list of pokemons:
[5]:
class RandomTeamFromPool(Teambuilder):
def __init__(self, pokemons):
self.pokemons = []
for pokemon in pokemons:
parsed_mons = self.parse_showdown_team(pokemon)
self.pokemons.append(parsed_mons[0])
self.n_pokemons = len(self.pokemons)
assert self.n_pokemons >= 6
def yield_team(self):
idxs = np.random.choice(self.n_pokemons, 6, replace=False)
team = [self.pokemons[idx] for idx in idxs]
return self.join_team(team)
custom_builder = RandomTeamFromPool(pokemons)
for _ in range(5):
print(custom_builder.yield_team())
Toxtricity||throatspray|punkrock|overdrive,boomburst,shiftgear,firepunch|Rash|,4,,252,,252|M||||]Cinderace||lifeorb|blaze|pyroball,suckerpunch,uturn,highjumpkick|Jolly|,252,,,4,252|M||||]Corviknight||leftovers|pressure|defog,bravebird,roost,uturn|Impish|248,,,,80,180|M||||]Cinccino||leftovers|skilllink|bulletseed,knockoff,rockblast,tailslap|Jolly|,252,4,,,252|||||]Togekiss||leftovers|serenegrace|airslash,nastyplot,substitute,thunderwave|Timid|248,,,8,,252||,0,,,,|||]Galvantula||focussash|compoundeyes|stickyweb,thunderwave,thunder,energyball|Timid|,,,252,4,252||,0,,,,|||
Cinderace||lifeorb|blaze|pyroball,suckerpunch,uturn,highjumpkick|Jolly|,252,,,4,252|M||||]Corviknight||leftovers|pressure|defog,bravebird,roost,uturn|Impish|248,,,,80,180|M||||]Sylveon||leftovers|pixilate|hypervoice,mysticalfire,protect,wish|Calm|248,,244,,16,|M|,0,,,,|||]Sandaconda||focussash|sandspit|stealthrock,glare,earthquake,rocktomb|Jolly|,252,,,4,252|||||]Excadrill||focussash|sandrush|ironhead,rockslide,earthquake,rapidspin|Adamant|,252,,,4,252|||||]Toxtricity||throatspray|punkrock|overdrive,boomburst,shiftgear,firepunch|Rash|,4,,252,,252|M||||
Excadrill||focussash|sandrush|ironhead,rockslide,earthquake,rapidspin|Adamant|,252,,,4,252|||||]Seismitoad||leftovers|waterabsorb|stealthrock,scald,earthquake,toxic|Relaxed|252,,252,,4,|M||||]Galvantula||focussash|compoundeyes|stickyweb,thunderwave,thunder,energyball|Timid|,,,252,4,252||,0,,,,|||]Cloyster||leftovers|skilllink|iciclespear,rockblast,iceshard,shellsmash|Adamant|,252,,,4,252|||||]Goodra||assaultvest|sapsipper|dragonpulse,flamethrower,sludgewave,thunderbolt|Modest|248,,,252,,8|M|,0,,,,|||]Sandaconda||focussash|sandspit|stealthrock,glare,earthquake,rocktomb|Jolly|,252,,,4,252|||||
Corviknight||leftovers|pressure|defog,bravebird,roost,uturn|Impish|248,,,,80,180|M||||]Galvantula||focussash|compoundeyes|stickyweb,thunderwave,thunder,energyball|Timid|,,,252,4,252||,0,,,,|||]Sylveon||leftovers|pixilate|hypervoice,mysticalfire,protect,wish|Calm|248,,244,,16,|M|,0,,,,|||]Sandaconda||focussash|sandspit|stealthrock,glare,earthquake,rocktomb|Jolly|,252,,,4,252|||||]Cloyster||leftovers|skilllink|iciclespear,rockblast,iceshard,shellsmash|Adamant|,252,,,4,252|||||]Cinccino||leftovers|skilllink|bulletseed,knockoff,rockblast,tailslap|Jolly|,252,4,,,252|||||
Goodra||assaultvest|sapsipper|dragonpulse,flamethrower,sludgewave,thunderbolt|Modest|248,,,252,,8|M|,0,,,,|||]Seismitoad||leftovers|waterabsorb|stealthrock,scald,earthquake,toxic|Relaxed|252,,252,,4,|M||||]Excadrill||focussash|sandrush|ironhead,rockslide,earthquake,rapidspin|Adamant|,252,,,4,252|||||]Togekiss||leftovers|serenegrace|airslash,nastyplot,substitute,thunderwave|Timid|248,,,8,,252||,0,,,,|||]Galvantula||focussash|compoundeyes|stickyweb,thunderwave,thunder,energyball|Timid|,,,252,4,252||,0,,,,|||]Cinderace||lifeorb|blaze|pyroball,suckerpunch,uturn,highjumpkick|Jolly|,252,,,4,252|M||||
[6]:
from poke_env.player import RandomPlayer
player_1 = RandomPlayer(
battle_format="gen8anythinggoes",
team=custom_builder,
max_concurrent_battles=10,
)
player_2 = RandomPlayer(
battle_format="gen8anythinggoes",
team=custom_builder,
max_concurrent_battles=10,
)
await player_1.battle_against(player_2, n_battles=1)