File size: 4,703 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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
'use strict';

const path = require('path');
const fs = require('fs');
const assert = require('./assert');
const Sim = require('./../dist/sim');
const Dex = Sim.Dex;

const cache = new Map();
const formatsCache = new Map();

function capitalize(word) {
	return word.charAt(0).toUpperCase() + word.slice(1);
}

/**
 * The default random number generator seed used if one is not given.
 */
const DEFAULT_SEED = 'gen5,99176924e1c86af0';

class TestTools {
	constructor(mod = 'base') {
		this.currentMod = mod;
		this.dex = Dex.mod(mod);

		this.modPrefix = this.dex.isBase ? `[gen9] ` : `[${mod}] `;
	}

	mod(mod) {
		if (cache.has(mod)) return cache.get(mod);

		if (typeof mod !== 'string') throw new Error("This only supports strings");
		if (!Dex.dexes[mod]) throw new Error(`Mod ${mod} does not exist`);

		const moddedTestTools = new TestTools(mod);
		cache.set(mod, moddedTestTools);
		return moddedTestTools;
	}

	gen(genNum) {
		return this.mod('gen' + genNum);
	}

	getFormat(options) {
		if (options.formatid) {
			const format = Dex.formats.get(options.formatid);
			if (format.effectType !== 'Format') throw new Error(`Unidentified format: ${options.formatid}`);
			return format;
		}

		const gameType = Dex.toID(options.gameType || 'singles');
		const customRules = [
			options.pokemon && '-Nonexistent',
			options.legality && 'Obtainable',
			!options.preview && '!Team Preview',
			options.sleepClause && 'Sleep Clause Mod',
			!options.cancel && '!Cancel Mod',
			options.endlessBattleClause && 'Endless Battle Clause',
			options.inverseMod && 'Inverse Mod',
			options.overflowStatMod && 'Overflow Stat Mod',
		].filter(Boolean);
		const customRulesID = customRules.length ? `@@@${customRules.join(',')}` : ``;

		let basicFormat = this.currentMod === 'base' && gameType === 'singles' ? 'Anything Goes' : 'Custom Game';
		let modPrefix = this.modPrefix;
		if (this.currentMod === 'gen1stadium') basicFormat = 'OU';
		if (gameType === 'multi') {
			basicFormat = 'randombattle';
			modPrefix = `[gen8] `; // Remove when multis support Gen 9
		}
		// Re-integrate to the above if statement when gen 9 ffa randbats is added
		if (gameType === 'freeforall') basicFormat = '';
		const gameTypePrefix = gameType === 'singles' ? '' : capitalize(gameType) + ' ';
		const formatName = `${modPrefix}${gameTypePrefix}${basicFormat}${customRulesID}`;

		let format = formatsCache.get(formatName);
		if (format) return format;

		format = Dex.formats.get(formatName);
		if (format.effectType !== 'Format') throw new Error(`Unidentified format: ${formatName}`);

		formatsCache.set(formatName, format);
		return format;
	}

	/**
	 * Creates a new Battle and returns it.
	 *
	 * @param {Object} [options]
	 * @param {Team[]} [teams]
	 * @returns {Sim.Battle} A battle.
	 */
	createBattle(options, teams) {
		if (Array.isArray(options)) {
			teams = options;
			options = {};
		}
		if (!options) options = {};
		const format = this.getFormat(options);

		const battleOptions = {
			debug: true,
			forceRandomChance: options.forceRandomChance,
			format,
			// If a seed for the pseudo-random number generator is not provided,
			// a default seed (guaranteed to be the same across test executions)
			// will be used.
			seed: options.seed === undefined ? DEFAULT_SEED : (options.seed || undefined),
			strictChoices: options.strictChoices !== false,
		};

		if (!teams) return new Sim.Battle(battleOptions);

		for (let i = 0; i < teams.length; i++) {
			assert(Array.isArray(teams[i]), `Team provided is not an array`);
			const playerSlot = `p${i + 1}`;
			battleOptions[playerSlot] = { team: teams[i] };
		}

		return new Sim.Battle(battleOptions);
	}

	/**
	 * Saves the log of the given battle as a bare-bones replay file in the `test\replays` directory
	 * You can view the replay by opening the file in any browser or by dragging and dropping the
	 * file into a PS! client window.
	 *
	 * @param {Sim.Battle} battle
	 * @param {string} [fileName]
	 */
	saveReplay(battle, fileName) {
		const battleLog = battle.getDebugLog();
		if (!fileName) fileName = 'test-replay';
		const filePath = path.resolve(__dirname, `./replays/${fileName}-${Date.now()}.html`);
		const out = fs.createWriteStream(filePath, { flags: 'a' });
		out.on('open', () => {
			out.write(
				`<!DOCTYPE html>\n` +
				`<script type="text/plain" class="battle-log-data">${battleLog}</script>\n` +
				`<script src="https://play.pokemonshowdown.com/js/replay-embed.js"></script>\n`
			);
			out.end();
		});
	}
	hasModule(mod) {
		try {
			require(mod);
			return true;
		} catch {
			return false;
		}
	}
}

const common = exports = module.exports = new TestTools();
cache.set('base', common);
cache.set('gen9', common);