First, make sure you have installed node and npm. If you're using Windows, I recommend you use the node Windows installer, and that you install Git for Windows and use git command shell that comes with it (MinGW).
node -v
# 6.0.0 or greater plz!
# inside your bot's directory
npm install leftovers-againTo create your first bot, run node node_modules/leftovers-again/scripts/generate.js.
Note that you'll want to have a good idea of what format you'd like to compete in. Most formats reqire you to choose a valid team for that format (besides ones where your team is randomly assigned to you). If you don't want to worry about team validation, just choose the format 'anythinggoes' for now.
Try battling your bot against a simple opponent - one who chooses moves at random, and one who switches into other Pokemon only when necessary.
npm install
npm start -- --opponent=randumbIf you want a more challenging opponent, use --opponent=stabby - a bot who chooses moves that will do the most damage to you. Check the source code for more included opponents.
If you're not already running a server, you can set one up and start it by doing the following:
git clone https://github.com/dramamine/Pokemon-Showdown.git
npm run serverThis downloads and installs a forked copy of the official Showdown server. The fork makes the server more bot-friendly by removing throttling on challenges and matches.
The team() function should return a string that matches the Smogon format. So, if you build a team on Pokemon Showdown using the Teambuilder, or if you copy Pokemon data from Smogon, you can paste it in this function.
The team() takes team data in other formats - see the code documentation for more details.
Note that if you're playing the randombattle format, you don't need to define team().
The heart of developing a bot lies in processing the state data from each turn / request, and choosing your moves & switches wisely. Check the code documentation for more details.
The preferred way is to return new instances of MOVE or SWITCH. Example code:
import {MOVE, SWITCH} from 'decisions';
return new MOVE(0); // the array index of the move, 0-3
return new MOVE('thunderbolt'); // the move's id
return new MOVE({id: 'thunderbolt', ...}); // a Move object
return new MOVE(state.self.active.moves[3]); // a Move object
return new SWITCH(0); // the array index of the pokemon, 0-5
return new SWITCH('eevee'); // the pokemon's id
return new SWITCH({id: 'eevee', ...}); // a Pokemon object
return new SWITCH(state.self.reserve[5]); // a Pokemon object
The unpreferred way is to return the string '/MOVE 4' or '/SWITCH 6' (1-indexed moves and switches), without using a Decision object.
Besides the docs for state, you'll also wnat to familiarize yourself with what's in a Pokemon object and what's in a Move object
Seriously, read the docs!
state.self.active.conditions = ['fro']
state.self.active.dead = true
[your pokemon].types.indexOf(move.type) >= 0
const Typechart = require('lib/typechart');
Typechart.compare('Normal', 'Fighting'); // 0.5
Typechart.compare('Fire', 'Grass'); // 2
// my move against the opponent
Typechart.compare(move.type, state.opponent.active.types);Say you just cast Swords Dance, which raises your attack by 2 levels. You should find that state.self.active.boosts.atk = 2. Make sure you check if mon.boosts exists before poking at it, since the property boosts is only set when there are boosts.
state.teamPreview = true. When this is set, you need to SWITCH (MOVE decisions are not valid).
If your Pokemon is able to mega-evolve, it will do so by default.
// see if your Pokemon is able to mega-evolve
if (state.self.active.canMegaEvo)
{
// this will keep your Pokemon from mega-evolving this turn
const decision = new MOVE("Brave Bird")
decision.shouldMegaEvo = false
return decision
}If you Pokemon is able to use its Z-Move, it will do so by default when you select the "base move" for that Z-Move.
// see if your Pokemon is able to use a Z-Move
if (state.self.active.canZMove)
{
const zMoveable = state.self.active.moves.find(move => move.zMove)
console.log(zMoveable.name) // ex. "Scald"
console.log(zMoveable.zMove.name) // ex. "Hydro Vortex"
var decision = new MOVE(zMoveable)
decision.shouldZMove = false // don't use Z-Move this turn
return decision
}state.forceSwitch = true, which is an indication that you need to SWITCH instead of MOVE. This can happen in other situations too, due to moves such as Volt Switch or Whirlwind. Make sure you handle this!
You will also find that that Pokemon has these properties: { dead: true, condition: '0/{{maxhp}} fnt', hp: 0, hppct: 0 } You probably care most about 'dead'.
Make sure you're only choosing valid choices.
- If
state.forceSwitchis true, make sure you switch. - Don't switch into a Pokemon with (mon.dead = true) or (mon.active = true)
- Don't send moves with (move.disabled = true)
You can change the logging level when running the bot using '--loglevel=[x]'. 1 returns only errors, 5 shows debug info
Server messages get logged to log/replays/{{timestamp}}-{{battleid}}
States get logged to log/states/{{timestamp}}-{{battleid}}. These can be extremely useful for unit tests and debugging crashes (you can easily call decide() on these JSON objects). TODO: logging isn't like this yet.
npm start -- --production will connect you to the leftovers-again bot server at http://cyberdyne.psim.us. From there, you can play against humans and
You need to publish your bot on NPM, GitHub, or some other git hosting service. I'm manually maintaining these lists for now, so just message marten@metal-heart.org and we'll work from there.
Why don't you solve it yourself and send me a pull request? (Please!)
Otherwise, file an issue. Be sure to include reproduction steps, relevant logs, expected / actual results, etc. Help me help you!
You can set your format in your bot config file; this is not validated before it's sent to the server. The valid names of formats are listed in the official repo. Formats are more about team validation than anything else.
Probably every singles format is functional. Doubles formats aren't implemented yet.
Included in here is library code for the Pokemon Damage Calculator. I adapted it to work with our bot code.
const Damage = require('leftovers-again/src/game/damage')
const estimatedDamage = Damage.getDamageResult(
state.self.active,
state.opponent.active,
move
)
[More sample code here.](https://github.com/dramamine/leftovers-again/blob/master/src/bots/stabby/stabby.js)
## Development Tips
* Want to continuously run battles as you develop code? Use `npm run develop -- {{your bot's name}} [other cmdline args]`, which watches src/ directory and refreshes when those files chage.
* Want to run battles faster? Keep the server and your opponent running instead of restarting them each time. For example, I would keep three tabs open with these commands running:
```bash
npm run server # in separate tab
npm start randumb # in separate tab
npm run develop -- myawesomebot --scrappy(The --scrappy flag specifies that the bot will initiate battles against all users on the server, including users who join later.)
If you're playing random battles:
- 'randumb' is a bot that just picks random moves, and switches into another Pokemon only when necessary.
- 'stabby' picks the strongest attack moves, and switches into another Pokemon only when necessary.
If you're playing other tiers:
- 'randumb' is still a good opponent; it chooses a random team, then random moves etc.
- You may want to set your bot to 'anythinggoes' so that you can test against bots in any tier.
- 'elitefour' bots are included. Check the bots/elitefour directory for full names, ex. 'opponent=elitefour/xy-diantha' TODO
Set --loglevel=[x] to whatever you'd like: 1: erros: catastrophic errors and broken code 2: warnings: stuff you might want to look into; weird server responses; inconsistencies in internal state 3: logs: this is the default level that gets logged to console 4: info: normal server communication updates 5: debug: includes all state objects sent to your bot
Some stuff in the logs directory TODO