File size: 2,869 Bytes
5c2ed06
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
/**
 * Battle Simulator multi random runner.
 * Pokemon Showdown - http://pokemonshowdown.com/
 *
 * @license MIT
 */

import { PRNG, type PRNGSeed } from '../prng';
import { Runner, type RunnerOptions } from './runner';

export interface MultiRandomRunnerOptions extends Partial<RunnerOptions> {
	totalGames: number;
	prng?: PRNG | PRNGSeed | null;
	format?: string;
	cycle?: boolean;
	all?: boolean;
	async?: boolean;
}

export class MultiRandomRunner {
	static readonly FORMATS = [
		'gen8randombattle', 'gen8randomdoublesbattle', 'gen8battlefactory',
		'gen7randombattle', 'gen7battlefactory',
		'gen6randombattle', 'gen6battlefactory',
		'gen5randombattle',
		'gen4randombattle',
		'gen3randombattle',
		'gen2randombattle',
		'gen1randombattle',
	];

	private readonly options: Partial<RunnerOptions>;
	private readonly totalGames: number;
	private readonly prng: PRNG;
	private readonly format: string | undefined;
	private readonly cycle: boolean;
	private readonly all: boolean;
	private readonly isAsync: boolean;

	private formatIndex: number;
	private numGames: number;

	constructor(options: MultiRandomRunnerOptions) {
		this.options = { ...options };

		this.totalGames = options.totalGames;

		this.prng = PRNG.get(options.prng);
		this.options.prng = this.prng;

		this.format = options.format;
		this.cycle = !!options.cycle;
		this.all = !!options.all;

		this.isAsync = !!options.async;

		this.formatIndex = 0;
		this.numGames = 0;
	}

	async run() {
		let games = [];
		let format: string | false;
		let lastFormat: string | false = false;
		let failures = 0;
		while ((format = this.getNextFormat())) {
			if (this.all && lastFormat && format !== lastFormat) {
				if (this.isAsync) await Promise.all(games);
				games = [];
			}

			const seed = this.prng.getSeed();
			const game = new Runner({ format, ...this.options }).run().catch(err => {
				failures++;
				console.error(
					`Run \`node tools/simulate multi 1 --format=${format} --seed=${seed}\` ` +
					`to debug (optionally with \`--output\` and/or \`--input\` for more info):\n`,
					err
				);
			});

			if (!this.isAsync) await game;
			games.push(game);
			lastFormat = format;
		}

		if (this.isAsync) await Promise.all(games);
		return failures;
	}

	private getNextFormat() {
		const FORMATS = MultiRandomRunner.FORMATS;
		if (this.formatIndex > FORMATS.length) return false;

		if (this.numGames++ < this.totalGames) {
			if (this.format) {
				return this.format;
			} else if (this.all) {
				return FORMATS[this.formatIndex];
			} else if (this.cycle) {
				const format = FORMATS[this.formatIndex];
				this.formatIndex = (this.formatIndex + 1) % FORMATS.length;
				return format;
			} else {
				return this.prng.sample(FORMATS);
			}
		} else if (this.all) {
			this.numGames = 1;
			this.formatIndex++;
			return FORMATS[this.formatIndex];
		}

		return false;
	}
}