@pkmn/sim 0.5.15 → 0.5.18

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.
Files changed (80) hide show
  1. package/build/config/formats.js +39 -57
  2. package/build/config/formats.js.map +1 -1
  3. package/build/data/formats-data.js +1 -1
  4. package/build/data/formats-data.js.map +1 -1
  5. package/build/data/items.js +1 -3
  6. package/build/data/items.js.map +1 -1
  7. package/build/data/learnsets.js +2 -2
  8. package/build/data/learnsets.js.map +1 -1
  9. package/build/data/mods/gen1/formats-data.js +1 -1
  10. package/build/data/mods/gen1/formats-data.js.map +1 -1
  11. package/build/data/mods/gen1/moves.js +1 -4
  12. package/build/data/mods/gen1/moves.js.map +1 -1
  13. package/build/data/mods/gen2/moves.js +0 -3
  14. package/build/data/mods/gen2/moves.js.map +1 -1
  15. package/build/data/mods/gen3/moves.js +0 -10
  16. package/build/data/mods/gen3/moves.js.map +1 -1
  17. package/build/data/mods/gen3/scripts.js +2 -2
  18. package/build/data/mods/gen3/scripts.js.map +1 -1
  19. package/build/data/mods/gen4/moves.js +4 -5
  20. package/build/data/mods/gen4/moves.js.map +1 -1
  21. package/build/data/mods/gen4/rulesets.js +2 -1
  22. package/build/data/mods/gen4/rulesets.js.map +1 -1
  23. package/build/data/mods/gen5/rulesets.js +2 -1
  24. package/build/data/mods/gen5/rulesets.js.map +1 -1
  25. package/build/data/mods/gen7/formats-data.js +4 -4
  26. package/build/data/mods/gen7/formats-data.js.map +1 -1
  27. package/build/data/mods/gen7/rulesets.js +2 -1
  28. package/build/data/mods/gen7/rulesets.js.map +1 -1
  29. package/build/data/moves.js +59 -79
  30. package/build/data/moves.js.map +1 -1
  31. package/build/data/pokedex.js +2 -2
  32. package/build/data/pokedex.js.map +1 -1
  33. package/build/data/rulesets.js +3 -4
  34. package/build/data/rulesets.js.map +1 -1
  35. package/build/data/text/items.js +1 -0
  36. package/build/data/text/items.js.map +1 -1
  37. package/build/data/text/moves.js +5 -3
  38. package/build/data/text/moves.js.map +1 -1
  39. package/build/sim/battle-actions.js +12 -4
  40. package/build/sim/battle-actions.js.map +1 -1
  41. package/build/sim/battle.js +43 -5
  42. package/build/sim/battle.js.map +1 -1
  43. package/build/sim/dex-conditions.d.ts +1 -0
  44. package/build/sim/dex-conditions.js.map +1 -1
  45. package/build/sim/dex-moves.d.ts +1 -1
  46. package/build/sim/exported-global-types.d.ts +1 -0
  47. package/build/sim/global-types.d.ts +1 -0
  48. package/build/sim/team-validator.js +15 -4
  49. package/build/sim/team-validator.js.map +1 -1
  50. package/build/sim/tools/exhaustive-runner.d.ts +9 -0
  51. package/build/sim/tools/exhaustive-runner.js +18 -7
  52. package/build/sim/tools/exhaustive-runner.js.map +1 -1
  53. package/config/formats.ts +39 -57
  54. package/data/formats-data.ts +1 -1
  55. package/data/items.ts +1 -3
  56. package/data/learnsets.ts +2 -2
  57. package/data/mods/gen1/formats-data.ts +1 -1
  58. package/data/mods/gen1/moves.ts +1 -4
  59. package/data/mods/gen2/moves.ts +0 -3
  60. package/data/mods/gen3/moves.ts +0 -10
  61. package/data/mods/gen3/scripts.ts +3 -3
  62. package/data/mods/gen4/moves.ts +3 -5
  63. package/data/mods/gen4/rulesets.ts +2 -1
  64. package/data/mods/gen5/rulesets.ts +2 -1
  65. package/data/mods/gen7/formats-data.ts +4 -4
  66. package/data/mods/gen7/rulesets.ts +2 -1
  67. package/data/moves.ts +59 -70
  68. package/data/pokedex.ts +2 -2
  69. package/data/rulesets.ts +3 -6
  70. package/data/text/items.ts +2 -0
  71. package/data/text/moves.ts +6 -3
  72. package/package.json +1 -1
  73. package/sim/battle-actions.ts +16 -5
  74. package/sim/battle.ts +42 -5
  75. package/sim/dex-conditions.ts +1 -0
  76. package/sim/dex-moves.ts +1 -1
  77. package/sim/exported-global-types.ts +3 -0
  78. package/sim/global-types.ts +3 -0
  79. package/sim/team-validator.ts +16 -4
  80. package/sim/tools/exhaustive-runner.ts +30 -11
