@pkmn/randoms 0.6.4 → 0.7.0

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/gen2.ts DELETED
@@ -1,323 +0,0 @@
1
- import {MoveCounter} from './gen8';
2
- import {RandomGen3Teams} from './gen3';
3
- import {
4
- Format,
5
- ModdedDex,
6
- Move,
7
- PRNG,
8
- PRNGSeed,
9
- RandomTeamsTypes,
10
- Species,
11
- } from '@pkmn/sim';
12
-
13
- export class RandomGen2Teams extends RandomGen3Teams {
14
- constructor(dex: ModdedDex, format: Format, prng: PRNG | PRNGSeed | null) {
15
- super(dex, format, prng);
16
- this.moveEnforcementCheckers = {
17
- Electric: (movePool, moves, abilities, types, counter) => !counter.get('Electric'),
18
- Fire: (movePool, moves, abilities, types, counter) => !counter.get('Fire'),
19
- Ground: (movePool, moves, abilities, types, counter) => !counter.get('Ground'),
20
- Ice: (movePool, moves, abilities, types, counter) => !counter.get('Ice'),
21
- Normal: (movePool, moves, abilities, types, counter) => !counter.get('Normal') && counter.setupType === 'Physical',
22
- Psychic: (movePool, moves, abilities, types, counter) => !counter.get('Psychic') && types.has('Grass'),
23
- Rock: (movePool, moves, abilities, types, counter, species) => !counter.get('Rock') && species.baseStats.atk > 60,
24
- Water: (movePool, moves, abilities, types, counter) => !counter.get('Water'),
25
- };
26
- }
27
-
28
- shouldCullMove(
29
- move: Move,
30
- types: Set<string>,
31
- moves: Set<string>,
32
- abilities = {},
33
- counter: MoveCounter,
34
- movePool: string[],
35
- teamDetails: RandomTeamsTypes.TeamDetails,
36
- ): {cull: boolean, isSetup?: boolean} {
37
- const restTalk = moves.has('rest') && moves.has('sleeptalk');
38
-
39
- switch (move.id) {
40
- // Set up once and only if we have the moves for it
41
- case 'bellydrum': case 'curse': case 'meditate': case 'screech': case 'swordsdance':
42
- return {
43
- cull: (
44
- (counter.setupType !== 'Physical' || counter.get('physicalsetup') > 1) ||
45
- (!counter.get('Physical') || counter.damagingMoves.size < 2 && !moves.has('batonpass') && !moves.has('sleeptalk'))
46
- ),
47
- isSetup: true,
48
- };
49
-
50
- // Not very useful without their supporting moves
51
- case 'batonpass':
52
- return {cull: !counter.setupType && !counter.get('speedsetup') && !moves.has('meanlook')};
53
- case 'meanlook':
54
- return {cull: movePool.includes('perishsong')};
55
- case 'nightmare':
56
- return {cull: !moves.has('lovelykiss') && !moves.has('sleeppowder')};
57
- case 'swagger':
58
- return {cull: !moves.has('substitute')};
59
-
60
- // Bad after setup
61
- case 'charm': case 'counter':
62
- return {cull: !!counter.setupType};
63
- case 'haze':
64
- return {cull: !!counter.setupType || restTalk};
65
- case 'reflect': case 'lightscreen':
66
- return {cull: !!counter.setupType || moves.has('rest')};
67
-
68
- // Ineffective to have both
69
- case 'doubleedge':
70
- return {cull: moves.has('bodyslam') || moves.has('return')};
71
- case 'explosion': case 'selfdestruct':
72
- return {cull: moves.has('softboiled') || restTalk};
73
- case 'extremespeed':
74
- return {cull: moves.has('bodyslam') || restTalk};
75
- case 'hyperbeam':
76
- return {cull: moves.has('rockslide')};
77
- case 'quickattack':
78
- return {cull: moves.has('rest')};
79
- case 'rapidspin':
80
- return {cull: !!teamDetails.rapidSpin || moves.has('sleeptalk')};
81
- case 'return':
82
- return {cull: moves.has('bodyslam')};
83
- case 'surf':
84
- return {cull: moves.has('hydropump')};
85
- case 'thunder':
86
- return {cull: moves.has('thunderbolt')};
87
- case 'gigadrain':
88
- return {cull: moves.has('razorleaf') || moves.has('swordsdance') && movePool.includes('sludgebomb')};
89
- case 'icebeam':
90
- return {cull: moves.has('dragonbreath')};
91
- case 'seismictoss':
92
- return {cull: moves.has('rest') || moves.has('sleeptalk')};
93
- case 'destinybond':
94
- return {cull: moves.has('explosion')};
95
- case 'pursuit':
96
- return {cull: moves.has('crunch') && moves.has('solarbeam')};
97
- case 'thief':
98
- return {cull: moves.has('rest') || moves.has('substitute')};
99
- case 'irontail':
100
- return {cull: types.has('Ground') && movePool.includes('earthquake')};
101
-
102
- // Status and illegal move rejections
103
- case 'confuseray': case 'roar': case 'whirlwind':
104
- return {cull: restTalk};
105
- case 'encore':
106
- return {cull: moves.has('bodyslam') || moves.has('surf') || restTalk};
107
- case 'lovelykiss':
108
- return {cull: ['healbell', 'moonlight', 'morningsun'].some(m => moves.has(m)) || restTalk};
109
- case 'sleeptalk':
110
- return {cull: moves.has('curse') && counter.get('stab') >= 2};
111
- case 'softboiled':
112
- return {cull: movePool.includes('swordsdance')};
113
- case 'spikes':
114
- return {cull: !!teamDetails.spikes || types.has('Ice') && moves.has('rapidspin')};
115
- case 'substitute':
116
- return {cull: moves.has('agility') || moves.has('rest')};
117
- case 'synthesis':
118
- return {cull: moves.has('explosion')};
119
- case 'thunderwave':
120
- return {cull: moves.has('thunder') || moves.has('toxic')};
121
- }
122
-
123
- return {cull: false};
124
- }
125
-
126
- getItem(
127
- ability: string,
128
- types: Set<string>,
129
- moves: Set<string>,
130
- counter: MoveCounter,
131
- species: Species,
132
- ) {
133
- // First, the high-priority items
134
- if (species.name === 'Ditto') return this.sample(['Metal Powder', 'Quick Claw']);
135
- if (species.name === 'Farfetch\u2019d') return 'Stick';
136
- if (species.name === 'Marowak') return 'Thick Club';
137
- if (species.name === 'Pikachu') return 'Light Ball';
138
- if (species.name === 'Unown') return 'Twisted Spoon';
139
- if (moves.has('thief')) return '';
140
-
141
- // Medium priority
142
- if (moves.has('rest') && !moves.has('sleeptalk')) return 'Mint Berry';
143
- if (
144
- (moves.has('bellydrum') || moves.has('swordsdance')) &&
145
- species.baseStats.spe >= 60 && !types.has('Ground') &&
146
- !moves.has('sleeptalk') && !moves.has('substitute') &&
147
- this.randomChance(1, 2)
148
- ) {
149
- return 'Miracle Berry';
150
- }
151
-
152
- // Default to Leftovers
153
- return 'Leftovers';
154
- }
155
-
156
- randomSet(species: string | Species, teamDetails: RandomTeamsTypes.TeamDetails = {}): RandomTeamsTypes.RandomSet {
157
- species = this.dex.species.get(species);
158
-
159
- const movePool = (species.randomBattleMoves || Object.keys(this.dex.species.getLearnset(species.id)!)).slice();
160
- const rejectedPool: string[] = [];
161
- const moves = new Set<string>();
162
-
163
- let ivs = {hp: 30, atk: 30, def: 30, spa: 30, spd: 30, spe: 30};
164
- let availableHP = 0;
165
- for (const setMoveid of movePool) {
166
- if (setMoveid.startsWith('hiddenpower')) availableHP++;
167
- }
168
-
169
- const types = new Set(species.types);
170
-
171
- let counter;
172
- // We use a special variable to track Hidden Power
173
- // so that we can check for all Hidden Powers at once
174
- let hasHiddenPower = false;
175
-
176
- do {
177
- // Choose next 4 moves from learnset/viable moves and add them to moves list:
178
- while (moves.size < this.maxMoveCount && movePool.length) {
179
- const moveid = this.sampleNoReplace(movePool);
180
- if (moveid.startsWith('hiddenpower')) {
181
- availableHP--;
182
- if (hasHiddenPower) continue;
183
- hasHiddenPower = true;
184
- }
185
- moves.add(moveid);
186
- }
187
- while (moves.size < this.maxMoveCount && rejectedPool.length) {
188
- const moveid = this.sampleNoReplace(rejectedPool);
189
- if (moveid.startsWith('hiddenpower')) {
190
- if (hasHiddenPower) continue;
191
- hasHiddenPower = true;
192
- }
193
- moves.add(moveid);
194
- }
195
-
196
- counter = this.queryMoves(moves, species.types, new Set(), movePool);
197
-
198
- // Iterate through the moves again, this time to cull them:
199
- for (const moveid of moves) {
200
- const move = this.dex.moves.get(moveid);
201
- let {cull, isSetup} = this.shouldCullMove(move, types, moves, {}, counter, movePool, teamDetails);
202
-
203
- // This move doesn't satisfy our setup requirements:
204
- if (counter.setupType === 'Physical' && move.category === 'Special' && !counter.get('Physical')) {
205
- cull = true;
206
- }
207
-
208
-
209
- // Reject Status, non-STAB, or low basepower moves
210
- const moveIsRejectable = (
211
- (move.category !== 'Status' || !move.flags.heal) &&
212
- // These moves cannot be rejected in favor of a forced move
213
- !['batonpass', 'sleeptalk', 'spikes', 'sunnyday'].includes(move.id) &&
214
- (move.category === 'Status' || !types.has(move.type) || (move.basePower && move.basePower < 40))
215
- );
216
-
217
- if (!cull && !isSetup && moveIsRejectable && (counter.setupType || !move.stallingMove)) {
218
- // There may be more important moves that this Pokemon needs
219
- if (
220
- // Pokemon should usually have at least one STAB move
221
- (
222
- !counter.get('stab') &&
223
- !counter.get('damage') &&
224
- !types.has('Ghost') &&
225
- counter.get('physicalpool') + counter.get('specialpool') > 0
226
- ) || (movePool.includes('megahorn') || (movePool.includes('softboiled') && moves.has('present'))) ||
227
- // Rest + Sleep Talk should be selected together
228
- ((moves.has('rest') && movePool.includes('sleeptalk')) || (moves.has('sleeptalk') && movePool.includes('rest'))) ||
229
- // Sunny Day + Solar Beam should be selected together
230
- (moves.has('sunnyday') && movePool.includes('solarbeam') ||
231
- (moves.has('solarbeam') && movePool.includes('sunnyday'))) ||
232
- ['milkdrink', 'recover', 'spore'].some(m => movePool.includes(m))
233
- ) {
234
- cull = true;
235
- } else {
236
- // Pokemon should have moves that benefit their typing
237
- for (const type of types) {
238
- if (this.moveEnforcementCheckers[type]?.(movePool, moves, new Set(), types, counter, species, teamDetails)) cull = true;
239
- }
240
- }
241
- }
242
-
243
- // Remove rejected moves from the move list
244
- if (
245
- cull &&
246
- (movePool.length - availableHP || availableHP && (move.id === 'hiddenpower' || !hasHiddenPower))
247
- ) {
248
- if (move.category !== 'Status' && !move.damage && (move.id !== 'hiddenpower' || !availableHP)) {
249
- rejectedPool.push(moveid);
250
- }
251
- moves.delete(moveid);
252
- if (moveid.startsWith('hiddenpower')) hasHiddenPower = false;
253
- break;
254
- }
255
-
256
- if (cull && rejectedPool.length) {
257
- moves.delete(moveid);
258
- if (moveid.startsWith('hiddenpower')) hasHiddenPower = false;
259
- break;
260
- }
261
- }
262
- } while (moves.size < this.maxMoveCount && (movePool.length || rejectedPool.length));
263
-
264
- // Adjust IVs for Hidden Power
265
- for (const setMoveid of moves) {
266
- if (!setMoveid.startsWith('hiddenpower')) continue;
267
- const hpType = setMoveid.substr(11, setMoveid.length);
268
-
269
- const hpIVs: {[k: string]: Partial<typeof ivs>} = {
270
- dragon: {def: 28},
271
- ice: {def: 26},
272
- psychic: {def: 24},
273
- electric: {atk: 28},
274
- grass: {atk: 28, def: 28},
275
- water: {atk: 28, def: 26},
276
- fire: {atk: 28, def: 24},
277
- steel: {atk: 26},
278
- ghost: {atk: 26, def: 28},
279
- bug: {atk: 26, def: 26},
280
- rock: {atk: 26, def: 24},
281
- ground: {atk: 24},
282
- poison: {atk: 24, def: 28},
283
- flying: {atk: 24, def: 26},
284
- fighting: {atk: 24, def: 24},
285
- };
286
- if (hpIVs[hpType]) {
287
- ivs = {...ivs, ...hpIVs[hpType]};
288
- }
289
-
290
- if (ivs.atk === 28 || ivs.atk === 24) ivs.hp = 14;
291
- if (ivs.def === 28 || ivs.def === 24) ivs.hp -= 8;
292
- }
293
-
294
- const levelScale: {[k: string]: number} = {
295
- NU: 73,
296
- NUBL: 71,
297
- UU: 69,
298
- UUBL: 67,
299
- OU: 65,
300
- Uber: 61,
301
- };
302
- const customScale: {[k: string]: number} = {
303
- Ditto: 83, Unown: 87, Wobbuffet: 83,
304
- };
305
- const level = this.adjustLevel || customScale[species.name] || levelScale[species.tier] || 80;
306
-
307
- return {
308
- name: species.name,
309
- species: species.name,
310
- moves: Array.from(moves),
311
- ability: 'No Ability',
312
- evs: {hp: 255, atk: 255, def: 255, spa: 255, spd: 255, spe: 255},
313
- ivs,
314
- item: this.getItem('None', types, moves, counter, species),
315
- level,
316
- // No shiny chance because Gen 2 shinies have bad IVs
317
- shiny: false,
318
- gender: species.gender ? species.gender : 'M',
319
- };
320
- }
321
- }
322
-
323
- export default RandomGen2Teams;