1.0.1 • Published 4 years ago

survivalcalc v1.0.1

Weekly downloads
-
License
MIT
Repository
-
Last release
4 years ago

SurvivalCalc-Core

This is the repository for the SurvivalCalc's computation engine.

The Pokémon Attack Survival Calculator (SurvivalCalc) is a tool created for the competitive Pokémon community. It is designed to compute the optimal distribution of defensive EVs while surviving specific attacks. Refer here for an introduction to the original SurvivalCalc, and see below for links to the current SurvivalCalc implementations on the web:

For information on how to use these calculators, see the notes below the calculators on their respective webpages.

Installation

Install via npm with npm i survivalcalc.

One can also download the source and use the provided webpack.config.js to generate a bundle for use in the browser. Run npx webpack and bundle.js will be created in the dist directory. By default the library is exported as scc, so one would use scc.findMinEVs(...) or scc.findBestEVs(...).

Usage

The SurvivalCalc-Core exposes two primary functions: findMinEVs and findBestEVs. Both utilize the following APIs:

  • SurvivalCalc-Core
    • Requirements: specifies SurvivalRequirements and an HPRequirement the EV spread should meet
      • SurvivalRequirement: specifies what Attacks a defending Pokémon is to survive with a percentage of HP remaining a percentage of the time
      • HPRequirement: specifies passive damage reducing HP numbers the defending Pokémon should have (e.g. reduce weather damage, burn damage)
    • Attack: specifies an attacking Pokémon, defending Pokémon, the Move used, and any Field conditions
    • Spread: representation of an EV spread with HP, Defense, and Special Defense. Returned by findMinEVs and findBestEVs. Fields are accessed with the Stat enumeration
    • BulkLoss: optional loss function for use by findBestEVs. Further description found under the findBestEVs section
  • @smogon/calc: Smogon's Damage Calculator API
    • Pokemon: representation of a Pokémon
    • Move: representation of a Pokémon's move
    • Field: representation of the field on which an attack occurs

The SurvivalCalc-Core components listed above are re-exported in src/index.ts. Hence, one would write

import {
    findMinEVs, findBestEVs,
    Requirements, SurvivalRequirement, HPRequirement,
    Spread, Stat, BulkLoss
} from 'survivalcalc';
import { Pokemon, Move, Field } from '@smogon/calc'

to import all of the components one might need.

Requirements

The Requirements object specifies what requirements should be met by an EV spread. There are two kinds of requirements that can be met: SurvivalRequirements and HPRequirements.

SurvivalRequirement

This object specifies what the defending Pokémon should survive, the percentage of HP remaining the defending Pokémon should have, and the percentage of the time it should have the specified percentage of HP remaining.

Suppose we wish our Assault Vest Conkeldurr to survive Togekiss' Air Slash followed by Dragapult's Dragon Darts at level 50. We wish for our Conkeldurr to survive the series of attacks with .01% of its HP remaining, 100% of the time. We would use

// Construct attackers, defenders, and moves
const togekiss = new Pokemon(8, 'Togekiss', {
    level: 50,
    evs: {
        [Stat.SATK]: 252
    }
});
const airSlash = new Move(8, 'Air Slash');

const dragapult = new Pokemon(8, 'Dragapult', {
    level: 50,
    evs: {
        [Stat.ATK]: 252
    }
});
const dragonDarts = new Move(8, 'Dragon Darts');

const conkeldurr = new Pokemon(8, 'Conkeldurr', {
    level: 50,
    item: 'Assault Vest'
});

// Construct Attack objects
const attack1 = new Attack(togekiss, conkeldurr, airSlash);
const attack2 = new Attack(dragapult, conkeldurr, dragonDarts);

// Specify the SurvivalRequirement
// Repeat attack2 as there are two hits of the 50 BP Dragon Darts
const survivalReq = new SurvivalRequirement(.01, 100, attack1, attack2, attack2);

HPRequirement

This object specifies any passive damage reduction numbers the defending Pokémon's HP stat should satisfy. For example, to minimize Life Orb recoil the HP stat should equal 10n - 1. To specify this, we would use

const hpReq = new HPRequirement({ reduceLifeOrb: true });

See src/model/requirements.ts for the full specification. At the current time, it is recommended to select up to one special HP number, as choosing more than one may restrict the set of possibilities. Future work may involve allowing for combinations of special HP numbers (e.g. Sitrus Berry after Super Fang and Reduce Life Orb Recoil ==> 10n - 2).

Requirements

The Requirements object simply bundles the HPRequirement and any number of SurvivalRequirements together. Continuing our above examples, we would have

