@pkmn/randoms 0.4.19 → 0.4.23

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,38 +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[][] = [[], [], [], [], [], []];
24
+ const randomN = this.randomNPokemon(this.maxTeamSize, this.forceMonotype);
26
25
 
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 id in this.dex.data.Pokedex) {
38
- if (!(this.dex.data.Pokedex[id].num in hasDexNumber)) continue;
39
-
40
- const species = this.dex.species.get(id);
41
- if (this.forceMonotype && !species.types.includes(this.forceMonotype)) continue;
42
-
43
- const learnset = this.dex.species.getLearnset(id as ID);
44
- if (!learnset || species.forme) continue;
45
- formes[hasDexNumber[species.num]].push(species.name);
46
- if (++formeCounter >= 6) {
47
- // Gen 1 had no alternate formes, so we can break out of the loop already.
48
- break;
49
- }
50
- }
51
-
52
- for (let i = 0; i < this.maxTeamSize; i++) {
53
- // Choose forme.
54
- const poke = this.sample(formes[i]);
55
- const species = this.dex.species.get(poke);
26
+ for (const pokemon of randomN) {
27
+ const species = this.dex.species.get(pokemon);
56
28
  const learnset = this.dex.species.getLearnset(species.id);
57
29
 
58
30
  // Level balance: calculate directly from stats rather than using some silly lookup table.
@@ -114,11 +86,11 @@ export class RandomGen1Teams extends RandomGen2Teams {
114
86
  }
115
87
 
116
88
  team.push({
117
- name: poke,
89
+ name: species.baseSpecies,
118
90
  species: species.name,
119
91
  moves: this.multipleSamplesNoReplace(pool, 4),
120
92
  gender: false,
121
- ability: 'None',
93
+ ability: 'No Ability',
122
94
  evs: evs,
123
95
  ivs: ivs,
124
96
  item: '',
@@ -135,23 +107,21 @@ export class RandomGen1Teams extends RandomGen2Teams {
135
107
  // Random team generation for Gen 1 Random Battles.
136
108
  randomTeam() {
137
109
  // Get what we need ready.
138
- const pokemon = [];
139
110
  const seed = this.prng.seed;
111
+ const ruleTable = this.dex.formats.getRuleTable(this.format);
112
+ const pokemon: RandomTeamsTypes.RandomSet[] = [];
140
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);
118
+
119
+ /** Pokémon that are not wholly incompatible with the team, but still pretty bad */
120
+ const rejectedButNotInvalidPool: string[] = [];
141
121
  const handicapMons = ['magikarp', 'weedle', 'kakuna', 'caterpie', 'metapod'];
142
122
  const nuTiers = ['UU', 'UUBL', 'NFE', 'LC', 'NU'];
143
123
  const uuTiers = ['NFE', 'UU', 'UUBL', 'NU'];
144
124
 
145
- const pokemonPool = [];
146
- /** Pokémon that are not wholly incompatible with the team, but still pretty bad */
147
- const rejectedButNotInvalidPool = [];
148
- for (const id in this.dex.data.FormatsData) {
149
- const species = this.dex.species.get(id);
150
- if (!species.isNonstandard && species.randomBattleMoves) {
151
- pokemonPool.push(id);
152
- }
153
- }
154
-
155
125
  // Now let's store what we are getting.
156
126
  const typeCount: {[k: string]: number} = {};
157
127
  const weaknessCount: {[k: string]: number} = {Electric: 0, Psychic: 0, Water: 0, Ice: 0, Ground: 0};
@@ -159,9 +129,10 @@ export class RandomGen1Teams extends RandomGen2Teams {
159
129
  let nuCount = 0;
160
130
  let hasShitmon = false;
161
131
 
132
+ const pokemonPool = this.getPokemonPool(type, pokemon, isMonotype);
162
133
  while (pokemonPool.length && pokemon.length < this.maxTeamSize) {
163
134
  const species = this.dex.species.get(this.sampleNoReplace(pokemonPool));
164
- if (!species.exists) continue;
135
+ if (!species.exists || !species.randomBattleMoves) continue;
165
136
  // Only one Ditto is allowed per battle in Generation 1,
166
137
  // as it can cause an endless battle if two Dittos are forced
167
138
  // to face each other.
@@ -174,8 +145,6 @@ export class RandomGen1Teams extends RandomGen2Teams {
174
145
  continue;
175
146
  }
176
147
 
177
- if (this.forceMonotype && !species.types.includes(this.forceMonotype)) continue;
178
-
179
148
  // Dynamically scale limits for different team sizes. The default and minimum value is 1.
180
149
  const limitFactor = Math.round(this.maxTeamSize / 6) || 1;
181
150
 
@@ -200,31 +169,34 @@ export class RandomGen1Teams extends RandomGen2Teams {
200
169
 
201
170
  let skip = false;
202
171
 
203
- // Limit 2 of any type as well. Diversity and minor weakness count.
204
- // The second of a same type has halved chance of being added.
205
- for (const type of species.types) {
206
- if (typeCount[type] >= 2 * limitFactor ||
207
- (typeCount[type] >= 1 * limitFactor && this.randomChance(1, 2) && pokemonPool.length > 1)) {
208
- skip = true;
209
- 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;
210
186
  }
211
- }
212
- if (skip) {
213
- rejectedButNotInvalidPool.push(species.id);
214
- continue;
215
187
  }
216
188
 
217
189
  // We need a weakness count of spammable attacks to avoid being swept by those.
218
190
  // Spammable attacks are: Thunderbolt, Psychic, Surf, Blizzard, Earthquake.
219
191
  const pokemonWeaknesses = [];
220
- for (const type in weaknessCount) {
221
- 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;
222
194
  if (!increaseCount) continue;
223
- if (weaknessCount[type] >= 2 * limitFactor) {
195
+ if (weaknessCount[typeName] >= 2 * limitFactor) {
224
196
  skip = true;
225
197
  break;
226
198
  }
227
- pokemonWeaknesses.push(type);
199
+ pokemonWeaknesses.push(typeName);
228
200
  }
229
201
 
230
202
  if (skip) {
@@ -237,11 +209,11 @@ export class RandomGen1Teams extends RandomGen2Teams {
237
209
 
238
210
  // Now let's increase the counters.
239
211
  // Type counter.
240
- for (const type of species.types) {
241
- if (typeCount[type]) {
242
- typeCount[type]++;
212
+ for (const typeName of species.types) {
213
+ if (typeCount[typeName]) {
214
+ typeCount[typeName]++;
243
215
  } else {
244
- typeCount[type] = 1;
216
+ typeCount[typeName] = 1;
245
217
  }
246
218
  }
247
219
 
@@ -270,7 +242,7 @@ export class RandomGen1Teams extends RandomGen2Teams {
270
242
  pokemon.push(this.randomSet(species));
271
243
  }
272
244
 
273
- if (pokemon.length < this.maxTeamSize && pokemon.length < 12 && !this.forceMonotype) {
245
+ if (pokemon.length < this.maxTeamSize && pokemon.length < 12 && !isMonotype) {
274
246
  throw new Error(`Could not build a random team for ${this.format} (seed=${seed})`);
275
247
  }
276
248
 
@@ -389,7 +361,7 @@ export class RandomGen1Teams extends RandomGen2Teams {
389
361
  name: species.name,
390
362
  species: species.name,
391
363
  moves: Array.from(moves),
392
- ability: 'None',
364
+ ability: 'No Ability',
393
365
  evs: {hp: 255, atk: 255, def: 255, spa: 255, spd: 255, spe: 255},
394
366
  ivs: {hp: 30, atk: 30, def: 30, spa: 30, spd: 30, spe: 30},
395
367
  item: '',
@@ -402,7 +374,7 @@ export class RandomGen1Teams extends RandomGen2Teams {
402
374
  randomHCTeam(): PokemonSet[] {
403
375
  const team = [];
404
376
 
405
- const movePool = Object.keys(this.dex.data.Moves);
377
+ const movePool = [...this.dex.moves.all()];
406
378
  const typesPool = ['Bird', ...this.dex.types.names()];
407
379
 
408
380
  const randomN = this.randomNPokemon(this.maxTeamSize);
@@ -435,10 +407,9 @@ export class RandomGen1Teams extends RandomGen2Teams {
435
407
  // Random unique moves
436
408
  const moves = [];
437
409
  do {
438
- const moveid = this.sampleNoReplace(movePool);
439
- const move = this.dex.moves.get(moveid);
410
+ const move = this.sampleNoReplace(movePool);
440
411
  if (move.gen <= this.gen && !move.isNonstandard && !move.name.startsWith('Hidden Power ')) {
441
- moves.push(moveid);
412
+ moves.push(move.id);
442
413
  }
443
414
  } while (moves.length < 4);
444
415
 
@@ -499,7 +470,7 @@ export class RandomGen1Teams extends RandomGen2Teams {
499
470
  species: species.name,
500
471
  gender: species.gender,
501
472
  item: '',
502
- ability: 'None',
473
+ ability: 'No Ability',
503
474
  moves,
504
475
  evs,
505
476
  ivs,
package/src/gen2.ts CHANGED
@@ -156,7 +156,7 @@ export class RandomGen2Teams extends RandomGen3Teams {
156
156
  randomSet(species: string | Species, teamDetails: RandomTeamsTypes.TeamDetails = {}): RandomTeamsTypes.RandomSet {
157
157
  species = this.dex.species.get(species);
158
158
 
159
- const movePool = (species.randomBattleMoves || Object.keys(this.dex.data.Learnsets[species.id]!.learnset!)).slice();
159
+ const movePool = (species.randomBattleMoves || Object.keys(this.dex.species.getLearnset(species.id)!)).slice();
160
160
  const rejectedPool: string[] = [];
161
161
  const moves = new Set<string>();
162
162
 
@@ -309,7 +309,7 @@ export class RandomGen2Teams extends RandomGen3Teams {
309
309
  name: species.name,
310
310
  species: species.name,
311
311
  moves: Array.from(moves),
312
- ability: 'None',
312
+ ability: 'No Ability',
313
313
  evs: {hp: 255, atk: 255, def: 255, spa: 255, spd: 255, spe: 255},
314
314
  ivs,
315
315
  item: this.getItem('None', types, moves, counter, species),
package/src/gen3.ts CHANGED
@@ -335,7 +335,7 @@ export class RandomGen3Teams extends RandomGen4Teams {
335
335
 
336
336
  if (typeof species.battleOnly === 'string') forme = species.battleOnly;
337
337
 
338
- const movePool = (species.randomBattleMoves || Object.keys(this.dex.data.Learnsets[species.id]!.learnset!)).slice();
338
+ const movePool = (species.randomBattleMoves || Object.keys(this.dex.species.getLearnset(species.id)!)).slice();
339
339
  const rejectedPool = [];
340
340
  const moves = new Set<string>();
341
341
  let ability = '';
package/src/gen4.ts CHANGED
@@ -524,7 +524,7 @@ export class RandomGen4Teams extends RandomGen5Teams {
524
524
  forme = this.sample([species.name].concat(species.cosmeticFormes));
525
525
  }
526
526
 
527
- const movePool = (species.randomBattleMoves || Object.keys(this.dex.data.Learnsets[species.id].learnset!)).slice();
527
+ const movePool = (species.randomBattleMoves || Object.keys(this.dex.species.getLearnset(species.id)!)).slice();
528
528
  const rejectedPool: string[] = [];
529
529
  const moves = new Set<string>();
530
530
  let ability = '';
package/src/gen5.ts CHANGED
@@ -462,7 +462,7 @@ export class RandomGen5Teams extends RandomGen6Teams {
462
462
  forme = this.sample([species.name].concat(species.cosmeticFormes));
463
463
  }
464
464
 
465
- const movePool = (species.randomBattleMoves || Object.keys(this.dex.data.Learnsets[species.id]!.learnset!)).slice();
465
+ const movePool = (species.randomBattleMoves || Object.keys(this.dex.species.getLearnset(species.id)!)).slice();
466
466
  const rejectedPool = [];
467
467
  const moves = new Set<string>();
468
468
  let ability = '';
package/src/gen6.ts CHANGED
@@ -1,4 +1,4 @@
1
- import {RandomGen7Teams} from './gen7';
1
+ import {BattleFactorySpecies, RandomGen7Teams} from './gen7';
2
2
  import {MoveCounter, TeamData} from './gen8';
3
3
  import {Utils} from './utils';
4
4
  import {
@@ -781,7 +781,7 @@ export class RandomGen6Teams extends RandomGen7Teams {
781
781
  forme = this.sample([species.name].concat(species.cosmeticFormes));
782
782
  }
783
783
 
784
- const movePool = (species.randomBattleMoves || Object.keys(this.dex.data.Learnsets[species.id]!.learnset!)).slice();
784
+ const movePool = (species.randomBattleMoves || Object.keys(this.dex.species.getLearnset(species.id)!)).slice();
785
785
  const rejectedPool = [];
786
786
  let ability = '';
787
787
 
@@ -1112,7 +1112,7 @@ export class RandomGen6Teams extends RandomGen7Teams {
1112
1112
  };
1113
1113
  }
1114
1114
 
1115
- randomFactorySets: AnyObject = {};
1115
+ randomFactorySets: {[format: string]: {[species: string]: BattleFactorySpecies}} = {};
1116
1116
 
1117
1117
  randomFactorySet(
1118
1118
  species: Species,
@@ -1214,7 +1214,7 @@ export class RandomGen6Teams extends RandomGen7Teams {
1214
1214
  const pokemonPool = Object.keys(this.randomFactorySets[chosenTier]);
1215
1215
 
1216
1216
  const teamData: TeamData = {
1217
- typeCount: {}, typeComboCount: {}, baseFormes: {}, megaCount: 0, has: {}, forceResult: forceResult,
1217
+ typeCount: {}, typeComboCount: {}, baseFormes: {}, megaCount: 0, has: {}, forceResult,
1218
1218
  weaknesses: {}, resistances: {},
1219
1219
  };
1220
1220
  const requiredMoveFamilies = ['hazardSet', 'hazardClear'];
package/src/gen7.ts CHANGED
@@ -10,9 +10,23 @@ import {
10
10
  PlayerOptions,
11
11
  RandomTeamsTypes,
12
12
  Species,
13
+ StatsTable,
13
14
  toID,
14
15
  } from '@pkmn/sim';
15
16
 
17
+ export interface BattleFactorySpecies {
18
+ flags: {megaOnly?: 1, zmoveOnly?: 1, limEevee?: 1};
19
+ sets: BattleFactorySet[];
20
+ }
21
+ interface BattleFactorySet {
22
+ species: string;
23
+ item: string;
24
+ ability: string;
25
+ nature: string;
26
+ moves: string[];
27
+ evs?: Partial<StatsTable>;
28
+ ivs?: Partial<StatsTable>;
29
+ }
16
30
  export class RandomGen7Teams extends RandomTeams {
17
31
  constructor(dex: ModdedDex, format: Format, prng: PRNG | PRNGSeed | null) {
18
32
  super(dex, format, prng);
@@ -503,6 +517,7 @@ export class RandomGen7Teams extends RandomTeams {
503
517
  return {cull: (
504
518
  counter.get('Physical') + counter.get('Special') < 2 ||
505
519
  hasRestTalk ||
520
+ moves.has('rest') ||
506
521
  (!types.has('Water') && !counter.get('Water'))
507
522
  )};
508
523
  case 'sunnyday':
@@ -1588,7 +1603,7 @@ export class RandomGen7Teams extends RandomTeams {
1588
1603
  return pokemon;
1589
1604
  }
1590
1605
 
1591
- randomFactorySets: AnyObject = {};
1606
+ randomFactorySets: {[format: string]: {[species: string]: BattleFactorySpecies}} = {};
1592
1607
 
1593
1608
  randomFactorySet(
1594
1609
  species: Species, teamData: RandomTeamsTypes.FactoryTeamDetails, tier: string
@@ -1696,12 +1711,16 @@ export class RandomGen7Teams extends RandomTeams {
1696
1711
 
1697
1712
  randomFactoryTeam(side: PlayerOptions, depth = 0): RandomTeamsTypes.RandomFactorySet[] {
1698
1713
  const forceResult = (depth >= 4);
1714
+ const isMonotype = !!this.forceMonotype || this.dex.formats.getRuleTable(this.format).has('sametypeclause');
1699
1715
 
1700
1716
  // The teams generated depend on the tier choice in such a way that
1701
1717
  // no exploitable information is leaked from rolling the tier in getTeam(p1).
1702
- const availableTiers = ['Uber', 'OU', 'UU', 'RU', 'NU', 'PU', 'LC', 'Mono'];
1703
- if (!this.factoryTier) this.factoryTier = this.sample(availableTiers);
1704
- const chosenTier = this.factoryTier;
1718
+ if (!this.factoryTier) {
1719
+ this.factoryTier = isMonotype ? 'Mono' : this.sample(['Uber', 'OU', 'UU', 'RU', 'NU', 'PU', 'LC']);
1720
+ } else if (isMonotype && this.factoryTier !== 'Mono') {
1721
+ // I don't think this can ever happen?
1722
+ throw new Error(`Can't generate a Monotype Battle Factory set in a battle with factory tier ${this.factoryTier}`);
1723
+ }
1705
1724
 
1706
1725
  const tierValues: {[k: string]: number} = {
1707
1726
  Uber: 5,
@@ -1713,7 +1732,7 @@ export class RandomGen7Teams extends RandomTeams {
1713
1732
  };
1714
1733
 
1715
1734
  const pokemon = [];
1716
- const pokemonPool = Object.keys(this.randomFactorySets[chosenTier]);
1735
+ const pokemonPool = Object.keys(this.randomFactorySets[this.factoryTier]);
1717
1736
 
1718
1737
  const typePool = this.dex.types.names();
1719
1738
  const type = this.sample(typePool);
@@ -1749,11 +1768,11 @@ export class RandomGen7Teams extends RandomTeams {
1749
1768
 
1750
1769
  // Lessen the need of deleting sets of Pokemon after tier shifts
1751
1770
  if (
1752
- chosenTier in tierValues && species.tier in tierValues &&
1753
- tierValues[species.tier] > tierValues[chosenTier]
1771
+ this.factoryTier in tierValues && species.tier in tierValues &&
1772
+ tierValues[species.tier] > tierValues[this.factoryTier]
1754
1773
  ) continue;
1755
1774
 
1756
- const speciesFlags = this.randomFactorySets[chosenTier][species.id].flags;
1775
+ const speciesFlags = this.randomFactorySets[this.factoryTier][species.id].flags;
1757
1776
 
1758
1777
  // Limit to one of each species (Species Clause)
1759
1778
  if (teamData.baseFormes[species.baseSpecies]) continue;
@@ -1762,7 +1781,7 @@ export class RandomGen7Teams extends RandomTeams {
1762
1781
  if (!teamData.megaCount) teamData.megaCount = 0;
1763
1782
  if (teamData.megaCount >= 1 && speciesFlags.megaOnly) continue;
1764
1783
 
1765
- const set = this.randomFactorySet(species, teamData, chosenTier);
1784
+ const set = this.randomFactorySet(species, teamData, this.factoryTier);
1766
1785
  if (!set) continue;
1767
1786
 
1768
1787
  const itemData = this.dex.items.get(set.item);
@@ -1778,7 +1797,7 @@ export class RandomGen7Teams extends RandomTeams {
1778
1797
  const limitFactor = Math.round(this.maxTeamSize / 6) || 1;
1779
1798
 
1780
1799
  // Enforce Monotype
1781
- if (chosenTier === 'Mono') {
1800
+ if (isMonotype) {
1782
1801
  // Prevents Mega Evolutions from breaking the type limits
1783
1802
  if (itemData.megaStone) {
1784
1803
  const megaSpecies = this.dex.species.get(itemData.megaStone);
@@ -1986,7 +2005,7 @@ export class RandomGen7Teams extends RandomTeams {
1986
2005
 
1987
2006
  const teamData: TeamData = {
1988
2007
  typeCount: {}, typeComboCount: {}, baseFormes: {}, megaCount: 0, zCount: 0,
1989
- eeveeLimCount: 0, has: {}, forceResult: forceResult, weaknesses: {}, resistances: {},
2008
+ eeveeLimCount: 0, has: {}, forceResult, weaknesses: {}, resistances: {},
1990
2009
  };
1991
2010
  const requiredMoveFamilies: string[] = [];
1992
2011
  const requiredMoves: {[k: string]: string} = {};