From 0b239a6b9a7e1dae58969ecac97663cbfe8747b0 Mon Sep 17 00:00:00 2001 From: livid washed <115855253+livid-washed@users.noreply.github.com> Date: Sat, 27 Jan 2024 12:39:52 +1100 Subject: [PATCH 1/8] Speed up Random Battles species selection --- data/mods/gen1/random-teams.ts | 3 +- data/mods/gen3/random-teams.ts | 15 +++---- data/mods/gen5/random-teams.ts | 15 +++---- data/mods/gen7/random-doubles-teams.ts | 26 ++++++------ data/mods/gen7/random-teams.ts | 26 ++++++------ data/mods/gen8/random-teams.ts | 55 +++++++++++++++--------- data/mods/potd/random-teams.ts | 21 +++------ data/random-teams.ts | 59 ++++++++++++++++---------- 8 files changed, 120 insertions(+), 100 deletions(-) diff --git a/data/mods/gen1/random-teams.ts b/data/mods/gen1/random-teams.ts index d5bf256638b1..58c780e51b98 100644 --- a/data/mods/gen1/random-teams.ts +++ b/data/mods/gen1/random-teams.ts @@ -124,7 +124,8 @@ export class RandomGen1Teams extends RandomGen2Teams { const weaknessCount: {[k: string]: number} = {Electric: 0, Psychic: 0, Water: 0, Ice: 0, Ground: 0, Fire: 0}; let numMaxLevelPokemon = 0; - const pokemonPool = this.getPokemonPool(type, pokemon, isMonotype, Object.keys(this.randomData))[0]; + const pokemonPool = Object.keys(this.getPokemonPool(type, pokemon, isMonotype, Object.keys(this.randomData))[0]); + while (pokemonPool.length && pokemon.length < this.maxTeamSize) { const species = this.dex.species.get(this.sampleNoReplace(pokemonPool)); if (!species.exists) continue; diff --git a/data/mods/gen3/random-teams.ts b/data/mods/gen3/random-teams.ts index e57b62f6a48d..4c2bdd7b1ad9 100644 --- a/data/mods/gen3/random-teams.ts +++ b/data/mods/gen3/random-teams.ts @@ -684,15 +684,12 @@ export class RandomGen3Teams extends RandomGen4Teams { let numMaxLevelPokemon = 0; const pokemonList = (this.gen === 3) ? Object.keys(this.randomSets) : Object.keys(this.randomData); - const [pokemonPool, baseSpeciesPool] = this.getPokemonPool(type, pokemon, isMonotype, pokemonList); - while (baseSpeciesPool.length && pokemon.length < this.maxTeamSize) { - const baseSpecies = this.sampleNoReplace(baseSpeciesPool); - const currentSpeciesPool: Species[] = []; - for (const poke of pokemonPool) { - const species = this.dex.species.get(poke); - if (species.baseSpecies === baseSpecies) currentSpeciesPool.push(species); - } - const species = this.sample(currentSpeciesPool); + const [pokemonPool, shuffledBaseSpecies] = this.getPokemonPool(type, pokemon, isMonotype, pokemonList); + + while (shuffledBaseSpecies.length && pokemon.length < this.maxTeamSize) { + // repeated popping from weighted shuffle is equivalent to repeated weighted sampling without replacement + const baseSpecies = shuffledBaseSpecies.pop()!.baseSpecies; + const species = this.dex.species.get(this.sample(pokemonPool[baseSpecies].speciesArray)); if (!species.exists) continue; // Limit to one of each species (Species Clause) diff --git a/data/mods/gen5/random-teams.ts b/data/mods/gen5/random-teams.ts index 31a9fe77f032..c37e0dcb7573 100644 --- a/data/mods/gen5/random-teams.ts +++ b/data/mods/gen5/random-teams.ts @@ -952,15 +952,12 @@ export class RandomGen5Teams extends RandomGen6Teams { let numMaxLevelPokemon = 0; const pokemonList = Object.keys(this.randomSets); - const [pokemonPool, baseSpeciesPool] = this.getPokemonPool(type, pokemon, isMonotype, pokemonList); - while (baseSpeciesPool.length && pokemon.length < this.maxTeamSize) { - const baseSpecies = this.sampleNoReplace(baseSpeciesPool); - const currentSpeciesPool: Species[] = []; - for (const poke of pokemonPool) { - const species = this.dex.species.get(poke); - if (species.baseSpecies === baseSpecies) currentSpeciesPool.push(species); - } - const species = this.sample(currentSpeciesPool); + const [pokemonPool, shuffledBaseSpecies] = this.getPokemonPool(type, pokemon, isMonotype, pokemonList); + + while (shuffledBaseSpecies.length && pokemon.length < this.maxTeamSize) { + // repeated popping from weighted shuffle is equivalent to repeated weighted sampling without replacement + const baseSpecies = shuffledBaseSpecies.pop()!.baseSpecies; + const species = this.dex.species.get(this.sample(pokemonPool[baseSpecies].speciesArray)); if (!species.exists) continue; // Limit to one of each species (Species Clause) diff --git a/data/mods/gen7/random-doubles-teams.ts b/data/mods/gen7/random-doubles-teams.ts index 8a6e351f10f5..c538739b4ff5 100644 --- a/data/mods/gen7/random-doubles-teams.ts +++ b/data/mods/gen7/random-doubles-teams.ts @@ -1367,25 +1367,25 @@ export class RandomGen7DoublesTeams extends RandomGen8Teams { if (pokemon.length >= this.maxTeamSize) break; const pokemonList = Object.keys(this.randomDoublesData); - const [pokemonPool, baseSpeciesPool] = this.getPokemonPool(type, pokemon, isMonotype, pokemonList); - while (baseSpeciesPool.length && pokemon.length < this.maxTeamSize) { - const baseSpecies = this.sampleNoReplace(baseSpeciesPool); + const [pokemonPool, shuffledBaseSpecies] = this.getPokemonPool(type, pokemon, isMonotype, pokemonList); + + while (shuffledBaseSpecies.length && pokemon.length < this.maxTeamSize) { + // repeated popping from weighted shuffle is equivalent to repeated weighted sampling without replacement + const baseSpecies = shuffledBaseSpecies.pop()!.baseSpecies; const currentSpeciesPool: Species[] = []; // Check if the base species has a mega forme available let canMega = false; - for (const poke of pokemonPool) { + for (const poke of pokemonPool[baseSpecies].speciesArray) { const species = this.dex.species.get(poke); - if (!hasMega && species.baseSpecies === baseSpecies && species.isMega) canMega = true; + if (!hasMega && species.isMega) canMega = true; } - for (const poke of pokemonPool) { + for (const poke of pokemonPool[baseSpecies].speciesArray) { const species = this.dex.species.get(poke); - if (species.baseSpecies === baseSpecies) { - // Prevent multiple megas - if (hasMega && species.isMega) continue; - // Prevent base forme, if a mega is available - if (canMega && !species.isMega) continue; - currentSpeciesPool.push(species); - } + // Prevent multiple megas + if (hasMega && species.isMega) continue; + // Prevent base forme, if a mega is available + if (canMega && !species.isMega) continue; + currentSpeciesPool.push(species); } const species = this.sample(currentSpeciesPool); if (!species.exists) continue; diff --git a/data/mods/gen7/random-teams.ts b/data/mods/gen7/random-teams.ts index 83ec29392b6a..c16a6ff0d8d1 100644 --- a/data/mods/gen7/random-teams.ts +++ b/data/mods/gen7/random-teams.ts @@ -1323,25 +1323,25 @@ export class RandomGen7Teams extends RandomGen8Teams { if (pokemon.length >= this.maxTeamSize) break; const pokemonList = Object.keys(this.randomSets); - const [pokemonPool, baseSpeciesPool] = this.getPokemonPool(type, pokemon, isMonotype, pokemonList); - while (baseSpeciesPool.length && pokemon.length < this.maxTeamSize) { - const baseSpecies = this.sampleNoReplace(baseSpeciesPool); + const [pokemonPool, shuffledBaseSpecies] = this.getPokemonPool(type, pokemon, isMonotype, pokemonList); + + while (shuffledBaseSpecies.length && pokemon.length < this.maxTeamSize) { + // repeated popping from weighted shuffle is equivalent to repeated weighted sampling without replacement + const baseSpecies = shuffledBaseSpecies.pop()!.baseSpecies; const currentSpeciesPool: Species[] = []; // Check if the base species has a mega forme available let canMega = false; - for (const poke of pokemonPool) { + for (const poke of pokemonPool[baseSpecies].speciesArray) { const species = this.dex.species.get(poke); - if (!hasMega && species.baseSpecies === baseSpecies && species.isMega) canMega = true; + if (!hasMega && species.isMega) canMega = true; } - for (const poke of pokemonPool) { + for (const poke of pokemonPool[baseSpecies].speciesArray) { const species = this.dex.species.get(poke); - if (species.baseSpecies === baseSpecies) { - // Prevent multiple megas - if (hasMega && species.isMega) continue; - // Prevent base forme, if a mega is available - if (canMega && !species.isMega) continue; - currentSpeciesPool.push(species); - } + // Prevent multiple megas + if (hasMega && species.isMega) continue; + // Prevent base forme, if a mega is available + if (canMega && !species.isMega) continue; + currentSpeciesPool.push(species); } const species = this.sample(currentSpeciesPool); diff --git a/data/mods/gen8/random-teams.ts b/data/mods/gen8/random-teams.ts index 529d02fb2ceb..3c6b20b14d37 100644 --- a/data/mods/gen8/random-teams.ts +++ b/data/mods/gen8/random-teams.ts @@ -2413,11 +2413,10 @@ export class RandomGen8Teams { pokemonToExclude: RandomTeamsTypes.RandomSet[] = [], isMonotype = false, pokemonList: string[] - ) { + ): [{[k: string]: {count: number, speciesArray: ID[]}}, {baseSpecies: string, score: number}[]] { const exclude = pokemonToExclude.map(p => toID(p.species)); - const pokemonPool = []; - const baseSpeciesPool = []; - const baseSpeciesCount: {[k: string]: number} = {}; + const pokemonPool: {[k: string]: {count: number, speciesArray: ID[]}} = {}; + const shuffledBaseSpecies = []; for (const pokemon of pokemonList) { let species = this.dex.species.get(pokemon); if (exclude.includes(species.id)) continue; @@ -2428,16 +2427,37 @@ export class RandomGen8Teams { if (!species.types.includes(type)) continue; } } - pokemonPool.push(pokemon); - baseSpeciesCount[species.baseSpecies] = (baseSpeciesCount[species.baseSpecies] || 0) + 1; + + if (species.baseSpecies in pokemonPool) { + pokemonPool[species.baseSpecies].count++; + pokemonPool[species.baseSpecies].speciesArray.push(species.id); + } else { + pokemonPool[species.baseSpecies] = {count: 1, speciesArray: [species.id]}; + } } // Include base species 1x if 1-3 formes, 2x if 4-6 formes, 3x if 7+ formes - for (const baseSpecies of Object.keys(baseSpeciesCount)) { - for (let i = 0; i < Math.min(Math.ceil(baseSpeciesCount[baseSpecies] / 3), 3); i++) { - baseSpeciesPool.push(baseSpecies); + for (const baseSpecies of Object.keys(pokemonPool)) { + // Squawkabilly has 4 formes, but only 2 functionally different formes, so only include it 1x + if (baseSpecies === 'Squawkabilly') { + pokemonPool[baseSpecies].count = 1; + } else { + pokemonPool[baseSpecies].count = Math.min(Math.ceil(pokemonPool[baseSpecies].count / 3), 3); } + + /** + * Weighted random shuffle + * Uses the fact that for two uniform variables x1 and x2, x1^(1/w1) is larger than x2^(1/w2) + * with probability equal to w1/(w1+w2), which is what we want. See e.g. here https://arxiv.org/pdf/1012.0256.pdf, + * original paper is behind a paywall. + */ + const sortObject = { + baseSpecies: baseSpecies, + score: Math.pow(this.prng.next(), 1 / pokemonPool[baseSpecies].count), + }; + shuffledBaseSpecies.push(sortObject); } - return [pokemonPool, baseSpeciesPool]; + shuffledBaseSpecies.sort((a, b) => a.score - b.score); + return [pokemonPool, shuffledBaseSpecies]; } randomTeam() { @@ -2471,15 +2491,12 @@ export class RandomGen8Teams { pokemonList.push(poke); } } - const [pokemonPool, baseSpeciesPool] = this.getPokemonPool(type, pokemon, isMonotype, pokemonList); - while (baseSpeciesPool.length && pokemon.length < this.maxTeamSize) { - const baseSpecies = this.sampleNoReplace(baseSpeciesPool); - const currentSpeciesPool: Species[] = []; - for (const poke of pokemonPool) { - const species = this.dex.species.get(poke); - if (species.baseSpecies === baseSpecies) currentSpeciesPool.push(species); - } - let species = this.sample(currentSpeciesPool); + const [pokemonPool, shuffledBaseSpecies] = this.getPokemonPool(type, pokemon, isMonotype, pokemonList); + + while (shuffledBaseSpecies.length && pokemon.length < this.maxTeamSize) { + // repeated popping from weighted shuffle is equivalent to repeated weighted sampling without replacement + const baseSpecies = shuffledBaseSpecies.pop()!.baseSpecies; + let species = this.dex.species.get(this.sample(pokemonPool[baseSpecies].speciesArray)); if (!species.exists) continue; // Limit to one of each species (Species Clause) diff --git a/data/mods/potd/random-teams.ts b/data/mods/potd/random-teams.ts index 727ba926a3ec..1395b6867084 100644 --- a/data/mods/potd/random-teams.ts +++ b/data/mods/potd/random-teams.ts @@ -33,12 +33,9 @@ export class RandomPOTDTeams extends RandomTeams { const teamDetails: RandomTeamsTypes.TeamDetails = {}; const pokemonList = isDoubles ? Object.keys(this.randomDoublesSets) : Object.keys(this.randomSets); - const [pokemonPool, baseSpeciesPool] = this.getPokemonPool(type, pokemon, isMonotype, pokemonList); - - // Remove PotD from baseSpeciesPool - if (baseSpeciesPool.includes(potd.baseSpecies)) { - this.fastPop(baseSpeciesPool, baseSpeciesPool.indexOf(potd.baseSpecies)); - } + const [pokemonPool, shuffledBaseSpecies] = this.getPokemonPool( + type, pokemon, isMonotype, pokemonList.filter(m => this.dex.species.get(m).baseSpecies !== potd.baseSpecies) + ); // Add PotD to type counts for (const typeName of potd.types) { @@ -54,14 +51,10 @@ export class RandomPOTDTeams extends RandomTeams { } } - while (baseSpeciesPool.length && pokemon.length < this.maxTeamSize) { - const baseSpecies = this.sampleNoReplace(baseSpeciesPool); - const currentSpeciesPool: Species[] = []; - for (const poke of pokemonPool) { - const species = this.dex.species.get(poke); - if (species.baseSpecies === baseSpecies) currentSpeciesPool.push(species); - } - let species = this.sample(currentSpeciesPool); + while (shuffledBaseSpecies.length && pokemon.length < this.maxTeamSize) { + // repeated popping from weighted shuffle is equivalent to repeated weighted sampling without replacement + const baseSpecies = shuffledBaseSpecies.pop()!.baseSpecies; + let species = this.dex.species.get(this.sample(pokemonPool[baseSpecies].speciesArray)); if (!species.exists) continue; // Limit to one of each species (Species Clause) diff --git a/data/random-teams.ts b/data/random-teams.ts index 857f13fe77c8..2c1cab9d7a4f 100644 --- a/data/random-teams.ts +++ b/data/random-teams.ts @@ -1716,11 +1716,10 @@ export class RandomTeams { pokemonToExclude: RandomTeamsTypes.RandomSet[] = [], isMonotype = false, pokemonList: string[] - ) { + ): [{[k: string]: {count: number, speciesArray: ID[]}}, {baseSpecies: string, score: number}[]] { const exclude = pokemonToExclude.map(p => toID(p.species)); - const pokemonPool = []; - const baseSpeciesPool = []; - const baseSpeciesCount: {[k: string]: number} = {}; + const pokemonPool: {[k: string]: {count: number, speciesArray: ID[]}} = {}; + const shuffledBaseSpecies = []; for (const pokemon of pokemonList) { let species = this.dex.species.get(pokemon); if (exclude.includes(species.id)) continue; @@ -1731,18 +1730,37 @@ export class RandomTeams { if (!species.types.includes(type)) continue; } } - pokemonPool.push(pokemon); - baseSpeciesCount[species.baseSpecies] = (baseSpeciesCount[species.baseSpecies] || 0) + 1; + + if (species.baseSpecies in pokemonPool) { + pokemonPool[species.baseSpecies].count++; + pokemonPool[species.baseSpecies].speciesArray.push(species.id); + } else { + pokemonPool[species.baseSpecies] = {count: 1, speciesArray: [species.id]}; + } } // Include base species 1x if 1-3 formes, 2x if 4-6 formes, 3x if 7+ formes - for (const baseSpecies of Object.keys(baseSpeciesCount)) { - for (let i = 0; i < Math.min(Math.ceil(baseSpeciesCount[baseSpecies] / 3), 3); i++) { - baseSpeciesPool.push(baseSpecies); - // Squawkabilly has 4 formes, but only 2 functionally different formes, so only include it 1x - if (baseSpecies === 'Squawkabilly') break; - } + for (const baseSpecies of Object.keys(pokemonPool)) { + // Squawkabilly has 4 formes, but only 2 functionally different formes, so only include it 1x + if (baseSpecies === 'Squawkabilly') { + pokemonPool[baseSpecies].count = 1; + } else { + pokemonPool[baseSpecies].count = Math.min(Math.ceil(pokemonPool[baseSpecies].count / 3), 3); + } + + /** + * Weighted random shuffle + * Uses the fact that for two uniform variables x1 and x2, x1^(1/w1) is larger than x2^(1/w2) + * with probability equal to w1/(w1+w2), which is what we want. See e.g. here https://arxiv.org/pdf/1012.0256.pdf, + * original paper is behind a paywall. + */ + const sortObject = { + baseSpecies: baseSpecies, + score: Math.pow(this.prng.next(), 1 / pokemonPool[baseSpecies].count), + }; + shuffledBaseSpecies.push(sortObject); } - return [pokemonPool, baseSpeciesPool]; + shuffledBaseSpecies.sort((a, b) => a.score - b.score); + return [pokemonPool, shuffledBaseSpecies]; } randomSets: {[species: string]: RandomTeamsTypes.RandomSpeciesData} = require('./random-sets.json'); @@ -1774,17 +1792,14 @@ export class RandomTeams { let numMaxLevelPokemon = 0; const pokemonList = isDoubles ? Object.keys(this.randomDoublesSets) : Object.keys(this.randomSets); - const [pokemonPool, baseSpeciesPool] = this.getPokemonPool(type, pokemon, isMonotype, pokemonList); + const [pokemonPool, shuffledBaseSpecies] = this.getPokemonPool(type, pokemon, isMonotype, pokemonList); let leadsRemaining = this.format.gameType === 'doubles' ? 2 : 1; - while (baseSpeciesPool.length && pokemon.length < this.maxTeamSize) { - const baseSpecies = this.sampleNoReplace(baseSpeciesPool); - const currentSpeciesPool: Species[] = []; - for (const poke of pokemonPool) { - const species = this.dex.species.get(poke); - if (species.baseSpecies === baseSpecies) currentSpeciesPool.push(species); - } - let species = this.sample(currentSpeciesPool); + + while (shuffledBaseSpecies.length && pokemon.length < this.maxTeamSize) { + // repeated popping from weighted shuffle is equivalent to repeated weighted sampling without replacement + const baseSpecies = shuffledBaseSpecies.pop()!.baseSpecies; + let species = this.dex.species.get(this.sample(pokemonPool[baseSpecies].speciesArray)); if (!species.exists) continue; // Limit to one of each species (Species Clause) From df7c75922a43c39335126dd397d6af5a0a011cc4 Mon Sep 17 00:00:00 2001 From: livid washed <115855253+livid-washed@users.noreply.github.com> Date: Sat, 27 Jan 2024 23:28:24 +1100 Subject: [PATCH 2/8] Apply suggestions from code review Thanks, you're right. This simplifies the code. I will make the other necessary adjustments Co-authored-by: urkerab --- data/random-teams.ts | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/data/random-teams.ts b/data/random-teams.ts index 2c1cab9d7a4f..84f2678a4c9d 100644 --- a/data/random-teams.ts +++ b/data/random-teams.ts @@ -1718,7 +1718,7 @@ export class RandomTeams { pokemonList: string[] ): [{[k: string]: {count: number, speciesArray: ID[]}}, {baseSpecies: string, score: number}[]] { const exclude = pokemonToExclude.map(p => toID(p.species)); - const pokemonPool: {[k: string]: {count: number, speciesArray: ID[]}} = {}; + const pokemonPool: {[k: string]: ID[]} = {}; const shuffledBaseSpecies = []; for (const pokemon of pokemonList) { let species = this.dex.species.get(pokemon); @@ -1732,20 +1732,15 @@ export class RandomTeams { } if (species.baseSpecies in pokemonPool) { - pokemonPool[species.baseSpecies].count++; - pokemonPool[species.baseSpecies].speciesArray.push(species.id); + pokemonPool[species.baseSpecies].push(species.id); } else { - pokemonPool[species.baseSpecies] = {count: 1, speciesArray: [species.id]}; + pokemonPool[species.baseSpecies] = [species.id]; } } // Include base species 1x if 1-3 formes, 2x if 4-6 formes, 3x if 7+ formes for (const baseSpecies of Object.keys(pokemonPool)) { // Squawkabilly has 4 formes, but only 2 functionally different formes, so only include it 1x - if (baseSpecies === 'Squawkabilly') { - pokemonPool[baseSpecies].count = 1; - } else { - pokemonPool[baseSpecies].count = Math.min(Math.ceil(pokemonPool[baseSpecies].count / 3), 3); - } + let weight = baseSpecies === 'Squawkabilly` ? 1 : Math.min(Math.ceil(pokemonPool[baseSpecies].count / 3), 3); /** * Weighted random shuffle @@ -1755,7 +1750,7 @@ export class RandomTeams { */ const sortObject = { baseSpecies: baseSpecies, - score: Math.pow(this.prng.next(), 1 / pokemonPool[baseSpecies].count), + score: Math.pow(this.prng.next(), 1 / weight), }; shuffledBaseSpecies.push(sortObject); } @@ -1799,7 +1794,7 @@ export class RandomTeams { while (shuffledBaseSpecies.length && pokemon.length < this.maxTeamSize) { // repeated popping from weighted shuffle is equivalent to repeated weighted sampling without replacement const baseSpecies = shuffledBaseSpecies.pop()!.baseSpecies; - let species = this.dex.species.get(this.sample(pokemonPool[baseSpecies].speciesArray)); + let species = this.dex.species.get(this.sample(pokemonPool[baseSpecies])); if (!species.exists) continue; // Limit to one of each species (Species Clause) From 279c841772c178224e38efa3e3e740a7de586962 Mon Sep 17 00:00:00 2001 From: livid washed <115855253+livid-washed@users.noreply.github.com> Date: Sat, 27 Jan 2024 23:33:42 +1100 Subject: [PATCH 3/8] Improve gen 9 weight code --- data/mods/potd/random-teams.ts | 2 +- data/random-teams.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/data/mods/potd/random-teams.ts b/data/mods/potd/random-teams.ts index 1395b6867084..2f1c0e085e48 100644 --- a/data/mods/potd/random-teams.ts +++ b/data/mods/potd/random-teams.ts @@ -54,7 +54,7 @@ export class RandomPOTDTeams extends RandomTeams { while (shuffledBaseSpecies.length && pokemon.length < this.maxTeamSize) { // repeated popping from weighted shuffle is equivalent to repeated weighted sampling without replacement const baseSpecies = shuffledBaseSpecies.pop()!.baseSpecies; - let species = this.dex.species.get(this.sample(pokemonPool[baseSpecies].speciesArray)); + let species = this.dex.species.get(this.sample(pokemonPool[baseSpecies])); if (!species.exists) continue; // Limit to one of each species (Species Clause) diff --git a/data/random-teams.ts b/data/random-teams.ts index 84f2678a4c9d..37d92be5b7f9 100644 --- a/data/random-teams.ts +++ b/data/random-teams.ts @@ -1716,7 +1716,7 @@ export class RandomTeams { pokemonToExclude: RandomTeamsTypes.RandomSet[] = [], isMonotype = false, pokemonList: string[] - ): [{[k: string]: {count: number, speciesArray: ID[]}}, {baseSpecies: string, score: number}[]] { + ): [{[k: string]: ID[]}, {baseSpecies: string, score: number}[]] { const exclude = pokemonToExclude.map(p => toID(p.species)); const pokemonPool: {[k: string]: ID[]} = {}; const shuffledBaseSpecies = []; @@ -1740,7 +1740,7 @@ export class RandomTeams { // Include base species 1x if 1-3 formes, 2x if 4-6 formes, 3x if 7+ formes for (const baseSpecies of Object.keys(pokemonPool)) { // Squawkabilly has 4 formes, but only 2 functionally different formes, so only include it 1x - let weight = baseSpecies === 'Squawkabilly` ? 1 : Math.min(Math.ceil(pokemonPool[baseSpecies].count / 3), 3); + const weight = (baseSpecies === 'Squawkabilly') ? 1 : Math.min(Math.ceil(pokemonPool[baseSpecies].length / 3), 3); /** * Weighted random shuffle From a3b69a305ccf4ea2262977397e8e419ca2ff3ed2 Mon Sep 17 00:00:00 2001 From: livid washed <115855253+livid-washed@users.noreply.github.com> Date: Sat, 27 Jan 2024 23:35:36 +1100 Subject: [PATCH 4/8] Apply the same changes to old gens --- data/mods/gen3/random-teams.ts | 2 +- data/mods/gen5/random-teams.ts | 2 +- data/mods/gen7/random-doubles-teams.ts | 4 ++-- data/mods/gen7/random-teams.ts | 4 ++-- data/mods/gen8/random-teams.ts | 19 +++++++------------ 5 files changed, 13 insertions(+), 18 deletions(-) diff --git a/data/mods/gen3/random-teams.ts b/data/mods/gen3/random-teams.ts index 4c2bdd7b1ad9..60a472da47b7 100644 --- a/data/mods/gen3/random-teams.ts +++ b/data/mods/gen3/random-teams.ts @@ -689,7 +689,7 @@ export class RandomGen3Teams extends RandomGen4Teams { while (shuffledBaseSpecies.length && pokemon.length < this.maxTeamSize) { // repeated popping from weighted shuffle is equivalent to repeated weighted sampling without replacement const baseSpecies = shuffledBaseSpecies.pop()!.baseSpecies; - const species = this.dex.species.get(this.sample(pokemonPool[baseSpecies].speciesArray)); + const species = this.dex.species.get(this.sample(pokemonPool[baseSpecies])); if (!species.exists) continue; // Limit to one of each species (Species Clause) diff --git a/data/mods/gen5/random-teams.ts b/data/mods/gen5/random-teams.ts index c37e0dcb7573..6e9fda8006f1 100644 --- a/data/mods/gen5/random-teams.ts +++ b/data/mods/gen5/random-teams.ts @@ -957,7 +957,7 @@ export class RandomGen5Teams extends RandomGen6Teams { while (shuffledBaseSpecies.length && pokemon.length < this.maxTeamSize) { // repeated popping from weighted shuffle is equivalent to repeated weighted sampling without replacement const baseSpecies = shuffledBaseSpecies.pop()!.baseSpecies; - const species = this.dex.species.get(this.sample(pokemonPool[baseSpecies].speciesArray)); + const species = this.dex.species.get(this.sample(pokemonPool[baseSpecies])); if (!species.exists) continue; // Limit to one of each species (Species Clause) diff --git a/data/mods/gen7/random-doubles-teams.ts b/data/mods/gen7/random-doubles-teams.ts index c538739b4ff5..90d85c70df19 100644 --- a/data/mods/gen7/random-doubles-teams.ts +++ b/data/mods/gen7/random-doubles-teams.ts @@ -1375,11 +1375,11 @@ export class RandomGen7DoublesTeams extends RandomGen8Teams { const currentSpeciesPool: Species[] = []; // Check if the base species has a mega forme available let canMega = false; - for (const poke of pokemonPool[baseSpecies].speciesArray) { + for (const poke of pokemonPool[baseSpecies]) { const species = this.dex.species.get(poke); if (!hasMega && species.isMega) canMega = true; } - for (const poke of pokemonPool[baseSpecies].speciesArray) { + for (const poke of pokemonPool[baseSpecies]) { const species = this.dex.species.get(poke); // Prevent multiple megas if (hasMega && species.isMega) continue; diff --git a/data/mods/gen7/random-teams.ts b/data/mods/gen7/random-teams.ts index c16a6ff0d8d1..a32f8c83aa7a 100644 --- a/data/mods/gen7/random-teams.ts +++ b/data/mods/gen7/random-teams.ts @@ -1331,11 +1331,11 @@ export class RandomGen7Teams extends RandomGen8Teams { const currentSpeciesPool: Species[] = []; // Check if the base species has a mega forme available let canMega = false; - for (const poke of pokemonPool[baseSpecies].speciesArray) { + for (const poke of pokemonPool[baseSpecies]) { const species = this.dex.species.get(poke); if (!hasMega && species.isMega) canMega = true; } - for (const poke of pokemonPool[baseSpecies].speciesArray) { + for (const poke of pokemonPool[baseSpecies]) { const species = this.dex.species.get(poke); // Prevent multiple megas if (hasMega && species.isMega) continue; diff --git a/data/mods/gen8/random-teams.ts b/data/mods/gen8/random-teams.ts index 3c6b20b14d37..d957c12283ae 100644 --- a/data/mods/gen8/random-teams.ts +++ b/data/mods/gen8/random-teams.ts @@ -2413,9 +2413,9 @@ export class RandomGen8Teams { pokemonToExclude: RandomTeamsTypes.RandomSet[] = [], isMonotype = false, pokemonList: string[] - ): [{[k: string]: {count: number, speciesArray: ID[]}}, {baseSpecies: string, score: number}[]] { + ): [{[k: string]: ID[]}, {baseSpecies: string, score: number}[]] { const exclude = pokemonToExclude.map(p => toID(p.species)); - const pokemonPool: {[k: string]: {count: number, speciesArray: ID[]}} = {}; + const pokemonPool: {[k: string]: ID[]} = {}; const shuffledBaseSpecies = []; for (const pokemon of pokemonList) { let species = this.dex.species.get(pokemon); @@ -2429,20 +2429,15 @@ export class RandomGen8Teams { } if (species.baseSpecies in pokemonPool) { - pokemonPool[species.baseSpecies].count++; - pokemonPool[species.baseSpecies].speciesArray.push(species.id); + pokemonPool[species.baseSpecies].push(species.id); } else { - pokemonPool[species.baseSpecies] = {count: 1, speciesArray: [species.id]}; + pokemonPool[species.baseSpecies] = [species.id]; } } // Include base species 1x if 1-3 formes, 2x if 4-6 formes, 3x if 7+ formes for (const baseSpecies of Object.keys(pokemonPool)) { // Squawkabilly has 4 formes, but only 2 functionally different formes, so only include it 1x - if (baseSpecies === 'Squawkabilly') { - pokemonPool[baseSpecies].count = 1; - } else { - pokemonPool[baseSpecies].count = Math.min(Math.ceil(pokemonPool[baseSpecies].count / 3), 3); - } + const weight = (baseSpecies === 'Squawkabilly') ? 1 : Math.min(Math.ceil(pokemonPool[baseSpecies].length / 3), 3); /** * Weighted random shuffle @@ -2452,7 +2447,7 @@ export class RandomGen8Teams { */ const sortObject = { baseSpecies: baseSpecies, - score: Math.pow(this.prng.next(), 1 / pokemonPool[baseSpecies].count), + score: Math.pow(this.prng.next(), 1 / weight), }; shuffledBaseSpecies.push(sortObject); } @@ -2496,7 +2491,7 @@ export class RandomGen8Teams { while (shuffledBaseSpecies.length && pokemon.length < this.maxTeamSize) { // repeated popping from weighted shuffle is equivalent to repeated weighted sampling without replacement const baseSpecies = shuffledBaseSpecies.pop()!.baseSpecies; - let species = this.dex.species.get(this.sample(pokemonPool[baseSpecies].speciesArray)); + let species = this.dex.species.get(this.sample(pokemonPool[baseSpecies])); if (!species.exists) continue; // Limit to one of each species (Species Clause) From bf23550c83325e515be5c90091d2aaf5f6b17f9f Mon Sep 17 00:00:00 2001 From: livid washed <115855253+livid-washed@users.noreply.github.com> Date: Sun, 28 Jan 2024 11:24:58 +1100 Subject: [PATCH 5/8] Go back to baseSpeciesPool --- data/mods/gen3/random-teams.ts | 7 +++---- data/mods/gen5/random-teams.ts | 7 +++---- data/mods/gen7/random-doubles-teams.ts | 7 +++---- data/mods/gen7/random-teams.ts | 7 +++---- data/mods/gen8/random-teams.ts | 27 +++++++------------------- data/mods/potd/random-teams.ts | 7 +++---- data/random-teams.ts | 27 +++++++------------------- 7 files changed, 29 insertions(+), 60 deletions(-) diff --git a/data/mods/gen3/random-teams.ts b/data/mods/gen3/random-teams.ts index 60a472da47b7..e35ffd1d3b26 100644 --- a/data/mods/gen3/random-teams.ts +++ b/data/mods/gen3/random-teams.ts @@ -684,11 +684,10 @@ export class RandomGen3Teams extends RandomGen4Teams { let numMaxLevelPokemon = 0; const pokemonList = (this.gen === 3) ? Object.keys(this.randomSets) : Object.keys(this.randomData); - const [pokemonPool, shuffledBaseSpecies] = this.getPokemonPool(type, pokemon, isMonotype, pokemonList); + const [pokemonPool, baseSpeciesPool] = this.getPokemonPool(type, pokemon, isMonotype, pokemonList); - while (shuffledBaseSpecies.length && pokemon.length < this.maxTeamSize) { - // repeated popping from weighted shuffle is equivalent to repeated weighted sampling without replacement - const baseSpecies = shuffledBaseSpecies.pop()!.baseSpecies; + while (baseSpeciesPool.length && pokemon.length < this.maxTeamSize) { + const baseSpecies = this.sampleNoReplace(baseSpeciesPool); const species = this.dex.species.get(this.sample(pokemonPool[baseSpecies])); if (!species.exists) continue; diff --git a/data/mods/gen5/random-teams.ts b/data/mods/gen5/random-teams.ts index 6e9fda8006f1..61460d0f46a4 100644 --- a/data/mods/gen5/random-teams.ts +++ b/data/mods/gen5/random-teams.ts @@ -952,11 +952,10 @@ export class RandomGen5Teams extends RandomGen6Teams { let numMaxLevelPokemon = 0; const pokemonList = Object.keys(this.randomSets); - const [pokemonPool, shuffledBaseSpecies] = this.getPokemonPool(type, pokemon, isMonotype, pokemonList); + const [pokemonPool, baseSpeciesPool] = this.getPokemonPool(type, pokemon, isMonotype, pokemonList); - while (shuffledBaseSpecies.length && pokemon.length < this.maxTeamSize) { - // repeated popping from weighted shuffle is equivalent to repeated weighted sampling without replacement - const baseSpecies = shuffledBaseSpecies.pop()!.baseSpecies; + while (baseSpeciesPool.length && pokemon.length < this.maxTeamSize) { + const baseSpecies = this.sampleNoReplace(baseSpeciesPool); const species = this.dex.species.get(this.sample(pokemonPool[baseSpecies])); if (!species.exists) continue; diff --git a/data/mods/gen7/random-doubles-teams.ts b/data/mods/gen7/random-doubles-teams.ts index 90d85c70df19..87548364fe8e 100644 --- a/data/mods/gen7/random-doubles-teams.ts +++ b/data/mods/gen7/random-doubles-teams.ts @@ -1367,11 +1367,10 @@ export class RandomGen7DoublesTeams extends RandomGen8Teams { if (pokemon.length >= this.maxTeamSize) break; const pokemonList = Object.keys(this.randomDoublesData); - const [pokemonPool, shuffledBaseSpecies] = this.getPokemonPool(type, pokemon, isMonotype, pokemonList); + const [pokemonPool, baseSpeciesPool] = this.getPokemonPool(type, pokemon, isMonotype, pokemonList); - while (shuffledBaseSpecies.length && pokemon.length < this.maxTeamSize) { - // repeated popping from weighted shuffle is equivalent to repeated weighted sampling without replacement - const baseSpecies = shuffledBaseSpecies.pop()!.baseSpecies; + while (baseSpeciesPool.length && pokemon.length < this.maxTeamSize) { + const baseSpecies = this.sampleNoReplace(baseSpeciesPool); const currentSpeciesPool: Species[] = []; // Check if the base species has a mega forme available let canMega = false; diff --git a/data/mods/gen7/random-teams.ts b/data/mods/gen7/random-teams.ts index a32f8c83aa7a..116aabe32db5 100644 --- a/data/mods/gen7/random-teams.ts +++ b/data/mods/gen7/random-teams.ts @@ -1323,11 +1323,10 @@ export class RandomGen7Teams extends RandomGen8Teams { if (pokemon.length >= this.maxTeamSize) break; const pokemonList = Object.keys(this.randomSets); - const [pokemonPool, shuffledBaseSpecies] = this.getPokemonPool(type, pokemon, isMonotype, pokemonList); + const [pokemonPool, baseSpeciesPool] = this.getPokemonPool(type, pokemon, isMonotype, pokemonList); - while (shuffledBaseSpecies.length && pokemon.length < this.maxTeamSize) { - // repeated popping from weighted shuffle is equivalent to repeated weighted sampling without replacement - const baseSpecies = shuffledBaseSpecies.pop()!.baseSpecies; + while (baseSpeciesPool.length && pokemon.length < this.maxTeamSize) { + const baseSpecies = this.sampleNoReplace(baseSpeciesPool); const currentSpeciesPool: Species[] = []; // Check if the base species has a mega forme available let canMega = false; diff --git a/data/mods/gen8/random-teams.ts b/data/mods/gen8/random-teams.ts index d957c12283ae..b5c7de684fec 100644 --- a/data/mods/gen8/random-teams.ts +++ b/data/mods/gen8/random-teams.ts @@ -2413,10 +2413,10 @@ export class RandomGen8Teams { pokemonToExclude: RandomTeamsTypes.RandomSet[] = [], isMonotype = false, pokemonList: string[] - ): [{[k: string]: ID[]}, {baseSpecies: string, score: number}[]] { + ): [{[k: string]: ID[]}, string[]] { const exclude = pokemonToExclude.map(p => toID(p.species)); const pokemonPool: {[k: string]: ID[]} = {}; - const shuffledBaseSpecies = []; + const baseSpeciesPool = []; for (const pokemon of pokemonList) { let species = this.dex.species.get(pokemon); if (exclude.includes(species.id)) continue; @@ -2438,21 +2438,9 @@ export class RandomGen8Teams { for (const baseSpecies of Object.keys(pokemonPool)) { // Squawkabilly has 4 formes, but only 2 functionally different formes, so only include it 1x const weight = (baseSpecies === 'Squawkabilly') ? 1 : Math.min(Math.ceil(pokemonPool[baseSpecies].length / 3), 3); - - /** - * Weighted random shuffle - * Uses the fact that for two uniform variables x1 and x2, x1^(1/w1) is larger than x2^(1/w2) - * with probability equal to w1/(w1+w2), which is what we want. See e.g. here https://arxiv.org/pdf/1012.0256.pdf, - * original paper is behind a paywall. - */ - const sortObject = { - baseSpecies: baseSpecies, - score: Math.pow(this.prng.next(), 1 / weight), - }; - shuffledBaseSpecies.push(sortObject); + for (let i = 0; i < weight; i++) baseSpeciesPool.push(baseSpecies); } - shuffledBaseSpecies.sort((a, b) => a.score - b.score); - return [pokemonPool, shuffledBaseSpecies]; + return [pokemonPool, baseSpeciesPool]; } randomTeam() { @@ -2486,11 +2474,10 @@ export class RandomGen8Teams { pokemonList.push(poke); } } - const [pokemonPool, shuffledBaseSpecies] = this.getPokemonPool(type, pokemon, isMonotype, pokemonList); + const [pokemonPool, baseSpeciesPool] = this.getPokemonPool(type, pokemon, isMonotype, pokemonList); - while (shuffledBaseSpecies.length && pokemon.length < this.maxTeamSize) { - // repeated popping from weighted shuffle is equivalent to repeated weighted sampling without replacement - const baseSpecies = shuffledBaseSpecies.pop()!.baseSpecies; + while (baseSpeciesPool.length && pokemon.length < this.maxTeamSize) { + const baseSpecies = this.sampleNoReplace(baseSpeciesPool); let species = this.dex.species.get(this.sample(pokemonPool[baseSpecies])); if (!species.exists) continue; diff --git a/data/mods/potd/random-teams.ts b/data/mods/potd/random-teams.ts index 2f1c0e085e48..bca2df23e2f9 100644 --- a/data/mods/potd/random-teams.ts +++ b/data/mods/potd/random-teams.ts @@ -33,7 +33,7 @@ export class RandomPOTDTeams extends RandomTeams { const teamDetails: RandomTeamsTypes.TeamDetails = {}; const pokemonList = isDoubles ? Object.keys(this.randomDoublesSets) : Object.keys(this.randomSets); - const [pokemonPool, shuffledBaseSpecies] = this.getPokemonPool( + const [pokemonPool, baseSpeciesPool] = this.getPokemonPool( type, pokemon, isMonotype, pokemonList.filter(m => this.dex.species.get(m).baseSpecies !== potd.baseSpecies) ); @@ -51,9 +51,8 @@ export class RandomPOTDTeams extends RandomTeams { } } - while (shuffledBaseSpecies.length && pokemon.length < this.maxTeamSize) { - // repeated popping from weighted shuffle is equivalent to repeated weighted sampling without replacement - const baseSpecies = shuffledBaseSpecies.pop()!.baseSpecies; + while (baseSpeciesPool.length && pokemon.length < this.maxTeamSize) { + const baseSpecies = this.sampleNoReplace(baseSpeciesPool); let species = this.dex.species.get(this.sample(pokemonPool[baseSpecies])); if (!species.exists) continue; diff --git a/data/random-teams.ts b/data/random-teams.ts index 37d92be5b7f9..c0e8009e74e4 100644 --- a/data/random-teams.ts +++ b/data/random-teams.ts @@ -1716,10 +1716,10 @@ export class RandomTeams { pokemonToExclude: RandomTeamsTypes.RandomSet[] = [], isMonotype = false, pokemonList: string[] - ): [{[k: string]: ID[]}, {baseSpecies: string, score: number}[]] { + ): [{[k: string]: ID[]}, string[]] { const exclude = pokemonToExclude.map(p => toID(p.species)); const pokemonPool: {[k: string]: ID[]} = {}; - const shuffledBaseSpecies = []; + const baseSpeciesPool = []; for (const pokemon of pokemonList) { let species = this.dex.species.get(pokemon); if (exclude.includes(species.id)) continue; @@ -1741,21 +1741,9 @@ export class RandomTeams { for (const baseSpecies of Object.keys(pokemonPool)) { // Squawkabilly has 4 formes, but only 2 functionally different formes, so only include it 1x const weight = (baseSpecies === 'Squawkabilly') ? 1 : Math.min(Math.ceil(pokemonPool[baseSpecies].length / 3), 3); - - /** - * Weighted random shuffle - * Uses the fact that for two uniform variables x1 and x2, x1^(1/w1) is larger than x2^(1/w2) - * with probability equal to w1/(w1+w2), which is what we want. See e.g. here https://arxiv.org/pdf/1012.0256.pdf, - * original paper is behind a paywall. - */ - const sortObject = { - baseSpecies: baseSpecies, - score: Math.pow(this.prng.next(), 1 / weight), - }; - shuffledBaseSpecies.push(sortObject); + for (let i = 0; i < weight; i++) baseSpeciesPool.push(baseSpecies); } - shuffledBaseSpecies.sort((a, b) => a.score - b.score); - return [pokemonPool, shuffledBaseSpecies]; + return [pokemonPool, baseSpeciesPool]; } randomSets: {[species: string]: RandomTeamsTypes.RandomSpeciesData} = require('./random-sets.json'); @@ -1787,13 +1775,12 @@ export class RandomTeams { let numMaxLevelPokemon = 0; const pokemonList = isDoubles ? Object.keys(this.randomDoublesSets) : Object.keys(this.randomSets); - const [pokemonPool, shuffledBaseSpecies] = this.getPokemonPool(type, pokemon, isMonotype, pokemonList); + const [pokemonPool, baseSpeciesPool] = this.getPokemonPool(type, pokemon, isMonotype, pokemonList); let leadsRemaining = this.format.gameType === 'doubles' ? 2 : 1; - while (shuffledBaseSpecies.length && pokemon.length < this.maxTeamSize) { - // repeated popping from weighted shuffle is equivalent to repeated weighted sampling without replacement - const baseSpecies = shuffledBaseSpecies.pop()!.baseSpecies; + while (baseSpeciesPool.length && pokemon.length < this.maxTeamSize) { + const baseSpecies = this.sampleNoReplace(baseSpeciesPool); let species = this.dex.species.get(this.sample(pokemonPool[baseSpecies])); if (!species.exists) continue; From cf5671ff44603a3c8baf2989ba39752222ffc531 Mon Sep 17 00:00:00 2001 From: livid washed <115855253+livid-washed@users.noreply.github.com> Date: Sun, 28 Jan 2024 12:36:41 +1100 Subject: [PATCH 6/8] unintended newlines --- data/mods/gen3/random-teams.ts | 1 - data/mods/gen5/random-teams.ts | 1 - data/mods/gen7/random-teams.ts | 1 - data/mods/gen8/random-teams.ts | 1 - data/random-teams.ts | 1 - 5 files changed, 5 deletions(-) diff --git a/data/mods/gen3/random-teams.ts b/data/mods/gen3/random-teams.ts index e35ffd1d3b26..507cacbae3fb 100644 --- a/data/mods/gen3/random-teams.ts +++ b/data/mods/gen3/random-teams.ts @@ -685,7 +685,6 @@ export class RandomGen3Teams extends RandomGen4Teams { const pokemonList = (this.gen === 3) ? Object.keys(this.randomSets) : Object.keys(this.randomData); const [pokemonPool, baseSpeciesPool] = this.getPokemonPool(type, pokemon, isMonotype, pokemonList); - while (baseSpeciesPool.length && pokemon.length < this.maxTeamSize) { const baseSpecies = this.sampleNoReplace(baseSpeciesPool); const species = this.dex.species.get(this.sample(pokemonPool[baseSpecies])); diff --git a/data/mods/gen5/random-teams.ts b/data/mods/gen5/random-teams.ts index 61460d0f46a4..7e9ed7263904 100644 --- a/data/mods/gen5/random-teams.ts +++ b/data/mods/gen5/random-teams.ts @@ -953,7 +953,6 @@ export class RandomGen5Teams extends RandomGen6Teams { const pokemonList = Object.keys(this.randomSets); const [pokemonPool, baseSpeciesPool] = this.getPokemonPool(type, pokemon, isMonotype, pokemonList); - while (baseSpeciesPool.length && pokemon.length < this.maxTeamSize) { const baseSpecies = this.sampleNoReplace(baseSpeciesPool); const species = this.dex.species.get(this.sample(pokemonPool[baseSpecies])); diff --git a/data/mods/gen7/random-teams.ts b/data/mods/gen7/random-teams.ts index 116aabe32db5..19c8143ece0b 100644 --- a/data/mods/gen7/random-teams.ts +++ b/data/mods/gen7/random-teams.ts @@ -1324,7 +1324,6 @@ export class RandomGen7Teams extends RandomGen8Teams { const pokemonList = Object.keys(this.randomSets); const [pokemonPool, baseSpeciesPool] = this.getPokemonPool(type, pokemon, isMonotype, pokemonList); - while (baseSpeciesPool.length && pokemon.length < this.maxTeamSize) { const baseSpecies = this.sampleNoReplace(baseSpeciesPool); const currentSpeciesPool: Species[] = []; diff --git a/data/mods/gen8/random-teams.ts b/data/mods/gen8/random-teams.ts index b5c7de684fec..6af307598d9d 100644 --- a/data/mods/gen8/random-teams.ts +++ b/data/mods/gen8/random-teams.ts @@ -2475,7 +2475,6 @@ export class RandomGen8Teams { } } const [pokemonPool, baseSpeciesPool] = this.getPokemonPool(type, pokemon, isMonotype, pokemonList); - while (baseSpeciesPool.length && pokemon.length < this.maxTeamSize) { const baseSpecies = this.sampleNoReplace(baseSpeciesPool); let species = this.dex.species.get(this.sample(pokemonPool[baseSpecies])); diff --git a/data/random-teams.ts b/data/random-teams.ts index c0e8009e74e4..1fa356891368 100644 --- a/data/random-teams.ts +++ b/data/random-teams.ts @@ -1778,7 +1778,6 @@ export class RandomTeams { const [pokemonPool, baseSpeciesPool] = this.getPokemonPool(type, pokemon, isMonotype, pokemonList); let leadsRemaining = this.format.gameType === 'doubles' ? 2 : 1; - while (baseSpeciesPool.length && pokemon.length < this.maxTeamSize) { const baseSpecies = this.sampleNoReplace(baseSpeciesPool); let species = this.dex.species.get(this.sample(pokemonPool[baseSpecies])); From f8493cddb9b29e3bdac35f75bf1ea01b5d9381c7 Mon Sep 17 00:00:00 2001 From: livid washed <115855253+livid-washed@users.noreply.github.com> Date: Sun, 28 Jan 2024 12:37:54 +1100 Subject: [PATCH 7/8] missed one --- data/mods/gen7/random-doubles-teams.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/data/mods/gen7/random-doubles-teams.ts b/data/mods/gen7/random-doubles-teams.ts index 87548364fe8e..55e0dc18ef01 100644 --- a/data/mods/gen7/random-doubles-teams.ts +++ b/data/mods/gen7/random-doubles-teams.ts @@ -1368,7 +1368,6 @@ export class RandomGen7DoublesTeams extends RandomGen8Teams { const pokemonList = Object.keys(this.randomDoublesData); const [pokemonPool, baseSpeciesPool] = this.getPokemonPool(type, pokemon, isMonotype, pokemonList); - while (baseSpeciesPool.length && pokemon.length < this.maxTeamSize) { const baseSpecies = this.sampleNoReplace(baseSpeciesPool); const currentSpeciesPool: Species[] = []; From 78856d8bfbaf6b2e6ee6c14abac4edaf260b4d64 Mon Sep 17 00:00:00 2001 From: livid washed <115855253+livid-washed@users.noreply.github.com> Date: Mon, 29 Jan 2024 13:47:15 +1100 Subject: [PATCH 8/8] Removing the final newline --- data/mods/gen1/random-teams.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/data/mods/gen1/random-teams.ts b/data/mods/gen1/random-teams.ts index 58c780e51b98..8a7216c72885 100644 --- a/data/mods/gen1/random-teams.ts +++ b/data/mods/gen1/random-teams.ts @@ -125,7 +125,6 @@ export class RandomGen1Teams extends RandomGen2Teams { let numMaxLevelPokemon = 0; const pokemonPool = Object.keys(this.getPokemonPool(type, pokemon, isMonotype, Object.keys(this.randomData))[0]); - while (pokemonPool.length && pokemon.length < this.maxTeamSize) { const species = this.dex.species.get(this.sampleNoReplace(pokemonPool)); if (!species.exists) continue;