Spaces:
Running
Running
/** | |
* Random Simulation harness for testing and benchmarking purposes. | |
* Pokemon Showdown - http://pokemonshowdown.com/ | |
* | |
* Refer to `README.md` for detailed usage instructions. | |
* | |
* @license MIT | |
*/ | |
; | |
if (process.argv[2]) { | |
const help = ['help', '-help', '--help', 'h', '-h', '--help', '?', '-?', '--?'].includes(process.argv[2]); | |
const unknown = !['multi', 'random', 'exhaustive'].includes(process.argv[2]) && !/^[0-9]+$/.test(process.argv[2]); | |
if (help || unknown) { | |
const out = help ? console.log : console.error; | |
if (unknown) out(`Unrecognized command: ${process.argv[2]}\n`); | |
out('tools/simulate random'); | |
out(''); | |
out(' Randomly simulates `--num` total games (default=100).'); | |
out(' The format(s) played and what gets output can be altered.'); | |
out(''); | |
out('tools/simulate exhaustive'); | |
out(''); | |
out(' Plays through enough randomly simulated battles to exhaust'); | |
out(' all options of abilities/items/moves/pokemon. `--cycles` can'); | |
out(' used to run through multiple exhaustions of the options.'); | |
out(''); | |
out('tools/simulate help'); | |
out(''); | |
out(' Displays this reference'); | |
out(''); | |
out('Please refer to tools/SIMULATE.md for full documentation'); | |
process.exit(help ? 0 : 1); | |
} | |
} | |
require('child_process').execSync('node ' + __dirname + "/../../build"); | |
const Dex = require('../../sim/dex').Dex; | |
global.Config = { allowrequestingties: false }; | |
Dex.includeModData(); | |
const { ExhaustiveRunner } = require('../../sim/tools/exhaustive-runner'); | |
const { MultiRandomRunner } = require('../../sim/tools/multi-random-runner'); | |
// Tracks whether some promises threw errors that weren't caught so we can log | |
// and exit with a non-zero status to fail any tests. This "shouldn't happen" | |
// because we're "great at propagating promises (TM)", but better safe than sorry. | |
const RejectionTracker = new class { | |
constructor() { | |
this.unhandled = []; | |
} | |
onUnhandledRejection(reason, promise) { | |
this.unhandled.push({ reason, promise }); | |
} | |
onRejectionHandled(promise) { | |
this.unhandled.splice(this.unhandled.findIndex(u => u.promise === promise), 1); | |
} | |
onExit(code) { | |
let i = 0; | |
for (const u of this.unhandled) { | |
const error = (u.reason instanceof Error) ? u.reason : | |
new Error(`Promise rejected with value: ${u.reason}`); | |
console.error(`UNHANDLED PROMISE REJECTION:\n${error.stack}`); | |
i++; | |
} | |
process.exit(code + i); | |
} | |
register() { | |
process.on('unhandledRejection', (r, p) => this.onUnhandledRejection(r, p)); | |
process.on('rejectionHandled', p => this.onRejectionHandled(p)); | |
process.on('exit', c => this.onExit(c)); // TODO | |
} | |
}(); | |
RejectionTracker.register(); | |
function missing(dep) { | |
try { | |
require.resolve(dep); | |
return false; | |
} catch (err) { | |
if (err.code !== 'MODULE_NOT_FOUND') throw err; | |
return true; | |
} | |
} | |
function shell(cmd) { | |
require('child_process').execSync(cmd, { stdio: 'inherit', cwd: __dirname }); | |
} | |
function parseFlags(argv) { | |
if (!(argv.length > 3 || argv.length === 3 && argv[2].startsWith('-'))) return { _: argv.slice(2) }; | |
if (missing('minimist')) shell('npm install minimist'); | |
return require('minimist')(argv.slice(2)); | |
} | |
if (!process.argv[2] || /^[0-9]+$/.test(process.argv[2])) process.argv.splice(2, 0, 'multi'); | |
switch (process.argv[2]) { | |
case 'multi': | |
case 'random': | |
{ | |
const argv = parseFlags(process.argv); | |
const options = { totalGames: 100, ...argv }; | |
options.totalGames = Number(argv._[1] || argv.num) || options.totalGames; | |
if (argv.seed) options.prng = argv.seed.split(',').map(s => Number(s)); | |
// Run options.totalGames, exiting with the number of games with errors. | |
(async () => process.exit(await new MultiRandomRunner(options).run()))(); | |
} | |
break; | |
case 'exhaustive': | |
{ | |
const argv = parseFlags(process.argv); | |
let formats; | |
if (argv.formats) { | |
formats = argv.formats.split(','); | |
} else if (argv.format) { | |
formats = argv.format.split(','); | |
} else { | |
formats = ExhaustiveRunner.FORMATS; | |
} | |
let cycles = Number(argv._[1] || argv.cycles) || ExhaustiveRunner.DEFAULT_CYCLES; | |
let forever = argv.forever; | |
if (cycles < 0) { | |
cycles = -cycles; | |
forever = true; | |
} | |
const maxFailures = argv.maxFailures || argv.failures || (formats.length > 1 ? ExhaustiveRunner.MAX_FAILURES : 1); | |
const prng = argv.seed && argv.seed.split(',').map(s => Number(s)); | |
const maxGames = argv.maxGames || argv.games; | |
(async () => { | |
let failures = 0; | |
do { | |
for (const format of formats) { | |
failures += await new ExhaustiveRunner({ | |
format, cycles, prng, maxFailures, log: true, dual: argv.dual, maxGames, | |
}).run(); | |
process.stdout.write('\n'); | |
if (failures >= maxFailures) break; | |
} | |
} while (forever); // eslint-disable-line no-unmodified-loop-condition | |
process.exit(failures); | |
})(); | |
} | |
break; | |
default: | |
throw new TypeError('Unknown command' + process.argv[2]); | |
} | |