package/data/moves.ts CHANGED
@@ -2689,53 +2689,50 @@ export const Moves: {[moveid: string]: MoveData} = {
2689
2689
  const offset = this.random(3) + 1;
2690
2690
  // the list of all sides in counterclockwise order
2691
2691
  const sides = [this.sides[0], this.sides[2]!, this.sides[1], this.sides[3]!];
2692
- for (const id of sideConditions) {
2693
- const effectName = this.dex.conditions.get(id).name;
2694
- const rotatedSides = [];
2695
- let someCondition = false;
2696
- for (let i = 0; i < 4; i++) {
2697
- const sourceSide = sides[i];
2698
- const targetSide = sides[(i + offset) % 4]; // the next side in rotation
2699
- rotatedSides.push(targetSide.sideConditions[id]);
2700
- if (sourceSide.sideConditions[id]) {
2701
- this.add('-sideend', sourceSide, effectName, '[silent]');
2702
- someCondition = true;
2703
- }
2692
+ const temp: {[k: number]: typeof source.side.sideConditions} = {0: {}, 1: {}, 2: {}, 3: {}};
2693
+ for (const side of sides) {
2694
+ for (const id in side.sideConditions) {
2695
+ if (!sideConditions.includes(id)) continue;
2696
+ temp[side.n][id] = side.sideConditions[id];
2697
+ delete side.sideConditions[id];
2698
+ const effectName = this.dex.conditions.get(id).name;
2699
+ this.add('-sideend', side, effectName, '[silent]');
2700
+ success = true;
2704
2701
  }
2705
- if (!someCondition) continue;
2706
- [
2707
- sides[0].sideConditions[id], sides[1].sideConditions[id],
2708
- sides[2]!.sideConditions[id], sides[3]!.sideConditions[id],
2709
- ] = [...rotatedSides];
2710
- for (const side of sides) {
2711
- if (side.sideConditions[id]) {
2712
- let layers = side.sideConditions[id].layers || 1;
2713
- for (; layers > 0; layers--) this.add('-sidestart', side, effectName, '[silent]');
2714
- } else {
2715
- delete side.sideConditions[id];
2716
- }
2702
+ }
2703
+ for (let i = 0; i < 4; i++) {
2704
+ const sourceSideConditions = temp[sides[i].n];
2705
+ const targetSide = sides[(i + offset) % 4]; // the next side in rotation
2706
+ for (const id in sourceSideConditions) {
2707
+ targetSide.sideConditions[id] = sourceSideConditions[id];
2708
+ const effectName = this.dex.conditions.get(id).name;
2709
+ let layers = sourceSideConditions[id].layers || 1;
2710
+ for (; layers > 0; layers--) this.add('-sidestart', targetSide, effectName, '[silent]');
2717
2711
  }
2718
- success = true;
2719
2712
  }
2720
2713
  } else {
2721
- const sourceSide = source.side;
2722
- const targetSide = source.side.foe;
2723
- for (const id of sideConditions) {
2724
- if (sourceSide.sideConditions[id] && targetSide.sideConditions[id]) {
2725
- [sourceSide.sideConditions[id], targetSide.sideConditions[id]] = [
2726
- targetSide.sideConditions[id], sourceSide.sideConditions[id],
2727
- ];
2728
- } else if (sourceSide.sideConditions[id] && !targetSide.sideConditions[id]) {
2729
- targetSide.sideConditions[id] = sourceSide.sideConditions[id];
2730
- delete sourceSide.sideConditions[id];
2731
- } else if (targetSide.sideConditions[id] && !sourceSide.sideConditions[id]) {
2732
- sourceSide.sideConditions[id] = targetSide.sideConditions[id];
2733
- delete targetSide.sideConditions[id];
2734
- } else {
2735
- continue;
2736
- }
2714
+ const sourceSideConditions = source.side.sideConditions;
2715
+ const targetSideConditions = source.side.foe.sideConditions;
2716
+ const sourceTemp: typeof sourceSideConditions = {};
2717
+ const targetTemp: typeof targetSideConditions = {};
2718
+ for (const id in sourceSideConditions) {
2719
+ if (!sideConditions.includes(id)) continue;
2720
+ sourceTemp[id] = sourceSideConditions[id];
2721
+ delete sourceSideConditions[id];
2722
+ success = true;
2723
+ }
2724
+ for (const id in targetSideConditions) {
2725
+ if (!sideConditions.includes(id)) continue;
2726
+ targetTemp[id] = targetSideConditions[id];
2727
+ delete targetSideConditions[id];
2737
2728
  success = true;
2738
2729
  }
2730
+ for (const id in sourceTemp) {
2731
+ targetSideConditions[id] = sourceTemp[id];
2732
+ }
2733
+ for (const id in targetTemp) {
2734
+ sourceSideConditions[id] = targetTemp[id];
2735
+ }
2739
2736
  this.add('-swapsideconditions');
2740
2737
  }
2741
2738
  if (!success) return false;
@@ -4799,7 +4796,6 @@ export const Moves: {[moveid: string]: MoveData} = {
4799
4796
  pp: 5,
4800
4797
  priority: 0,
4801
4798
  flags: {protect: 1},
4802
- selfdestruct: "ifHit",
4803
4799
  secondary: null,
4804
4800
  target: "normal",
4805
4801
  type: "Fighting",
@@ -6720,7 +6716,7 @@ export const Moves: {[moveid: string]: MoveData} = {
6720
6716
  onSideStart(side) {
6721
6717
  this.add('-sidestart', side, 'move: G-Max Steelsurge');
6722
6718
  },
6723
- onSwitchIn(pokemon) {
6719
+ onEntryHazard(pokemon) {
6724
6720
  if (pokemon.hasItem('heavydutyboots')) return;
6725
6721
  // Ice Face and Disguise correctly get typed damage from Stealth Rock
6726
6722
  // because Stealth Rock bypasses Substitute.
@@ -7820,10 +7816,10 @@ export const Moves: {[moveid: string]: MoveData} = {
7820
7816
  priority: 0,
7821
7817
  flags: {snatch: 1, heal: 1},
7822
7818
  onHit(target, source, move) {
7823
- if (!this.canSwitch(target.side)) {
7819
+ if (!this.canSwitch(source.side)) {
7824
7820
  delete move.selfdestruct;
7825
7821
  this.attrLastMove('[still]');
7826
- this.add('-fail', target);
7822
+ this.add('-fail', source);
7827
7823
  return this.NOT_FAIL;
7828
7824
  }
7829
7825
  },
@@ -9916,10 +9912,10 @@ export const Moves: {[moveid: string]: MoveData} = {
9916
9912
  priority: 0,
9917
9913
  flags: {snatch: 1, heal: 1, dance: 1},
9918
9914
  onHit(target, source, move) {
9919
- if (!this.canSwitch(target.side)) {
9915
+ if (!this.canSwitch(source.side)) {
9920
9916
  delete move.selfdestruct;
9921
9917
  this.attrLastMove('[still]');
9922
- this.add('-fail', target);
9918
+ this.add('-fail', source);
9923
9919
  return this.NOT_FAIL;
9924
9920
  }
9925
9921
  },
@@ -11063,26 +11059,21 @@ export const Moves: {[moveid: string]: MoveData} = {
11063
11059
  priority: 0,
11064
11060
  flags: {},
11065
11061
  noMetronome: [
11066
- "After You", "Apple Acid", "Assist", "Astral Barrage", "Aura Wheel", "Baneful Bunker", "Beak Blast", "Behemoth Bash", "Behemoth Blade", "Belch", "Bestow", "Body Press", "Branch Poke", "Breaking Swipe", "Celebrate", "Chatter", "Clangorous Soul", "Copycat", "Counter", "Covet", "Crafty Shield", "Decorate", "Destiny Bond", "Detect", "Diamond Storm", "Double Iron Bash", "Dragon Ascent", "Dragon Energy", "Drum Beating", "Dynamax Cannon", "Endure", "Eternabeam", "False Surrender", "Feint", "Fiery Wrath", "Fleur Cannon", "Focus Punch", "Follow Me", "Freeze Shock", "Freezing Glare", "Glacial Lance", "Grav Apple", "Helping Hand", "Hold Hands", "Hyperspace Fury", "Hyperspace Hole", "Ice Burn", "Instruct", "Jungle Healing", "King's Shield", "Life Dew", "Light of Ruin", "Mat Block", "Me First", "Meteor Assault", "Metronome", "Mimic", "Mind Blown", "Mirror Coat", "Mirror Move", "Moongeist Beam", "Nature Power", "Nature's Madness", "Obstruct", "Origin Pulse", "Overdrive", "Photon Geyser", "Plasma Fists", "Precipice Blades", "Protect", "Pyro Ball", "Quash", "Quick Guard", "Rage Powder", "Relic Song", "Secret Sword", "Shell Trap", "Sketch", "Sleep Talk", "Snap Trap", "Snarl", "Snatch", "Snore", "Spectral Thief", "Spiky Shield", "Spirit Break", "Spotlight", "Steam Eruption", "Steel Beam", "Strange Steam", "Struggle", "Sunsteel Strike", "Surging Strikes", "Switcheroo", "Techno Blast", "Thief", "Thousand Arrows", "Thousand Waves", "Thunder Cage", "Thunderous Kick", "Transform", "Trick", "V-create", "Wicked Blow", "Wide Guard",
11062
+ "After You", "Apple Acid", "Assist", "Astral Barrage", "Aura Wheel", "Baneful Bunker", "Beak Blast", "Behemoth Bash", "Behemoth Blade", "Belch", "Bestow", "Body Press", "Branch Poke", "Breaking Swipe", "Celebrate", "Chatter", "Clangorous Soul", "Copycat", "Counter", "Covet", "Crafty Shield", "Decorate", "Destiny Bond", "Detect", "Diamond Storm", "Double Iron Bash", "Dragon Ascent", "Dragon Energy", "Dragon Hammer", "Drum Beating", "Dynamax Cannon", "Endure", "Eternabeam", "False Surrender", "Feint", "Fiery Wrath", "Fleur Cannon", "Focus Punch", "Follow Me", "Freeze Shock", "Freezing Glare", "Glacial Lance", "Grav Apple", "Helping Hand", "Hold Hands", "Hyperspace Fury", "Hyperspace Hole", "Ice Burn", "Instruct", "Jungle Healing", "King's Shield", "Life Dew", "Light of Ruin", "Mat Block", "Me First", "Meteor Assault", "Metronome", "Mimic", "Mind Blown", "Mirror Coat", "Mirror Move", "Moongeist Beam", "Nature Power", "Nature's Madness", "Obstruct", "Origin Pulse", "Overdrive", "Photon Geyser", "Plasma Fists", "Precipice Blades", "Protect", "Pyro Ball", "Quash", "Quick Guard", "Rage Powder", "Relic Song", "Secret Sword", "Shell Trap", "Sketch", "Sleep Talk", "Snap Trap", "Snarl", "Snatch", "Snore", "Spectral Thief", "Spiky Shield", "Spirit Break", "Spotlight", "Steam Eruption", "Steel Beam", "Strange Steam", "Struggle", "Sunsteel Strike", "Surging Strikes", "Switcheroo", "Techno Blast", "Thief", "Thousand Arrows", "Thousand Waves", "Thunder Cage", "Thunderous Kick", "Transform", "Trick", "V-create", "Wicked Blow", "Wide Guard",
11067
11063
  ],
11068
11064
  onHit(target, source, effect) {
11069
- const moves: MoveData[] = [];
11070
- for (const id in Moves) {
11071
- const move = Moves[id];
11072
- if (move.realMove) continue;
11073
- if (move.isZ || move.isMax || move.isNonstandard) continue;
11074
- if (effect.noMetronome!.includes(move.name)) continue;
11075
- if (this.dex.moves.get(id).gen > this.gen) continue;
11076
- moves.push(move);
11077
- }
11065
+ const moves = this.dex.moves.all().filter(move => (
11066
+ (![2, 4].includes(this.gen) || !source.moves.includes(move.id)) &&
11067
+ !move.realMove && !move.isZ && !move.isMax &&
11068
+ (!move.isNonstandard || move.isNonstandard === 'Unobtainable') &&
11069
+ !effect.noMetronome!.includes(move.name)
11070
+ ));
11078
11071
  let randomMove = '';
11079
11072
  if (moves.length) {
11080
- moves.sort((a, b) => a.num! - b.num!);
11081
- randomMove = this.sample(moves).name;
11082
- }
11083
- if (!randomMove) {
11084
- return false;
11073
+ moves.sort((a, b) => a.num - b.num);
11074
+ randomMove = this.sample(moves).id;
11085
11075
  }
11076
+ if (!randomMove) return false;
11086
11077
  this.actions.useMove(randomMove, target);
11087
11078
  },
11088
11079
  secondary: null,
@@ -16412,9 +16403,8 @@ export const Moves: {[moveid: string]: MoveData} = {
16412
16403
  this.add('-sidestart', side, 'Spikes');
16413
16404
  this.effectState.layers++;
16414
16405
  },
16415
- onSwitchIn(pokemon) {
16416
- if (!pokemon.isGrounded()) return;
16417
- if (pokemon.hasItem('heavydutyboots')) return;
16406
+ onEntryHazard(pokemon) {
16407
+ if (!pokemon.isGrounded() || pokemon.hasItem('heavydutyboots')) return;
16418
16408
  const damageAmounts = [0, 3, 4, 6]; // 1/8, 1/6, 1/4
16419
16409
  this.damage(damageAmounts[this.effectState.layers] * pokemon.maxhp / 24);
16420
16410
  },
@@ -16697,7 +16687,7 @@ export const Moves: {[moveid: string]: MoveData} = {
16697
16687
  onSideStart(side) {
16698
16688
  this.add('-sidestart', side, 'move: Stealth Rock');
16699
16689
  },
16700
- onSwitchIn(pokemon) {
16690
+ onEntryHazard(pokemon) {
16701
16691
  if (pokemon.hasItem('heavydutyboots')) return;
16702
16692
  const typeMod = this.clampIntRange(pokemon.runEffectiveness(this.dex.getActiveMove('stealthrock')), -6, 6);
16703
16693
  this.damage(pokemon.maxhp * Math.pow(2, typeMod) / 8);
@@ -16821,9 +16811,8 @@ export const Moves: {[moveid: string]: MoveData} = {
16821
16811
  onSideStart(side) {
16822
16812
  this.add('-sidestart', side, 'move: Sticky Web');
16823
16813
  },
16824
- onSwitchIn(pokemon) {
16825
- if (!pokemon.isGrounded()) return;
16826
- if (pokemon.hasItem('heavydutyboots')) return;
16814
+ onEntryHazard(pokemon) {
16815
+ if (!pokemon.isGrounded() || pokemon.hasItem('heavydutyboots')) return;
16827
16816
  this.add('-activate', pokemon, 'move: Sticky Web');
16828
16817
  this.boost({spe: -1}, pokemon, this.effectState.source, this.dex.getActiveMove('stickyweb'));
16829
16818
  },
@@ -18427,7 +18416,7 @@ export const Moves: {[moveid: string]: MoveData} = {
18427
18416
  this.add('-sidestart', side, 'move: Toxic Spikes');
18428
18417
  this.effectState.layers++;
18429
18418
  },
18430
- onSwitchIn(pokemon) {
18419
+ onEntryHazard(pokemon) {
18431
18420
  if (!pokemon.isGrounded()) return;
18432
18421
  if (pokemon.hasType('Poison')) {
18433
18422
  this.add('-sideend', pokemon.side, 'move: Toxic Spikes', '[of] ' + pokemon);
package/data/pokedex.ts CHANGED
@@ -16034,7 +16034,7 @@ export const Pokedex: {[speciesid: string]: SpeciesData} = {
16034
16034
  color: "Blue",
16035
16035
  eggGroups: ["Undiscovered"],
16036
16036
  requiredItem: "Rusted Sword",
16037
- changesFrom: "Zacian",
16037
+ battleOnly: "Zacian",
16038
16038
  cannotDynamax: true,
16039
16039
  },
16040
16040
  zamazenta: {
@@ -16068,7 +16068,7 @@ export const Pokedex: {[speciesid: string]: SpeciesData} = {
16068
16068
  color: "Red",
16069
16069
  eggGroups: ["Undiscovered"],
16070
16070
  requiredItem: "Rusted Shield",
16071
- changesFrom: "Zamazenta",
16071
+ battleOnly: "Zamazenta",
16072
16072
  cannotDynamax: true,
16073
16073
  },
16074
16074
  eternatus: {
package/data/rulesets.ts CHANGED
@@ -446,7 +446,8 @@ export const Rulesets: {[k: string]: FormatData} = {
446
446
  this.add('clearpoke');
447
447
  for (const pokemon of this.getAllPokemon()) {
448
448
  const details = pokemon.details.replace(', shiny', '')
449
- .replace(/(Arceus|Gourgeist|Pumpkaboo|Xerneas|Silvally|Zacian|Zamazenta|Urshifu)(-[a-zA-Z?-]+)?/g, '$1-*');
449
+ .replace(/(Arceus|Gourgeist|Pumpkaboo|Xerneas|Silvally|Urshifu)(-[a-zA-Z?-]+)?/g, '$1-*')
450
+ .replace(/(Zacian|Zamazenta)(?!-Crowned)/g, '$1-*'); // Hacked-in Crowned formes will be revealed
450
451
  this.add('poke', pokemon.side.id, details, '');
451
452
  }
452
453
  this.makeRequest('teampreview');
@@ -1146,11 +1147,7 @@ export const Rulesets: {[k: string]: FormatData} = {
1146
1147
  if (prevo.evos.includes(formeName)) continue;
1147
1148
  }
1148
1149
  const forme = dex.species.get(formeName);
1149
- if (
1150
- forme.changesFrom === originalForme.name && !forme.battleOnly &&
1151
- // Temporary workaround
1152
- forme.forme !== 'Crowned'
1153
- ) {
1150
+ if (forme.changesFrom === originalForme.name && !forme.battleOnly) {
1154
1151
  speciesTypes.push(...forme.types);
1155
1152
  }
1156
1153
  }
@@ -429,6 +429,8 @@ export const ItemsText: {[k: string]: ItemText} = {
429
429
  ejectpack: {
430
430
  name: "Eject Pack",
431
431
  desc: "If the holder's stat stages are lowered, it switches to a chosen ally. Single use.",
432
+
433
+ end: " [POKEMON] is switched out by the Eject Pack!",
432
434
  },
433
435
  electirizer: {
434
436
  name: "Electirizer",
@@ -1934,6 +1934,8 @@ export const MovesText: {[k: string]: MoveText} = {
1934
1934
  name: "Flip Turn",
1935
1935
  desc: "If this move is successful and the user has not fainted, the user switches out even if it is trapped and is replaced immediately by a selected party member. The user does not switch out if there are no unfainted party members, or if the target switched out using an Eject Button or through the effect of the Emergency Exit or Wimp Out Abilities.",
1936
1936
  shortDesc: "User switches out after damaging the target.",
1937
+
1938
+ switchOut: "#uturn",
1937
1939
  },
1938
1940
  floatyfall: {
1939
1941
  name: "Floaty Fall",
@@ -3695,7 +3697,7 @@ export const MovesText: {[k: string]: MoveText} = {
3695
3697
  },
3696
3698
  metronome: {
3697
3699
  name: "Metronome",
3698
- desc: "A random move is selected for use, other than After You, Apple Acid, Assist, Astral Barrage, Aura Wheel, Baneful Bunker, Beak Blast, Behemoth Bash, Behemoth Blade, Belch, Bestow, Body Press, Branch Poke, Breaking Swipe, Celebrate, Chatter, Clangorous Soul, Copycat, Counter, Covet, Crafty Shield, Decorate, Destiny Bond, Detect, Diamond Storm, Double Iron Bash, Dragon Ascent, Dragon Energy, Drum Beating, Dynamax Cannon, Endure, Eternabeam, False Surrender, Feint, Fiery Wrath, Fleur Cannon, Focus Punch, Follow Me, Freeze Shock, Freezing Glare, Glacial Lance, Grav Apple, Helping Hand, Hold Hands, Hyperspace Fury, Hyperspace Hole, Ice Burn, Instruct, Jungle Healing, King's Shield, Life Dew, Light of Ruin, Mat Block, Me First, Meteor Assault, Metronome, Mimic, Mind Blown, Mirror Coat, Mirror Move, Moongeist Beam, Nature Power, Nature's Madness, Obstruct, Origin Pulse, Overdrive, Photon Geyser, Plasma Fists, Precipice Blades, Protect, Pyro Ball, Quash, Quick Guard, Rage Powder, Relic Song, Secret Sword, Shell Trap, Sketch, Sleep Talk, Snap Trap, Snarl, Snatch, Snore, Spectral Thief, Spiky Shield, Spirit Break, Spotlight, Steam Eruption, Steel Beam, Strange Steam, Struggle, Sunsteel Strike, Surging Strikes, Switcheroo, Techno Blast, Thief, Thousand Arrows, Thousand Waves, Thunder Cage, Thunderous Kick, Transform, Trick, V-create, Wicked Blow, or Wide Guard.",
3700
+ desc: "A random move is selected for use, other than After You, Apple Acid, Assist, Astral Barrage, Aura Wheel, Baneful Bunker, Beak Blast, Behemoth Bash, Behemoth Blade, Belch, Bestow, Body Press, Branch Poke, Breaking Swipe, Celebrate, Chatter, Clangorous Soul, Copycat, Counter, Covet, Crafty Shield, Decorate, Destiny Bond, Detect, Diamond Storm, Double Iron Bash, Dragon Ascent, Dragon Energy, Dragon Hammer, Drum Beating, Dynamax Cannon, Endure, Eternabeam, False Surrender, Feint, Fiery Wrath, Fleur Cannon, Focus Punch, Follow Me, Freeze Shock, Freezing Glare, Glacial Lance, Grav Apple, Helping Hand, Hold Hands, Hyperspace Fury, Hyperspace Hole, Ice Burn, Instruct, Jungle Healing, King's Shield, Life Dew, Light of Ruin, Mat Block, Me First, Meteor Assault, Metronome, Mimic, Mind Blown, Mirror Coat, Mirror Move, Moongeist Beam, Nature Power, Nature's Madness, Obstruct, Origin Pulse, Overdrive, Photon Geyser, Plasma Fists, Precipice Blades, Protect, Pyro Ball, Quash, Quick Guard, Rage Powder, Relic Song, Secret Sword, Shell Trap, Sketch, Sleep Talk, Snap Trap, Snarl, Snatch, Snore, Spectral Thief, Spiky Shield, Spirit Break, Spotlight, Steam Eruption, Steel Beam, Strange Steam, Struggle, Sunsteel Strike, Surging Strikes, Switcheroo, Techno Blast, Thief, Thousand Arrows, Thousand Waves, Thunder Cage, Thunderous Kick, Transform, Trick, V-create, Wicked Blow, or Wide Guard.",
3699
3701
  shortDesc: "Picks a random move.",
3700
3702
  gen7: {
3701
3703
  desc: "A random move is selected for use, other than After You, Assist, Baneful Bunker, Beak Blast, Belch, Bestow, Celebrate, Chatter, Copycat, Counter, Covet, Crafty Shield, Destiny Bond, Detect, Diamond Storm, Dragon Ascent, Endure, Feint, Fleur Cannon, Focus Punch, Follow Me, Freeze Shock, Helping Hand, Hold Hands, Hyperspace Fury, Hyperspace Hole, Ice Burn, Instruct, King's Shield, Light of Ruin, Mat Block, Me First, Metronome, Mimic, Mind Blown, Mirror Coat, Mirror Move, Nature Power, Origin Pulse, Photon Geyser, Plasma Fists, Precipice Blades, Protect, Quash, Quick Guard, Rage Powder, Relic Song, Secret Sword, Shell Trap, Sketch, Sleep Talk, Snarl, Snatch, Snore, Spectral Thief, Spiky Shield, Spotlight, Steam Eruption, Struggle, Switcheroo, Techno Blast, Thief, Thousand Arrows, Thousand Waves, Transform, Trick, V-create, or Wide Guard.",
@@ -3707,13 +3709,13 @@ export const MovesText: {[k: string]: MoveText} = {
3707
3709
  desc: "A random move is selected for use, other than After You, Assist, Bestow, Chatter, Copycat, Counter, Covet, Destiny Bond, Detect, Endure, Feint, Focus Punch, Follow Me, Freeze Shock, Helping Hand, Ice Burn, Me First, Metronome, Mimic, Mirror Coat, Mirror Move, Nature Power, Protect, Quash, Quick Guard, Rage Powder, Relic Song, Secret Sword, Sketch, Sleep Talk, Snarl, Snatch, Snore, Struggle, Switcheroo, Techno Blast, Thief, Transform, Trick, V-create, or Wide Guard.",
3708
3710
  },
3709
3711
  gen4: {
3710
- desc: "A random move is selected for use, other than Assist, Chatter, Copycat, Counter, Covet, Destiny Bond, Detect, Endure, Feint, Focus Punch, Follow Me, Helping Hand, Me First, Metronome, Mimic, Mirror Coat, Mirror Move, Protect, Sketch, Sleep Talk, Snatch, Struggle, Switcheroo, Thief, or Trick.",
3712
+ desc: "A random move is selected for use, other than Assist, Chatter, Copycat, Counter, Covet, Destiny Bond, Detect, Endure, Feint, Focus Punch, Follow Me, Helping Hand, Me First, Metronome, Mimic, Mirror Coat, Mirror Move, Protect, Sketch, Sleep Talk, Snatch, Struggle, Switcheroo, Thief, Trick, or any move the user already knows.",
3711
3713
  },
3712
3714
  gen3: {
3713
3715
  desc: "A random move is selected for use, other than Counter, Covet, Destiny Bond, Detect, Endure, Focus Punch, Follow Me, Helping Hand, Metronome, Mimic, Mirror Coat, Protect, Sketch, Sleep Talk, Snatch, Struggle, Thief, or Trick.",
3714
3716
  },
3715
3717
  gen2: {
3716
- desc: "A random move is selected for use, other than Counter, Destiny Bond, Detect, Endure, Metronome, Mimic, Mirror Coat, Protect, Sketch, Sleep Talk, Struggle, or Thief.",
3718
+ desc: "A random move is selected for use, other than Counter, Destiny Bond, Detect, Endure, Metronome, Mimic, Mirror Coat, Protect, Sketch, Sleep Talk, Struggle, Thief, or any move the user already knows.",
3717
3719
  },
3718
3720
  gen1: {
3719
3721
  desc: "A random move is selected for use, other than Metronome or Struggle.",
@@ -4167,6 +4169,7 @@ export const MovesText: {[k: string]: MoveText} = {
4167
4169
  },
4168
4170
 
4169
4171
  heal: "#memento",
4172
+ switchOut: "#uturn",
4170
4173
  },
4171
4174
  payback: {
4172
4175
  name: "Payback",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pkmn/sim",
3
- "version": "0.5.15",
3
+ "version": "0.5.18",
4
4
  "description": "An automatically generated extraction of just the simulator portion of Pokémon Showdown",
5
5
  "homepage": "https://psim.us",
6
6
  "main": "build/sim/index.js",
@@ -133,6 +133,8 @@ export class BattleActions {
133
133
  oldActive.isActive = false;
134
134
  oldActive.isStarted = false;
135
135
  oldActive.usedItemThisTurn = false;
136
+ oldActive.statsRaisedThisTurn = false;
137
+ oldActive.statsLoweredThisTurn = false;
136
138
  oldActive.position = pokemon.position;
137
139
  pokemon.position = pos;
138
140
  side.pokemon[pokemon.position] = pokemon;
@@ -178,7 +180,17 @@ export class BattleActions {
178
180
  }
179
181
  runSwitch(pokemon: Pokemon) {
180
182
  this.battle.runEvent('Swap', pokemon);
181
- this.battle.runEvent('SwitchIn', pokemon);
183
+
184
+ if (this.battle.gen >= 5) {
185
+ this.battle.runEvent('SwitchIn', pokemon);
186
+ }
187
+
188
+ this.battle.runEvent('EntryHazard', pokemon);
189
+
190
+ if (this.battle.gen <= 4) {
191
+ this.battle.runEvent('SwitchIn', pokemon);
192
+ }
193
+
182
194
  if (this.battle.gen <= 2 && !pokemon.side.faintedThisTurn && pokemon.draggedIn !== this.battle.turn) {
183
195
  this.battle.runEvent('AfterSwitchInSelf', pokemon);
184
196
  }
@@ -1124,9 +1136,6 @@ export class BattleActions {
1124
1136
  continue;
1125
1137
  }
1126
1138
  damage[i] = curDamage;
1127
- if (move.selfdestruct === 'ifHit') {
1128
- this.battle.faint(source, source, move);
1129
- }
1130
1139
  }
1131
1140
  return damage;
1132
1141
  }
@@ -1230,6 +1239,9 @@ export class BattleActions {
1230
1239
  }
1231
1240
  }
1232
1241
  }
1242
+ if (moveData.selfdestruct === 'ifHit' && damage[i] !== false) {
1243
+ this.battle.faint(source, source, move);
1244
+ }
1233
1245
  if (moveData.selfSwitch) {
1234
1246
  if (this.battle.canSwitch(source.side)) {
1235
1247
  didSomething = true;
@@ -1243,7 +1255,6 @@ export class BattleActions {
1243
1255
  didAnything = this.combineResults(didAnything, didSomething);
1244
1256
  }
1245
1257
 
1246
-
1247
1258
  if (!didAnything && didAnything !== 0 && !moveData.self && !moveData.selfdestruct) {
1248
1259
  if (!isSelf && !isSecondary) {
1249
1260
  if (didAnything === false) {
package/sim/battle.ts CHANGED
@@ -711,7 +711,7 @@ export class Battle {
711
711
  }
712
712
  }
713
713
 
714
- if (eventid === 'Invulnerability' || eventid === 'TryHit' || eventid === 'DamagingHit') {
714
+ if (['Invulnerability', 'TryHit', 'DamagingHit', 'EntryHazard'].includes(eventid)) {
715
715
  handlers.sort(Battle.compareLeftToRightOrder);
716
716
  } else if (fastExit) {
717
717
  handlers.sort(Battle.compareRedirectOrder);
@@ -2263,11 +2263,11 @@ export class Battle {
2263
2263
  if (this.activePerHalf > 2) {
2264
2264
  if (move.target === 'adjacentFoe' || move.target === 'normal' || move.target === 'randomNormal') {
2265
2265
  // even if a move can target an ally, auto-resolution will never make it target an ally
2266
- // i.e. if both your opponents faint before you use Flamethrower, it will fail instead of targeting your all
2266
+ // i.e. if both your opponents faint before you use Flamethrower, it will fail instead of targeting your ally
2267
2267
  const adjacentFoes = pokemon.adjacentFoes();
2268
2268
  if (adjacentFoes.length) return this.sample(adjacentFoes);
2269
- // no valid target at all, return a possibly-fainted foe for any possible redirection
2270
- return pokemon.side.foe.active[0];
2269
+ // no valid target at all, return slot directly across for any possible redirection
2270
+ return pokemon.side.foe.active[pokemon.side.foe.active.length - 1 - pokemon.position];
2271
2271
  }
2272
2272
  }
2273
2273
  return pokemon.side.randomFoe() || pokemon.side.foe.active[0];
@@ -2413,6 +2413,43 @@ export class Battle {
2413
2413
 
2414
2414
  this.add('start');
2415
2415
 
2416
+ // Change Zacian/Zamazenta into their Crowned formes
2417
+ for (const pokemon of this.getAllPokemon()) {
2418
+ let rawSpecies: Species | null = null;
2419
+ if (pokemon.species.id === 'zacian' && pokemon.item === 'rustedsword') {
2420
+ rawSpecies = this.dex.species.get('Zacian-Crowned');
2421
+ } else if (pokemon.species.id === 'zamazenta' && pokemon.item === 'rustedshield') {
2422
+ rawSpecies = this.dex.species.get('Zamazenta-Crowned');
2423
+ }
2424
+ if (!rawSpecies) continue;
2425
+ const species = pokemon.setSpecies(rawSpecies);
2426
+ if (!species) continue;
2427
+ pokemon.baseSpecies = rawSpecies;
2428
+ pokemon.details = species.name + (pokemon.level === 100 ? '' : ', L' + pokemon.level) +
2429
+ (pokemon.gender === '' ? '' : ', ' + pokemon.gender) + (pokemon.set.shiny ? ', shiny' : '');
2430
+ pokemon.setAbility(species.abilities['0'], null, true);
2431
+ pokemon.baseAbility = pokemon.ability;
2432
+
2433
+ const behemothMove: {[k: string]: string} = {
2434
+ 'Zacian-Crowned': 'behemothblade', 'Zamazenta-Crowned': 'behemothbash',
2435
+ };
2436
+ const ironHead = pokemon.baseMoves.indexOf('ironhead');
2437
+ if (ironHead >= 0) {
2438
+ const move = this.dex.moves.get(behemothMove[rawSpecies.name]);
2439
+ pokemon.baseMoveSlots[ironHead] = {
2440
+ move: move.name,
2441
+ id: move.id,
2442
+ pp: (move.noPPBoosts || move.isZ) ? move.pp : move.pp * 8 / 5,
2443
+ maxpp: (move.noPPBoosts || move.isZ) ? move.pp : move.pp * 8 / 5,
2444
+ target: move.target,
2445
+ disabled: false,
2446
+ disabledSource: '',
2447
+ used: false,
2448
+ };
2449
+ pokemon.moveSlots = pokemon.baseMoveSlots.slice();
2450
+ }
2451
+ }
2452
+
2416
2453
  if (this.format.onBattleStart) this.format.onBattleStart.call(this);
2417
2454
  for (const rule of this.ruleTable.keys()) {
2418
2455
  if ('+*-!'.includes(rule.charAt(0))) continue;
@@ -2624,7 +2661,7 @@ export class Battle {
2624
2661
 
2625
2662
  if (this.gen < 5) this.eachEvent('Update');
2626
2663
 
2627
- if (this.gen >= 8 && this.queue.peek()?.choice === 'move') {
2664
+ if (this.gen >= 8 && (this.queue.peek()?.choice === 'move' || this.queue.peek()?.choice === 'runDynamax')) {
2628
2665
  // In gen 8, speed is updated dynamically so update the queue's speed properties and sort it.
2629
2666
  this.updateSpeed();
2630
2667
  for (const queueAction of this.queue.list) {
@@ -53,6 +53,7 @@ export interface EventMethods {
53
53
  onDragOut?: (this: Battle, pokemon: Pokemon, source?: Pokemon, move?: ActiveMove) => void;
54
54
  onEatItem?: (this: Battle, item: Item, pokemon: Pokemon) => void;
55
55
  onEffectiveness?: MoveEventMethods['onEffectiveness'];
56
+ onEntryHazard?: (this: Battle, pokemon: Pokemon) => void;
56
57
  onFaint?: CommonHandlers['VoidEffect'];
57
58
  onFlinch?: ((this: Battle, pokemon: Pokemon) => boolean | void) | boolean;
58
59
  onFractionalPriority?: CommonHandlers['ModifierSourceMove'] | -0.1;
package/sim/dex-moves.ts CHANGED
@@ -196,7 +196,7 @@ export interface MoveData extends EffectData, MoveEventMethods, HitEffect {
196
196
  forceSwitch?: boolean;
197
197
  selfSwitch?: string | boolean;
198
198
  selfBoost?: {boosts?: SparseBoostsTable};
199
- selfdestruct?: string | boolean;
199
+ selfdestruct?: 'always' | 'ifHit' | boolean;
200
200
  breaksProtect?: boolean;
201
201
  /**
202
202
  * Note that this is only "true" recoil. Other self-damage, like Struggle,
@@ -312,6 +312,9 @@ export interface ModdedBattlePokemon {
312
312
  clearBoosts?: (this: Pokemon) => void;
313
313
  calculateStat?: (this: Pokemon, statName: StatIDExceptHP, boost: number, modifier?: number) => number;
314
314
  cureStatus?: (this: Pokemon, silent?: boolean) => boolean;
315
+ formeChange?: (
316
+ this: Pokemon, speciesId: string | Species, source: Effect, isPermanent?: boolean, message?: string
317
+ ) => boolean;
315
318
  getAbility?: (this: Pokemon) => Ability;
316
319
  getActionSpeed?: (this: Pokemon) => number;
317
320
  getItem?: (this: Pokemon) => Item;
@@ -312,6 +312,9 @@ interface ModdedBattlePokemon {
312
312
  clearBoosts?: (this: Pokemon) => void;
313
313
  calculateStat?: (this: Pokemon, statName: StatIDExceptHP, boost: number, modifier?: number) => number;
314
314
  cureStatus?: (this: Pokemon, silent?: boolean) => boolean;
315
+ formeChange?: (
316
+ this: Pokemon, speciesId: string | Species, source: Effect, isPermanent?: boolean, message?: string
317
+ ) => boolean;
315
318
  getAbility?: (this: Pokemon) => Ability;
316
319
  getActionSpeed?: (this: Pokemon) => number;
317
320
  getItem?: (this: Pokemon) => Item;
@@ -543,6 +543,10 @@ export class TeamValidator {
543
543
  tierSpecies = dex.species.get('Kyogre-Primal');
544
544
  } else if (canMegaEvo && species.id === 'rayquaza' && set.moves.map(toID).includes('dragonascent' as ID)) {
545
545
  tierSpecies = dex.species.get('Rayquaza-Mega');
546
+ } else if (item.id === 'rustedsword' && species.id === 'zacian') {
547
+ tierSpecies = dex.species.get('Zacian-Crowned');
548
+ } else if (item.id === 'rustedshield' && species.id === 'zamazenta') {
549
+ tierSpecies = dex.species.get('Zamazenta-Crowned');
546
550
  }
547
551
  }
548
552
 
@@ -1315,10 +1319,10 @@ export class TeamValidator {
1315
1319
  const crowned: {[k: string]: string} = {
1316
1320
  'Zacian-Crowned': 'behemothblade', 'Zamazenta-Crowned': 'behemothbash',
1317
1321
  };
1318
- if (set.species in crowned) {
1319
- const ironHead = set.moves.map(toID).indexOf('ironhead' as ID);
1320
- if (ironHead >= 0) {
1321
- set.moves[ironHead] = crowned[set.species];
1322
+ if (species.name in crowned) {
1323
+ const behemothMove = set.moves.map(toID).indexOf(crowned[species.name] as ID);
1324
+ if (behemothMove >= 0) {
1325
+ set.moves[behemothMove] = 'ironhead';
1322
1326
  }
1323
1327
  }
1324
1328
  return problems;
@@ -1328,6 +1332,14 @@ export class TeamValidator {
1328
1332
  const dex = this.dex;
1329
1333
  const ruleTable = this.ruleTable;
1330
1334
 
1335
+ // https://www.smogon.com/forums/posts/8659168
1336
+ if (
1337
+ (tierSpecies.id === 'zamazentacrowned' && species.id === 'zamazenta') ||
1338
+ (tierSpecies.id === 'zaciancrowned' && species.id === 'zacian')
1339
+ ) {
1340
+ species = tierSpecies;
1341
+ }
1342
+
1331
1343
  setHas['pokemon:' + species.id] = true;
1332
1344
  setHas['basepokemon:' + toID(species.baseSpecies)] = true;
1333
1345
 
@@ -1,4 +1,5 @@
1
1
  import {ObjectReadWriteStream} from '../../lib/streams';
2
+ import {ID} from '../exported-global-types';
2
3
  import {Dex, toID} from '../dex';
3
4
  import {PRNG, PRNGSeed} from '../prng';
4
5
  import {RandomPlayerAI} from './random-player-ai';
@@ -19,10 +20,18 @@ export interface ExhaustiveRunnerOptions {
19
20
  maxGames?: number;
20
21
  maxFailures?: number;
21
22
  dual?: boolean | 'debug';
23
+ possible?: ExhaustiveRunnerPossibilites;
22
24
  runner?: (options: RunnerOptions) => Promise<void>;
23
25
  cmd?: (cycles: number, format: string, seed: string) => string;
24
26
  }
25
27
 
28
+ export interface ExhaustiveRunnerPossibilites {
29
+ pokemon?: ID[];
30
+ items?: ID[];
31
+ abilities?: ID[];
32
+ moves?: ID[];
33
+ }
34
+
26
35
  export class ExhaustiveRunner {
27
36
  static readonly DEFAULT_CYCLES = 1;
28
37
  static readonly MAX_FAILURES = 10;
@@ -46,6 +55,7 @@ export class ExhaustiveRunner {
46
55
  private readonly maxGames?: number;
47
56
  private readonly maxFailures?: number;
48
57
  private readonly dual: boolean | 'debug';
58
+ private readonly possible?: ExhaustiveRunnerPossibilites;
49
59
 
50
60
  private readonly runner: (options: RunnerOptions) => Promise<void>;
51
61
  private readonly cmd: (cycles: number, format: string, seed: string) => string;
@@ -63,6 +73,7 @@ export class ExhaustiveRunner {
63
73
  this.maxFailures = options.maxFailures || ExhaustiveRunner.MAX_FAILURES;
64
74
  this.dual = options.dual || false;
65
75
  this.runner = options.runner || ((o: RunnerOptions) => new Runner(o).run());
76
+ this.possible = options.possible;
66
77
  this.cmd = options.cmd || ((cycles: number, format: string, seed: string) =>
67
78
  `node tools/simulate exhaustive --cycles=${cycles} --format=${format} --seed=${seed}`);
68
79
 
@@ -93,7 +104,7 @@ export class ExhaustiveRunner {
93
104
  error: true,
94
105
  });
95
106
 
96
- if (this.log) this.logProgress(pools);
107
+ if (this.log) this.logProgress(dex, pools);
97
108
  } catch (err) {
98
109
  this.failures++;
99
110
  console.error(
@@ -110,23 +121,31 @@ export class ExhaustiveRunner {
110
121
 
111
122
  private createPools(dex: typeof Dex): Pools {
112
123
  return {
113
- pokemon: new Pool(ExhaustiveRunner.onlyValid(dex.gen, dex.data.Pokedex, p => dex.species.get(p),
114
- (_, p) => (p.name !== 'Pichu-Spiky-eared' && p.name.substr(0, 8) !== 'Pikachu-')), this.prng),
115
- items: new Pool(ExhaustiveRunner.onlyValid(dex.gen, dex.data.Items, i => dex.items.get(i)), this.prng),
116
- abilities: new Pool(ExhaustiveRunner.onlyValid(dex.gen, dex.data.Abilities, a => dex.abilities.get(a)), this.prng),
117
- moves: new Pool(ExhaustiveRunner.onlyValid(dex.gen, dex.data.Moves, m => dex.moves.get(m),
118
- m => (m !== 'struggle' && (m === 'hiddenpower' || m.substr(0, 11) !== 'hiddenpower'))), this.prng),
124
+ pokemon: new Pool(this.possible?.pokemon ??
125
+ ExhaustiveRunner.onlyValid(dex.gen, dex.data.Pokedex, p => dex.species.get(p), (_, p) =>
126
+ (p.name !== 'Pichu-Spiky-eared' && p.name.substr(0, 8) !== 'Pikachu-')), this.prng),
127
+ items: new Pool(this.possible?.items ??
128
+ ExhaustiveRunner.onlyValid(dex.gen, dex.data.Items, i => dex.items.get(i)), this.prng),
129
+ abilities: new Pool(this.possible?.abilities ??
130
+ ExhaustiveRunner.onlyValid(dex.gen, dex.data.Abilities, a => dex.abilities.get(a)), this.prng),
131
+ moves: new Pool(this.possible?.moves ??
132
+ ExhaustiveRunner.onlyValid(dex.gen, dex.data.Moves, m => dex.moves.get(m), m =>
133
+ (m !== 'struggle' && (m === 'hiddenpower' || m.substr(0, 11) !== 'hiddenpower'))), this.prng),
119
134
  };
120
135
  }
121
136
 
122
- private logProgress(p: Pools) {
137
+ private logProgress(dex: typeof Dex, p: Pools) {
123
138
  // `\r` = return to the beginning of the line
124
139
  // `\x1b[k` (`\e[K`) = clear all characters from cursor position to EOL
125
140
  if (this.games) process.stdout.write('\r\x1b[K');
141
+
142
+ const msg = [`[${this.format}] P:${p.pokemon}`];
143
+ if (dex.gen >= 2) msg.push(`I:${p.items}`);
144
+ if (dex.gen >= 3) msg.push(`A:${p.abilities}`);
145
+ msg.push(`M:${p.moves} = ${this.games}`);
146
+
126
147
  // Deliberately don't print a `\n` character so that we can overwrite
127
- process.stdout.write(
128
- `[${this.format}] P:${p.pokemon} I:${p.items} A:${p.abilities} M:${p.moves} = ${this.games}`
129
- );
148
+ process.stdout.write(msg.join(' '));
130
149
  }
131
150
 
132
151
  private static getSignatures(dex: typeof Dex, pools: Pools): Map<string, {item: string, move?: string}[]> {