const reqs = new Requirements(hpReq, survivalReq);

findMinEVs

The findMinEVs function finds the best EV spread which meets the specified requirements while using the minimum number of total EVs. The function signature is

findMinEVs(reqs: Requirements): Spread

To continue our example with Conkeldurr, we would simply call

const minSpread: Spread = findMinEVs(reqs);

console.log(minSpread['hp']); // 228
console.log(minSpread['def']); // 28
console.log(minSpread['sd']); // 164

As another example, suppose we wish to find the minimum number of EVs necessary for Haban Berry Garchomp to survive Latios' Life Orb Draco Meteor at level 50. We would use

import {
    SurvivalRequirement, HPRequirement, Requirements,
    Attack, Spread, Stat,
    findMinEVs
} from 'survivalcalc';
import { Pokemon, Move } from '@smogon/calc';

// Construct the Attack object
const latios = new Pokemon(8, 'Latios', {
    level: 50,
    evs: {
        [Stat.SATK]: 252
    },
    item: 'Life Orb'
});
const draco = new Move(8, 'Draco Meteor');

const garchomp = new Pokemon(8, 'Garchomp', {
    level: 50,
    item: 'Haban Berry'
});

const attack = new Attack(latios, garchomp, draco);

// Requirements: no special HP numbers,
// survive with .01% of HP remaining 100% of the time
const reqs = new Requirements(new HPRequirement(),
                              new SurvivalRequirement(.01, 100, attack));
// Find the minimal spread
const minSpread = findMinEVs(reqs);
console.log(minSpread[Stat.HP]); // 4
console.log(minSpread[Stat.DEF]); // 0
console.log(minSpread[Stat.SDEF]); // 52

findBestEVs

The findBestEVs function finds the best EV spread which meets the specified requirements while using up to a specified number of EVs. The function signature is

findBestEVs(reqs: Requirements, optimizer: Attack[] | BulkLoss, maxEVs: number): Spread

where reqs is the set of Requirements to be met, optimizer determines how EV spreads which meet the requirements are selected (described below), and maxEVs is the number of EVs available for use.

Assuming the allowed number of EVs is greater than the minimum number necessary to meet the requirements, there are likely many possible spreads which meet the requirements. Therefore, an additional optimizer argument is required which determines which EV spread to select out of those which meet the requirements. The optimizer comes in two forms: an Attack[], or a BulkLoss.

Attack[]

If an array of Attacks is specified, findBestEVs will find the spread which meets the requirements and minimizes the damage from the Attack array.

For example, suppose we want our Amoonguss to survive a Mega Metagross' Zen Headbutt, but we want to minimize the damage dealt by Cresselia's Psychic. We would use Metagross' Zen Headbutt in our SurvivalRequirement, but supply as our optimizer

const toMinimize = [new Attack(cresselia, amoonguss, psychic)];

Finding the spread which minimizes damage is computed by the following rules in descending order of importance 1. Maximizing the number of hits required to KO (maximizing X in an XHKO) 2. Minimizing the chance to get XHKO'd 3. Minimizing the percentage of damage dealt

BulkLoss

If an instance of BulkLoss is specified, findBestEVs will find the spread which meets the requirements and maximizes general bulk (as opposed to damage from a specific series of attacks).

The BulkLoss class computes how "bad" a spread is when trying to maximize bulk. It takes as arguments the defending Pokémon and a weight argument in 0, 1 which determines how much attention is paid to Defense and Special Defense. A weight of 0 maximizes defensive bulk, while a weight of 1 maximizes special defensive bulk. A weight of .5 balances the bulk evenly.

Continuing with our previous example, if we want our Amoonguss to survive Mega Metagross' Zen Headbutt but try to balance our defensive and special defensive bulk, our optimizer would be

const toMinimize = new BulkLoss(amoonguss, .5);

If we want to throw all of our remaining bulk into Special Defense after surviving the Zen Headbutt, we would use

const toMinimize = new BulkLoss(amoonguss, 1);

Finally, if we want to ensure that our Amoonguss survives the Zen Headbutt and maximize our Defense, we would use

const toMinimize = new BulkLoss(amoonguss, 0);

Credits

All iterations of the SurvivalCalc were created by Stats.

The SurvivalCalc would not be possible without Honko's Damage Calculator and its npm package. Many thanks to its developers and maintainers.

The theory behind distributing EVs for defenses as implemented in this calculator was developed collaboratively with DaWoblefet. DaWoblefet's ingenuity and support have been invaluable throughout the years.

The following list enumerates some resources that were helpful to the author.