pokemon-showdown/sim
Guangcong Luo 87ef0cd5c4 TypeScript: export default
I'm currently pretty annoyed at TypeScript and TC39 for default exports
being a mess.

The goal here is to be able to type

    import Dex from './dex';

instead of any of

    import Dex = require('./dex');
    import {Dex} from './dex';
    import * as Dex from './dex';

This part involves a significant amount of struggle.

First, you can't automatically package up all your exports as your
default export. This leads to extremely un-DRY code, like in sim/index:

    export {
        Pokemon,
        Side,
        Battle,
        PRNG,
        Dex,
        TeamValidator,

        BattleStream,
    };

    export const Sim = {
        Pokemon,
        Side,
        Battle,
        PRNG,
        Dex,
        TeamValidator,

        BattleStream,
    };

(Both of these exports would be entirely unnecessary if you could just
automatically declare the file's exports as a default namespace.)

Second, a default export can't easily be a namespace. And TypeScript
doesn't allow types to exist in objects. Take the example from earlier:

    export const Sim = {
        Pokemon,
    };

If we were to try to use it:

    import Sim from './sim';
    let pokemon: Sim.Pokemon;

you'll get this error:

    Cannot find namespace 'Sim'. ts(2503)

You can, of course, fix this by making Sim a namespace:

    const PokemonT = Pokemon;
    type PokemonT = Pokemon;
    export namespace Sim {
        export const Pokemon = PokemonT;
        type Pokemon = PokemonT;
    }

But this quickly gets ridiculous the more classes you try to export.

You'd think there'd be a better way to do this. But I, at least,
haven't found one.
2019-05-14 20:33:33 +10:00
..
examples TypeScript: export default 2019-05-14 20:33:33 +10:00
tools TypeScript: export default 2019-05-14 20:33:33 +10:00
battle-stream.ts TypeScript: export default 2019-05-14 20:33:33 +10:00
battle.ts TypeScript: export default 2019-05-14 20:33:33 +10:00
dex-data.ts toId -> toID 2019-05-12 17:53:01 -07:00
dex.ts TypeScript: export default 2019-05-14 20:33:33 +10:00
field.ts TypeScript: export default 2019-05-14 20:33:33 +10:00
global.d.ts TypeScript: export default 2019-05-14 20:33:33 +10:00
globals.ts TypeScript: export default 2019-05-14 20:33:33 +10:00
index.ts TypeScript: export default 2019-05-14 20:33:33 +10:00
pokemon.ts TypeScript: export default 2019-05-14 20:33:33 +10:00
prng.ts TypeScript: export default 2019-05-14 20:33:33 +10:00
README.md Change protocol for '|split' message type (#5331) (#5332) 2019-04-22 09:20:47 +09:30
side.ts TypeScript: export default 2019-05-14 20:33:33 +10:00
SIM-PROTOCOL.md Remove |callback| in favor of |error| (#5418) 2019-04-09 03:33:32 +08:00
state.ts TypeScript: export default 2019-05-14 20:33:33 +10:00
team-validator.ts TypeScript: export default 2019-05-14 20:33:33 +10:00

Simulator

Pokémon Showdown's simulator API is implemented as a ReadWriteStream. You write player choices to it, and you read protocol messages from it.

const Sim = require('Pokemon-Showdown/sim');
stream = new Sim.BattleStream();

(async () => {
    let output;
    while ((output = await stream.read())) {
        console.log(output);
    }
})();

stream.write(`>start {"formatid":"gen7randombattle"}`);
stream.write(`>player p1 {"name":"Alice"}`);
stream.write(`>player p2 {"name":"Bob"}`);

The stream can be accessed from other programming languages using standard IO:

echo '>start {"formatid":"gen7randombattle"}
>player p1 {"name":"Alice"}
>player p2 {"name":"Bob"}
' | ./pokemon-showdown simulate-battle

Writing to the simulator

In a standard battle, what you write to the simulator looks something like this:

>start {"formatid":"gen7ou"}
>player p1 {"name":"Alice","team":"insert packed team here"}
>player p2 {"name":"Bob","team":"insert packed team here"}
>p1 team 123456
>p2 team 123456
>p1 move 1
>p2 switch 3
>p1 move 3
>p2 move 2

(In a data stream, messages should be delimited by \n; in an object stream, \n will be implicitly added after every message.)

Notice that every line starts with >. Lines not starting with > are comments, so that input logs can be mixed with output logs and/or normal text easily.

Note that the text after >p1, >p2, >p3, or >p4 can be untrusted input directly from the player, and should be treated accordingly.

Possible message types include:

>start OPTIONS

Starts a battle:

OPTIONS is a JSON object containing the following properties (optional, except formatid):

  • formatid - a string representing the format ID

  • seed - an array of four numbers representing a seed for the random number generator (defaults to a random seed)

  • p1 - PLAYEROPTIONS for player 1 (defaults to no player; player options must then be passed with >player p1)

  • p2 - PLAYEROPTIONS for player 2 (defaults to no player; player options must then be passed with >player p2)

  • p3 - PLAYEROPTIONS for player 3 (defaults to no player; player options must then be passed with >player p3)

  • p4 - PLAYEROPTIONS for player 4 (defaults to no player; player options must then be passed with >player p4)

If p1 and p2 (and p3 and p4 for 4 player battles) are specified, the battle will begin immediately. Otherwise, they must be specified with >player before the battle will begin.

See documentation of >player (below) for PLAYEROPTIONS.

>player PLAYERID PLAYEROPTIONS

Sets player information:

PLAYERID is p1, p2, p3, or p4

PLAYEROPTIONS is a JSON object containing the following properties (all optional):

  • name is a string for the player name (defaults to "Player 1" or "Player 2")

  • avatar is a string for the player avatar (defaults to "")

  • team is a team (either in JSON or a string in packed format)

>p1 CHOICE
>p2 CHOICE
>p3 CHOICE
>p4 CHOICE

Makes a choice for a player. Possible choices are documented in SIM-PROTOCOL.md.

Reading from the simulator

The simulator will send back messages. In a data stream, they're delimited by \n\n. In an object stream, they will just be sent as separate strings.

Messages start with a message type followed by \n. A message will never have two \n in a row, so that \n\n always delimits a They look like:

update
MESSAGES

An update which should be sent to all players and spectators.

The messages the simulator sends back are documented in SIM-PROTOCOL.md. You can also look at a replay log for examples.

One message type that only appears here is |split|PLAYERID:

|split|PLAYERID
SECRET
PUBLIC
  • PLAYERID - one of p1, p2, p3, or p4.

  • SECRET - messages for the specific player or an omniscient observer (details which may contain information about exact details of the player's set, like exact HP)

  • PUBLIC - message with public details suitable for display to opponents / teammates / spectators. Note that this may be empty.

    sideupdate PLAYERID MESSAGES

Send messages to only one player. |split will never appear here.

PLAYERID will be p1, p2, p3, or p4.

Note that choice requests (updates telling the player what choices they have for using moves or switching pokemon) are sent this way.

Choice requests are documented in "Choice requests" in SIM-PROTOCOL.md.

end
LOGDATA

Sent at the end of a battle. LOGDATA is a JSON object that has various information you might find useful but are too lazy to extract from the update messages, such as turn count and winner name.