Spaces:
Paused
Paused
| /** | |
| * Tools for testing Random Battles. | |
| * | |
| * @author Annika | |
| * Some helper functions by Slayer95 have also been added. | |
| */ | |
| ; | |
| const assert = require("../assert"); | |
| const Teams = require('./../../dist/sim/teams').Teams; | |
| const { TeamValidator, PokemonSources } = require('../../dist/sim/team-validator'); | |
| /** | |
| * Unit test helper for Pokemon sets | |
| * | |
| * @param {ID} pokemon the ID of the Pokemon whose set is to be tested | |
| * @param {{format?: string, rounds?: number, isDoubles?: boolean, isLead?: boolean, isDynamax?: boolean, seed?: PRNGSeed}} options | |
| * @param {(set: RandomTeamsTypes.RandomSet) => void} test a function called on each set | |
| */ | |
| function testSet(pokemon, options, test) { | |
| const rounds = options.rounds || 1000; | |
| const isDoubles = options.isDoubles || (options.format && options.format.includes('doubles')); | |
| const isDynamax = options.isDynamax || !(options.format && options.format.includes('nodmax')); | |
| const generator = Teams.getGenerator(options.format, [0, 0, 0, 0]); | |
| for (let i = 0; i < rounds; i++) { | |
| // If undefined, test lead 1/6 of the time | |
| const isLead = options.isLead === undefined ? i % 6 === 2 : options.isLead; | |
| generator.setSeed(options.seed || [i, i, i, i]); | |
| const set = generator.randomSet(pokemon, {}, isLead, isDoubles, isDynamax); | |
| test(set); | |
| } | |
| } | |
| /** | |
| * Tests that a Pokémon always gets STAB moves. | |
| * | |
| * @param {ID} pokemon | |
| * @param {{format?: string, rounds?: number, isDoubles?: boolean, isLead?: boolean, isDynamax?: boolean, seed?: PRNGSeed}} options | |
| * @param {string[] | undefined} types | |
| */ | |
| function testHasSTAB(pokemon, options, types = undefined) { | |
| const dex = Dex.forFormat(options.format || 'gen8randombattle'); | |
| types = types || dex.species.get(pokemon).types; | |
| testSet(pokemon, options, set => { | |
| assert( | |
| set.moves.some(move => types.includes(dex.moves.get(move).type)), | |
| `${pokemon} should have at least one STAB move (generated moveset: ${set.moves})` | |
| ); | |
| }); | |
| } | |
| /** | |
| * Tests that a Pokémon does not get two moves together. | |
| * | |
| * @param {ID} pokemon the ID of the Pokemon whose set is to be tested | |
| * @param {{format?: string, rounds?: number, isDoubles?: boolean, isLead?: boolean, isDynamax?: boolean, seed?: PRNGSeed}} options | |
| * @param {ID} move1 | |
| * @param {ID} move2 | |
| */ | |
| function testNotBothMoves(pokemon, options, move1, move2) { | |
| testSet(pokemon, options, set => { | |
| assert( | |
| !(set.moves.includes(move1) && set.moves.includes(move2)), | |
| `${pokemon} should not generate both "${move1}" and "${move2}" (generated moveset: ${set.moves})` | |
| ); | |
| }); | |
| } | |
| /** | |
| * Tests that a Pokémon does not get two copies of Hidden Power. | |
| * | |
| * @param {ID} pokemon the ID of the Pokemon whose set is to be tested | |
| * @param {{format?: string, rounds?: number, isDoubles?: boolean, isLead?: boolean, isDynamax?: boolean, seed?: PRNGSeed}} options | |
| */ | |
| function testHiddenPower(pokemon, options) { | |
| testSet(pokemon, options, set => { | |
| assert.equal(set.moves.length, 4, `fewer than 4 moves (got ${JSON.stringify(set.moves)})`); | |
| assert( | |
| set.moves.filter(m => m.startsWith('hiddenpower')).length < 2, | |
| `multiple Hidden Power moves (got ${JSON.stringify(set.moves)})` | |
| ); | |
| }); | |
| } | |
| /** | |
| * Tests that a Pokémon always gets a move. | |
| * | |
| * @param {ID} pokemon the ID of the Pokemon whose set is to be tested | |
| * @param {{format?: string, rounds?: number, isDoubles?: boolean, isLead?: boolean, isDynamax?: boolean, seed?: PRNGSeed}} options | |
| * @param {ID} move | |
| */ | |
| function testAlwaysHasMove(pokemon, options, move) { | |
| testSet(pokemon, options, set => { | |
| assert( | |
| set.moves.includes(move), | |
| `${pokemon} should always generate "${move}" (generated moveset: ${set.moves})` | |
| ); | |
| }); | |
| } | |
| /** | |
| * Unit test helper for Pokemon teams | |
| * | |
| * @param {{format?: string, rounds?: number, seed?: PRNGSeed}} options | |
| * @param {(team: RandomTeamsTypes.RandomSet[]) => void} test a function called on each team | |
| */ | |
| function testTeam(options, test) { | |
| const rounds = options.rounds || 1000; | |
| const generator = Teams.getGenerator(options.format, [0, 0, 0, 0]); | |
| for (let i = 0; i < rounds; i++) { | |
| generator.setSeed(options.seed || [i, i, i, i].join(',')); | |
| const team = generator.getTeam(); | |
| test(team); | |
| } | |
| } | |
| /** | |
| * Checks if a set is valid. | |
| * | |
| * @param {Format} format the format that the team is being validated in | |
| * @param {RandomTeamsTypes.RandomSet} set | |
| */ | |
| function assertSetValidity(format, set) { | |
| const dex = Dex.forFormat(format); | |
| const species = dex.species.get(set.species || set.name); | |
| const setString = JSON.stringify(set); | |
| // According to Random Battles room staff, we should not ensure that HP IVs are valid for | |
| // BSS formats. This is because level 100 Pokémon can be hypertrained | |
| // and then automatically downleveled to level 100 for the Battle Spot. | |
| if (!format.id.includes('bss')) { | |
| const valid = (new TeamValidator(format)) | |
| .validateStats(set, species, new PokemonSources()) | |
| // Suppress errors about mistaken EV quantities | |
| .filter(f => !f.includes(' EVs')); | |
| assert.equal(valid.length, 0, `Invalid stats: ${valid} (set: ${setString})`); | |
| } | |
| // We check `dex.gen` here because Format#gen is 0 in the current gen, while ModdedDex#gen is never 0. | |
| assert(species.exists, `The species "${species.name}" does not exist. (set: ${setString})`); | |
| assert(species.gen <= dex.gen, `The species "${species.name}" is from a newer generation. (set: ${setString})`); | |
| if (set.item) { | |
| const item = dex.items.get(set.item); | |
| assert(item.exists, `The item "${item.name}" does not exist. (set: ${setString})`); | |
| assert(item.gen <= dex.gen, `The item "${item.name}" is from a newer generation. (set: ${setString})`); | |
| } | |
| if (set.ability && set.ability !== 'None') { | |
| const ability = dex.abilities.get(set.ability); | |
| assert(ability.exists, `The ability "${ability.name}" does not exist. (set: ${setString})`); | |
| assert(ability.gen <= dex.gen, `The ability "${ability.name}" is from a newer generation. (set: ${setString})`); | |
| } else { | |
| assert(dex.gen < 3, `This set does not have an ability, but is intended for use in Gen 3 or later. (set: ${setString})`); | |
| } | |
| // Arceus plate check | |
| if ( | |
| species.baseSpecies === 'Arceus' && | |
| species.types[0] !== 'Normal' && | |
| (dex.gen !== 7 || !set.item.endsWith(' Z')) && | |
| format.team !== 'randomHC' | |
| ) { | |
| assert(set.item.endsWith(' Plate'), `${species.name} doesn't have a Plate (got "${set.item}" instead)`); | |
| } | |
| assert(set.moves.filter(m => m.startsWith('hiddenpower')).length <= 1, `This set has multiple Hidden Power moves. (set: ${setString})`); | |
| } | |
| /** | |
| * Checks if a move is valid on a set. | |
| * | |
| * @param {ID} move | |
| * @param {RandomTeamsTypes.RandomSet} set | |
| * @param {ID} tier | |
| * @param {ID} mod | |
| * @returns {boolean} | |
| */ | |
| function validateLearnset(move, set, tier, mod = 'gen8') { | |
| const validator = TeamValidator.get(`${mod}${tier}`); | |
| const species = validator.dex.species.get(set.species || set.name); | |
| return !validator.checkCanLearn(move, species); | |
| } | |
| exports.testSet = testSet; | |
| exports.testAlwaysHasMove = testAlwaysHasMove; | |
| exports.testNotBothMoves = testNotBothMoves; | |
| exports.testHiddenPower = testHiddenPower; | |
| exports.testTeam = testTeam; | |
| exports.testHasSTAB = testHasSTAB; | |
| exports.assertSetValidity = assertSetValidity; | |
| exports.validateLearnset = validateLearnset; | |