@pkmn/randoms 0.4.21 → 0.4.25
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/gen1.js +36 -62
- package/build/gen1.js.map +1 -1
- package/build/gen4.js +2 -0
- package/build/gen4.js.map +1 -1
- package/build/gen5.js +2 -0
- package/build/gen5.js.map +1 -1
- package/build/gen6.js +2 -0
- package/build/gen6.js.map +1 -1
- package/build/gen7.js +1 -0
- package/build/gen7.js.map +1 -1
- package/build/gen8.js +31 -4
- package/build/gen8.js.map +1 -1
- package/package.json +2 -2
- package/src/gen1.ts +38 -64
- package/src/gen4.ts +1 -0
- package/src/gen5.ts +1 -0
- package/src/gen6.ts +1 -0
- package/src/gen7.ts +1 -0
- package/src/gen8.ts +24 -5
package/src/gen1.ts
CHANGED
|
@@ -21,37 +21,10 @@ export class RandomGen1Teams extends RandomGen2Teams {
|
|
|
21
21
|
randomCCTeam() {
|
|
22
22
|
const team = [];
|
|
23
23
|
|
|
24
|
-
const
|
|
25
|
-
const formes: string[][] = [[], [], [], [], [], []];
|
|
26
|
-
|
|
27
|
-
// Pick six random Pokémon, no repeats.
|
|
28
|
-
let num: number;
|
|
29
|
-
for (let i = 0; i < this.maxTeamSize; i++) {
|
|
30
|
-
do {
|
|
31
|
-
num = this.random(151) + 1;
|
|
32
|
-
} while (num in hasDexNumber);
|
|
33
|
-
hasDexNumber[num] = i;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
let formeCounter = 0;
|
|
37
|
-
for (const species of this.dex.species.all()) {
|
|
38
|
-
if (!(species.num in hasDexNumber)) continue;
|
|
39
|
-
|
|
40
|
-
if (this.forceMonotype && !species.types.includes(this.forceMonotype)) continue;
|
|
24
|
+
const randomN = this.randomNPokemon(this.maxTeamSize, this.forceMonotype);
|
|
41
25
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
formes[hasDexNumber[species.num]].push(species.name);
|
|
45
|
-
if (++formeCounter >= 6) {
|
|
46
|
-
// Gen 1 had no alternate formes, so we can break out of the loop already.
|
|
47
|
-
break;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
for (let i = 0; i < this.maxTeamSize; i++) {
|
|
52
|
-
// Choose forme.
|
|
53
|
-
const poke = this.sample(formes[i]);
|
|
54
|
-
const species = this.dex.species.get(poke);
|
|
26
|
+
for (const pokemon of randomN) {
|
|
27
|
+
const species = this.dex.species.get(pokemon);
|
|
55
28
|
const learnset = this.dex.species.getLearnset(species.id);
|
|
56
29
|
|
|
57
30
|
// Level balance: calculate directly from stats rather than using some silly lookup table.
|
|
@@ -113,7 +86,7 @@ export class RandomGen1Teams extends RandomGen2Teams {
|
|
|
113
86
|
}
|
|
114
87
|
|
|
115
88
|
team.push({
|
|
116
|
-
name:
|
|
89
|
+
name: species.baseSpecies,
|
|
117
90
|
species: species.name,
|
|
118
91
|
moves: this.multipleSamplesNoReplace(pool, 4),
|
|
119
92
|
gender: false,
|
|
@@ -134,22 +107,21 @@ export class RandomGen1Teams extends RandomGen2Teams {
|
|
|
134
107
|
// Random team generation for Gen 1 Random Battles.
|
|
135
108
|
randomTeam() {
|
|
136
109
|
// Get what we need ready.
|
|
137
|
-
const pokemon = [];
|
|
138
110
|
const seed = this.prng.seed;
|
|
111
|
+
const ruleTable = this.dex.formats.getRuleTable(this.format);
|
|
112
|
+
const pokemon: RandomTeamsTypes.RandomSet[] = [];
|
|
113
|
+
|
|
114
|
+
// For Monotype
|
|
115
|
+
const isMonotype = !!this.forceMonotype || ruleTable.has('sametypeclause');
|
|
116
|
+
const typePool = this.dex.types.names();
|
|
117
|
+
const type = this.forceMonotype || this.sample(typePool);
|
|
139
118
|
|
|
119
|
+
/** Pokémon that are not wholly incompatible with the team, but still pretty bad */
|
|
120
|
+
const rejectedButNotInvalidPool: string[] = [];
|
|
140
121
|
const handicapMons = ['magikarp', 'weedle', 'kakuna', 'caterpie', 'metapod'];
|
|
141
122
|
const nuTiers = ['UU', 'UUBL', 'NFE', 'LC', 'NU'];
|
|
142
123
|
const uuTiers = ['NFE', 'UU', 'UUBL', 'NU'];
|
|
143
124
|
|
|
144
|
-
const pokemonPool = [];
|
|
145
|
-
/** Pokémon that are not wholly incompatible with the team, but still pretty bad */
|
|
146
|
-
const rejectedButNotInvalidPool = [];
|
|
147
|
-
for (const species of this.dex.species.all()) {
|
|
148
|
-
if (!species.isNonstandard && species.randomBattleMoves) {
|
|
149
|
-
pokemonPool.push(species.id);
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
125
|
// Now let's store what we are getting.
|
|
154
126
|
const typeCount: {[k: string]: number} = {};
|
|
155
127
|
const weaknessCount: {[k: string]: number} = {Electric: 0, Psychic: 0, Water: 0, Ice: 0, Ground: 0};
|
|
@@ -157,9 +129,10 @@ export class RandomGen1Teams extends RandomGen2Teams {
|
|
|
157
129
|
let nuCount = 0;
|
|
158
130
|
let hasShitmon = false;
|
|
159
131
|
|
|
132
|
+
const pokemonPool = this.getPokemonPool(type, pokemon, isMonotype);
|
|
160
133
|
while (pokemonPool.length && pokemon.length < this.maxTeamSize) {
|
|
161
134
|
const species = this.dex.species.get(this.sampleNoReplace(pokemonPool));
|
|
162
|
-
if (!species.exists) continue;
|
|
135
|
+
if (!species.exists || !species.randomBattleMoves) continue;
|
|
163
136
|
// Only one Ditto is allowed per battle in Generation 1,
|
|
164
137
|
// as it can cause an endless battle if two Dittos are forced
|
|
165
138
|
// to face each other.
|
|
@@ -172,8 +145,6 @@ export class RandomGen1Teams extends RandomGen2Teams {
|
|
|
172
145
|
continue;
|
|
173
146
|
}
|
|
174
147
|
|
|
175
|
-
if (this.forceMonotype && !species.types.includes(this.forceMonotype)) continue;
|
|
176
|
-
|
|
177
148
|
// Dynamically scale limits for different team sizes. The default and minimum value is 1.
|
|
178
149
|
const limitFactor = Math.round(this.maxTeamSize / 6) || 1;
|
|
179
150
|
|
|
@@ -198,31 +169,34 @@ export class RandomGen1Teams extends RandomGen2Teams {
|
|
|
198
169
|
|
|
199
170
|
let skip = false;
|
|
200
171
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
(typeCount[
|
|
206
|
-
|
|
207
|
-
|
|
172
|
+
if (!isMonotype && !this.forceMonotype) {
|
|
173
|
+
// Limit 2 of any type as well. Diversity and minor weakness count.
|
|
174
|
+
// The second of a same type has halved chance of being added.
|
|
175
|
+
for (const typeName of species.types) {
|
|
176
|
+
if (typeCount[typeName] >= 2 * limitFactor ||
|
|
177
|
+
(typeCount[typeName] >= 1 * limitFactor && this.randomChance(1, 2) && pokemonPool.length > 1)) {
|
|
178
|
+
skip = true;
|
|
179
|
+
break;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (skip) {
|
|
184
|
+
rejectedButNotInvalidPool.push(species.id);
|
|
185
|
+
continue;
|
|
208
186
|
}
|
|
209
|
-
}
|
|
210
|
-
if (skip) {
|
|
211
|
-
rejectedButNotInvalidPool.push(species.id);
|
|
212
|
-
continue;
|
|
213
187
|
}
|
|
214
188
|
|
|
215
189
|
// We need a weakness count of spammable attacks to avoid being swept by those.
|
|
216
190
|
// Spammable attacks are: Thunderbolt, Psychic, Surf, Blizzard, Earthquake.
|
|
217
191
|
const pokemonWeaknesses = [];
|
|
218
|
-
for (const
|
|
219
|
-
const increaseCount = this.dex.getImmunity(
|
|
192
|
+
for (const typeName in weaknessCount) {
|
|
193
|
+
const increaseCount = this.dex.getImmunity(typeName, species) && this.dex.getEffectiveness(typeName, species) > 0;
|
|
220
194
|
if (!increaseCount) continue;
|
|
221
|
-
if (weaknessCount[
|
|
195
|
+
if (weaknessCount[typeName] >= 2 * limitFactor) {
|
|
222
196
|
skip = true;
|
|
223
197
|
break;
|
|
224
198
|
}
|
|
225
|
-
pokemonWeaknesses.push(
|
|
199
|
+
pokemonWeaknesses.push(typeName);
|
|
226
200
|
}
|
|
227
201
|
|
|
228
202
|
if (skip) {
|
|
@@ -235,11 +209,11 @@ export class RandomGen1Teams extends RandomGen2Teams {
|
|
|
235
209
|
|
|
236
210
|
// Now let's increase the counters.
|
|
237
211
|
// Type counter.
|
|
238
|
-
for (const
|
|
239
|
-
if (typeCount[
|
|
240
|
-
typeCount[
|
|
212
|
+
for (const typeName of species.types) {
|
|
213
|
+
if (typeCount[typeName]) {
|
|
214
|
+
typeCount[typeName]++;
|
|
241
215
|
} else {
|
|
242
|
-
typeCount[
|
|
216
|
+
typeCount[typeName] = 1;
|
|
243
217
|
}
|
|
244
218
|
}
|
|
245
219
|
|
|
@@ -268,7 +242,7 @@ export class RandomGen1Teams extends RandomGen2Teams {
|
|
|
268
242
|
pokemon.push(this.randomSet(species));
|
|
269
243
|
}
|
|
270
244
|
|
|
271
|
-
if (pokemon.length < this.maxTeamSize && pokemon.length < 12 && !
|
|
245
|
+
if (pokemon.length < this.maxTeamSize && pokemon.length < 12 && !isMonotype) {
|
|
272
246
|
throw new Error(`Could not build a random team for ${this.format} (seed=${seed})`);
|
|
273
247
|
}
|
|
274
248
|
|
package/src/gen4.ts
CHANGED
|
@@ -390,6 +390,7 @@ export class RandomGen4Teams extends RandomGen5Teams {
|
|
|
390
390
|
isLead: boolean,
|
|
391
391
|
): string | undefined {
|
|
392
392
|
if (species.requiredItem) return species.requiredItem;
|
|
393
|
+
if (species.requiredItems) return this.sample(species.requiredItems);
|
|
393
394
|
if (species.name === 'Farfetch\u2019d') return 'Stick';
|
|
394
395
|
if (species.name === 'Marowak') return 'Thick Club';
|
|
395
396
|
if (species.name === 'Shedinja' || species.name === 'Smeargle') return 'Focus Sash';
|
package/src/gen5.ts
CHANGED
|
@@ -330,6 +330,7 @@ export class RandomGen5Teams extends RandomGen6Teams {
|
|
|
330
330
|
isLead: boolean
|
|
331
331
|
): string | undefined {
|
|
332
332
|
if (species.requiredItem) return species.requiredItem;
|
|
333
|
+
if (species.requiredItems) return this.sample(species.requiredItems);
|
|
333
334
|
|
|
334
335
|
if (species.name === 'Marowak') return 'Thick Club';
|
|
335
336
|
if (species.name === 'Farfetch\u2019d') return 'Stick';
|
package/src/gen6.ts
CHANGED
|
@@ -581,6 +581,7 @@ export class RandomGen6Teams extends RandomGen7Teams {
|
|
|
581
581
|
isLead: boolean
|
|
582
582
|
): string | undefined {
|
|
583
583
|
if (species.requiredItem) return species.requiredItem;
|
|
584
|
+
if (species.requiredItems) return this.sample(species.requiredItems);
|
|
584
585
|
|
|
585
586
|
// First, the extra high-priority items
|
|
586
587
|
if (species.name === 'Marowak') return 'Thick Club';
|
package/src/gen7.ts
CHANGED
|
@@ -517,6 +517,7 @@ export class RandomGen7Teams extends RandomTeams {
|
|
|
517
517
|
return {cull: (
|
|
518
518
|
counter.get('Physical') + counter.get('Special') < 2 ||
|
|
519
519
|
hasRestTalk ||
|
|
520
|
+
moves.has('rest') ||
|
|
520
521
|
(!types.has('Water') && !counter.get('Water'))
|
|
521
522
|
)};
|
|
522
523
|
case 'sunnyday':
|
package/src/gen8.ts
CHANGED
|
@@ -1758,12 +1758,19 @@ export class RandomTeams {
|
|
|
1758
1758
|
|
|
1759
1759
|
const moves = new Set<string>();
|
|
1760
1760
|
let counter: MoveCounter;
|
|
1761
|
+
// This is just for BDSP Unown;
|
|
1762
|
+
// it can be removed from this file if BDSP gets its own random-teams file in the future.
|
|
1763
|
+
let hasHiddenPower = false;
|
|
1761
1764
|
|
|
1762
1765
|
do {
|
|
1763
1766
|
// Choose next 4 moves from learnset/viable moves and add them to moves list:
|
|
1764
1767
|
const pool = (movePool.length ? movePool : rejectedPool);
|
|
1765
1768
|
while (moves.size < 4 && pool.length) {
|
|
1766
1769
|
const moveid = this.sampleNoReplace(pool);
|
|
1770
|
+
if (moveid.startsWith('hiddenpower')) {
|
|
1771
|
+
if (hasHiddenPower) continue;
|
|
1772
|
+
hasHiddenPower = true;
|
|
1773
|
+
}
|
|
1767
1774
|
moves.add(moveid);
|
|
1768
1775
|
}
|
|
1769
1776
|
|
|
@@ -1854,11 +1861,13 @@ export class RandomTeams {
|
|
|
1854
1861
|
|
|
1855
1862
|
// Remove rejected moves from the move list
|
|
1856
1863
|
if (cull && movePool.length) {
|
|
1864
|
+
if (moveid.startsWith('hiddenpower')) hasHiddenPower = false;
|
|
1857
1865
|
if (move.category !== 'Status' && !move.damage) rejectedPool.push(moveid);
|
|
1858
1866
|
moves.delete(moveid);
|
|
1859
1867
|
break;
|
|
1860
1868
|
}
|
|
1861
1869
|
if (cull && rejectedPool.length) {
|
|
1870
|
+
if (moveid.startsWith('hiddenpower')) hasHiddenPower = false;
|
|
1862
1871
|
moves.delete(moveid);
|
|
1863
1872
|
break;
|
|
1864
1873
|
}
|
|
@@ -1962,13 +1971,15 @@ export class RandomTeams {
|
|
|
1962
1971
|
if (item === 'Leftovers' && types.has('Poison')) {
|
|
1963
1972
|
item = 'Black Sludge';
|
|
1964
1973
|
}
|
|
1965
|
-
if (species.baseSpecies === 'Pikachu' && !gmax) {
|
|
1974
|
+
if (species.baseSpecies === 'Pikachu' && !gmax && this.dex.currentMod !== 'gen8bdsp') {
|
|
1966
1975
|
forme = 'Pikachu' + this.sample(['', '-Original', '-Hoenn', '-Sinnoh', '-Unova', '-Kalos', '-Alola', '-Partner', '-World']);
|
|
1967
1976
|
}
|
|
1968
1977
|
|
|
1969
1978
|
let level: number;
|
|
1979
|
+
// doubles levelling
|
|
1970
1980
|
if (isDoubles && species.randomDoubleBattleLevel) {
|
|
1971
1981
|
level = species.randomDoubleBattleLevel;
|
|
1982
|
+
// No Dmax levelling
|
|
1972
1983
|
} else if (isNoDynamax) {
|
|
1973
1984
|
const tier = species.name.endsWith('-Gmax') ? this.dex.species.get(species.changesFrom).tier : species.tier;
|
|
1974
1985
|
const tierScale: {[k: string]: number} = {
|
|
@@ -1994,13 +2005,18 @@ export class RandomTeams {
|
|
|
1994
2005
|
decidueye: 87, noivern: 85, magnezone: 82, slowking: 81,
|
|
1995
2006
|
};
|
|
1996
2007
|
level = customScale[species.id] || tierScale[tier];
|
|
2008
|
+
// BDSP tier levelling
|
|
2009
|
+
} else if (this.dex.currentMod === 'gen8bdsp') {
|
|
2010
|
+
// TODO: figure out BDSP levelling based on the in-room poll
|
|
2011
|
+
level = 80;
|
|
2012
|
+
// Arbitrary levelling base on data files (typically winrate-influenced)
|
|
1997
2013
|
} else if (species.randomBattleLevel) {
|
|
1998
2014
|
level = species.randomBattleLevel;
|
|
2015
|
+
// Default to level 80
|
|
1999
2016
|
} else {
|
|
2000
2017
|
level = 80;
|
|
2001
2018
|
}
|
|
2002
2019
|
|
|
2003
|
-
|
|
2004
2020
|
// Prepare optimal HP
|
|
2005
2021
|
const srImmunity = ability === 'Magic Guard' || item === 'Heavy-Duty Boots';
|
|
2006
2022
|
const srWeakness = srImmunity ? 0 : this.dex.getEffectiveness('Rock', species);
|
|
@@ -2072,6 +2088,7 @@ export class RandomTeams {
|
|
|
2072
2088
|
const pokemonPool = [];
|
|
2073
2089
|
for (let species of this.dex.species.all()) {
|
|
2074
2090
|
if (species.gen > this.gen || exclude.includes(species.id)) continue;
|
|
2091
|
+
if (this.dex.currentMod === 'gen8bdsp' && species.gen > 4) continue;
|
|
2075
2092
|
if (isMonotype) {
|
|
2076
2093
|
if (!species.types.includes(type)) continue;
|
|
2077
2094
|
if (typeof species.battleOnly === 'string') {
|
|
@@ -2112,9 +2129,9 @@ export class RandomTeams {
|
|
|
2112
2129
|
|
|
2113
2130
|
// Check if the forme has moves for random battle
|
|
2114
2131
|
if (this.format.gameType === 'singles') {
|
|
2115
|
-
if (!species.randomBattleMoves) continue;
|
|
2132
|
+
if (!species.randomBattleMoves?.length) continue;
|
|
2116
2133
|
} else {
|
|
2117
|
-
if (!species.randomDoubleBattleMoves) continue;
|
|
2134
|
+
if (!species.randomDoubleBattleMoves?.length) continue;
|
|
2118
2135
|
}
|
|
2119
2136
|
|
|
2120
2137
|
// Limit to one of each species (Species Clause)
|
|
@@ -2163,7 +2180,10 @@ export class RandomTeams {
|
|
|
2163
2180
|
const limitFactor = Math.round(this.maxTeamSize / 6) || 1;
|
|
2164
2181
|
|
|
2165
2182
|
// Limit one Pokemon per tier, two for Monotype
|
|
2183
|
+
// This limitation is not applied to BD/SP team generation, because tiering for BD/SP is not yet complete,
|
|
2184
|
+
// meaning that most Pokémon are in OU.
|
|
2166
2185
|
if (
|
|
2186
|
+
this.dex.currentMod !== 'gen8bdsp' &&
|
|
2167
2187
|
(tierCount[tier] >= (this.forceMonotype || isMonotype ? 2 : 1) * limitFactor) &&
|
|
2168
2188
|
!this.randomChance(1, Math.pow(5, tierCount[tier]))
|
|
2169
2189
|
) {
|
|
@@ -2193,7 +2213,6 @@ export class RandomTeams {
|
|
|
2193
2213
|
|
|
2194
2214
|
// Okay, the set passes, add it to our team
|
|
2195
2215
|
pokemon.push(set);
|
|
2196
|
-
|
|
2197
2216
|
if (pokemon.length === this.maxTeamSize) {
|
|
2198
2217
|
// Set Zoroark's level to be the same as the last Pokemon
|
|
2199
2218
|
const illusion = teamDetails.illusion;
|