@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/src/gen1.ts CHANGED
@@ -21,37 +21,10 @@ export class RandomGen1Teams extends RandomGen2Teams {
21
21
  randomCCTeam() {
22
22
  const team = [];
23
23
 
24
- const hasDexNumber: {[k: string]: number} = {};
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
- const learnset = this.dex.species.getLearnset(species.id);
43
- if (!learnset || species.forme) continue;
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: poke,
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
- // Limit 2 of any type as well. Diversity and minor weakness count.
202
- // The second of a same type has halved chance of being added.
203
- for (const type of species.types) {
204
- if (typeCount[type] >= 2 * limitFactor ||
205
- (typeCount[type] >= 1 * limitFactor && this.randomChance(1, 2) && pokemonPool.length > 1)) {
206
- skip = true;
207
- break;
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 type in weaknessCount) {
219
- const increaseCount = this.dex.getImmunity(type, species) && this.dex.getEffectiveness(type, species) > 0;
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[type] >= 2 * limitFactor) {
195
+ if (weaknessCount[typeName] >= 2 * limitFactor) {
222
196
  skip = true;
223
197
  break;
224
198
  }
225
- pokemonWeaknesses.push(type);
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 type of species.types) {
239
- if (typeCount[type]) {
240
- typeCount[type]++;
212
+ for (const typeName of species.types) {
213
+ if (typeCount[typeName]) {
214
+ typeCount[typeName]++;
241
215
  } else {
242
- typeCount[type] = 1;
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 && !this.forceMonotype) {
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;