The TeamValidator class handles team validation and learnset checking for Pokemon Showdown. It ensures teams comply with format rules, move legality, stat constraints, and event restrictions.
Constructor
Creates a new TeamValidator instance for a specific format.
new TeamValidator ( format : string | Format , dex ?: ModdedDex )
Format name (e.g., “gen9ou”, “gen8vgc2022”) or Format object
ModdedDex instance. Defaults to the standard Dex.
import { TeamValidator , Teams } from './sim' ;
const validator = new TeamValidator ( 'gen9ou' );
const team = Teams . unpack ( 'Garchomp||choiceband|roughskin|outrage,earthquake,stoneedge,firefang|Jolly|,252,,,4,252|||||' );
const problems = validator . validateTeam ( team );
if ( problems ) {
console . log ( 'Invalid team:' , problems );
} else {
console . log ( 'Team is valid!' );
}
Properties
The Format object for this validator
The ModdedDex instance used for validation (adjusted for the format)
The generation number for this format
The parsed rule table for this format
Minimum generation a Pokemon must be from (e.g., 6 if Pentagon required)
Methods
validateTeam()
Validates an entire team for format legality.
validateTeam (
team : PokemonSet [] | null ,
options ?: {
removeNicknames? : boolean ,
skipSets? : { [name: string]: { [key: string]: boolean } }
}
): string [] | null
Team to validate (null for formats with auto-generated teams)
Validation options Remove nicknames and use species names instead
skipSets
{ [name: string]: { [key: string]: boolean } }
Skip validation for specific sets
Array of problem strings if invalid, or null if valid
Valid Team
Invalid Team Example
Random Battle Format
import { TeamValidator , Teams } from './sim' ;
const validator = new TeamValidator ( 'gen9ou' );
const team = Teams . import ( `
Garchomp @ Choice Band
Ability: Rough Skin
EVs: 252 Atk / 4 SpD / 252 Spe
Jolly Nature
- Outrage
- Earthquake
- Stone Edge
- Fire Fang
` );
const problems = validator . validateTeam ( team );
if ( problems ) {
problems . forEach ( problem => console . log ( problem ));
} else {
console . log ( 'Valid team!' );
}
Team validation checks team size limits, species clauses, tier restrictions, and calls validateSet() for each Pokemon.
validateSet()
Validates a single Pokemon set.
validateSet (
set : PokemonSet ,
teamHas : AnyObject
): string [] | null
Object tracking what the team has (for clause checking). Modified in place.
Array of problem strings if invalid, or null if valid
import { TeamValidator } from './sim' ;
const validator = new TeamValidator ( 'gen9ou' );
const set = {
name: 'Garchomp' ,
species: 'Garchomp' ,
item: 'Choice Band' ,
ability: 'Rough Skin' ,
moves: [ 'Outrage' , 'Earthquake' , 'Stone Edge' , 'Fire Fang' ],
nature: 'Jolly' ,
evs: { hp: 0 , atk: 252 , def: 0 , spa: 0 , spd: 4 , spe: 252 },
ivs: { hp: 31 , atk: 31 , def: 31 , spa: 31 , spd: 31 , spe: 31 },
level: 100 ,
gender: ''
};
const teamHas = {};
const problems = validator . validateSet ( set , teamHas );
if ( problems ) {
console . log ( 'Invalid set:' , problems );
} else {
console . log ( 'Valid set!' );
console . log ( 'Team has:' , teamHas ); // Tracks species, abilities, items, etc.
}
baseValidateTeam()
Base team validation logic without custom format overrides.
baseValidateTeam (
team : PokemonSet [] | null ,
options ?: {
removeNicknames? : boolean ,
skipSets? : { [name: string]: { [key: string]: boolean } }
}
): string [] | null
Same parameters and return type as validateTeam(), but skips format-specific validateTeam overrides.
import { TeamValidator } from './sim' ;
const validator = new TeamValidator ( 'gen9ou' );
const team = [ /* ... */ ];
// Uses base validation only
const problems = validator . baseValidateTeam ( team );
validateStats()
Validates EVs, IVs, Hidden Power, and stat-related legality.
validateStats (
set : PokemonSet ,
species : Species ,
setSources : PokemonSources ,
pokemonGoProblems : string [] | null
): string []
Possible sources for this Pokemon
Pokemon GO validation problems
Array of stat-related problems
EVs Over Limit
Hidden Power Mismatch
Gen 2 DVs
const set = {
species: 'Garchomp' ,
evs: { hp: 252 , atk: 252 , def: 252 , spa: 0 , spd: 0 , spe: 0 }, // 756 total
ivs: { hp: 31 , atk: 31 , def: 31 , spa: 31 , spd: 31 , spe: 31 },
// ... other properties
};
const problems = validator . validateStats ( set , species , setSources , null );
// Returns: ["Garchomp has 756 total EVs, which is more than this format's limit of 510."]
validateStats() enforces:
EV limits (default 510 total, 252 per stat)
IV ranges (0-31, or 0-30 for Gen 1-2 DVs)
Hidden Power type matching IVs
Legendary 3-perfect-IV requirement (Gen 6+)
Gen 1-2 DV consistency (HP, gender, shininess)
Bottle Cap hyper training legality
getValidationSpecies()
Determines the out-of-battle and tier species for validation.
getValidationSpecies ( set : PokemonSet ): {
outOfBattleSpecies: Species ,
tierSpecies: Species
}
return
{ outOfBattleSpecies: Species, tierSpecies: Species }
Object containing both species forms Species used for learnset/event checking
Species used for tier/banlists (includes Mega/Primal forms)
Mega Evolution
Greninja-Ash
Primal Reversion
const set = {
species: 'Garchomp' ,
item: 'Garchompite' ,
// ...
};
const { outOfBattleSpecies , tierSpecies } = validator . getValidationSpecies ( set );
console . log ( outOfBattleSpecies . name ); // "Garchomp"
console . log ( tierSpecies . name ); // "Garchomp-Mega" (if obtainableformes rule)
getEventOnlyData()
Retrieves event data for event-only Pokemon.
getEventOnlyData (
species : Species ,
noRecurse ?: boolean
): { species: Species , eventData: EventInfo [] } | null
Don’t check prevo for event data
return
{ species: Species, eventData: EventInfo[] } | null
Event data object, or null if not event-only
const dex = validator . dex ;
const mew = dex . species . get ( 'Mew' );
const eventData = validator . getEventOnlyData ( mew );
if ( eventData ) {
console . log ( ` ${ eventData . species . name } has ${ eventData . eventData . length } events` );
eventData . eventData . forEach (( event , i ) => {
console . log ( `Event ${ i } : Gen ${ event . generation } , Level ${ event . level } ` );
});
}
PokemonSources Class
The PokemonSources class represents possible sources for obtaining a Pokemon with a specific set.
class PokemonSources {
sources : PokemonSource []; // Specific sources (e.g., "7S0", "8E")
sourcesBefore : number ; // Any source from this gen or earlier
sourcesAfter : number ; // Requires this gen or later
isHidden : boolean | null ; // Has Hidden Ability
limitedEggMoves ?: ID [] | null ; // Egg moves requiring specific fathers
moveEvoCarryCount : number ; // Moves requiring evolution
dreamWorldMoveCount : number ; // Dream World moves
// ... additional properties
}
Constructor
new PokemonSources ( sourcesBefore ?: number , sourcesAfter ?: number )
Allow all sources from this generation or earlier
Require sources from this generation or later
// Allow any Gen 8 or earlier source
const sources = new PokemonSources ( 8 , 0 );
// Require Gen 6+ sources (Pentagon)
const gen6plus = new PokemonSources ( 0 , 6 );
Methods
add()
Adds a specific source to the set.
add ( source : PokemonSource , limitedEggMove ?: ID | null ): void
const sources = new PokemonSources ();
sources . add ( '8S0' ); // Event source
sources . add ( '7E' ); // Egg source
sources . add ( '6L5' ); // Level-up move in Gen 6
addGen()
Adds all sources from a generation or earlier.
addGen ( sourceGen : number ): void
const sources = new PokemonSources ();
sources . addGen ( 7 ); // Allow all Gen 7 and earlier sources
minSourceGen() / maxSourceGen()
Gets the minimum or maximum generation in the source set.
minSourceGen (): number
maxSourceGen (): number
const sources = new PokemonSources ();
sources . add ( '6L1' );
sources . add ( '8M2' );
console . log ( sources . minSourceGen ()); // 6
console . log ( sources . maxSourceGen ()); // 8
intersectWith()
Intersects this source set with another (for combining move restrictions).
intersectWith ( other : PokemonSources ): void
const sources1 = new PokemonSources ();
sources1 . add ( '7E' );
sources1 . add ( '8V' );
const sources2 = new PokemonSources ();
sources2 . add ( '8V' );
sources2 . add ( '8L1' );
// Keep only common sources
sources1 . intersectWith ( sources2 );
// sources1.sources now contains only '8V'
Source Strings
Pokemon sources are encoded as strings:
// Format: [GEN][TYPE][INDEX] [SPECIES]
// Examples:
"8M2" // TM/TR move in Gen 8
"7L1" // Level-up move at level 1 in Gen 7
"6E" // Egg move in Gen 6
"7S0" // Event #0 in Gen 7
"5D" // Dream World in Gen 5
"8V" // Virtual Console/Let's Go transfer
"1ET" // Tradeback egg in Gen 1
Validation Examples
Basic Team Validation
import { TeamValidator , Teams } from './sim' ;
const validator = new TeamValidator ( 'gen9ou' );
const teamString = `
Garchomp @ Choice Band
Ability: Rough Skin
EVs: 252 Atk / 4 SpD / 252 Spe
Jolly Nature
- Outrage
- Earthquake
- Stone Edge
- Fire Fang
Great Tusk @ Leftovers
Ability: Protosynthesis
EVs: 252 HP / 4 Atk / 252 Spe
Jolly Nature
- Earthquake
- Rapid Spin
- Ice Spinner
- Knock Off
` ;
const team = Teams . import ( teamString );
const problems = validator . validateTeam ( team );
if ( problems ) {
console . log ( 'Team validation failed:' );
problems . forEach ( problem => console . log ( ` - ${ problem } ` ));
} else {
console . log ( 'Team is legal!' );
}
Validating Event Pokemon
import { TeamValidator , Teams } from './sim' ;
const validator = new TeamValidator ( 'gen8ou' );
// Victini with V-create (event-exclusive move)
const team = Teams . import ( `
Victini @ Choice Band
Ability: Victory Star
EVs: 252 Atk / 4 SpD / 252 Spe
Jolly Nature
- V-create
- Bolt Strike
- U-turn
- Trick
` );
const problems = validator . validateTeam ( team );
if ( problems ) {
console . log ( 'Event validation:' , problems );
// Will check if Victini's event allows this move combination
}
Checking Move Legality
import { TeamValidator , Teams } from './sim' ;
const validator = new TeamValidator ( 'gen9ou' );
// Illegal: Garchomp can't learn both Dragon Dance and Outrage
const illegalTeam = Teams . import ( `
Garchomp @ Choice Band
Ability: Rough Skin
EVs: 252 Atk / 4 SpD / 252 Spe
Jolly Nature
- Outrage
- Dragon Dance
- Earthquake
- Stone Edge
` );
const problems = validator . validateTeam ( illegalTeam );
if ( problems ) {
console . log ( 'Move legality issues:' );
problems . forEach ( problem => console . log ( ` ${ problem } ` ));
}
import { TeamValidator , Teams } from './sim' ;
// VGC 2023 Series 1
const validator = new TeamValidator ( 'gen9vgc2023series1' );
const vgcTeam = Teams . import ( `
Flutter Mane @ Choice Specs
Ability: Protosynthesis
Level: 50
EVs: 4 HP / 252 SpA / 252 Spe
Timid Nature
IVs: 0 Atk
- Shadow Ball
- Moonblast
- Icy Wind
- Thunderbolt
Iron Hands @ Assault Vest
Ability: Quark Drive
Level: 50
EVs: 252 HP / 252 Atk / 4 SpD
Adamant Nature
- Fake Out
- Close Combat
- Thunder Punch
- Heavy Slam
` );
const problems = validator . validateTeam ( vgcTeam );
if ( ! problems ) {
console . log ( 'VGC team is legal!' );
}
Static Methods
fillStats()
Fills missing stats with default values.
static TeamValidator . fillStats (
stats : Partial < StatsTable > | null ,
fillValue : number
): StatsTable
stats
Partial<StatsTable> | null
Partial stats object to fill
Value to use for missing stats
Complete stats object with all six stats
import { TeamValidator } from './sim' ;
// Fill EVs with 0
const evs = TeamValidator . fillStats ({ atk: 252 , spe: 252 }, 0 );
// Result: { hp: 0, atk: 252, def: 0, spa: 0, spd: 0, spe: 252 }
// Fill IVs with 31
const ivs = TeamValidator . fillStats ( null , 31 );
// Result: { hp: 31, atk: 31, def: 31, spa: 31, spd: 31, spe: 31 }
Error Handling
import { TeamValidator , Teams } from './sim' ;
try {
const validator = new TeamValidator ( 'invalidformat' );
} catch ( error ) {
console . error ( 'Invalid format:' , error . message );
// Error: format 'invalidformat' should be a 'Format', but was a 'undefined'
}
const validator = new TeamValidator ( 'gen9ou' );
const team = Teams . unpack ( 'InvalidData' );
if ( ! team ) {
console . error ( 'Failed to parse team' );
} else {
const problems = validator . validateTeam ( team );
if ( problems ) {
console . log ( 'Validation errors:' );
problems . forEach (( problem , i ) => {
console . log ( ` ${ i + 1 } . ${ problem } ` );
});
}
}
See Also
Teams - Team format conversion and generation
Dex - Pokemon data access
Formats - Format definitions and rules