{ "version": 3, "sources": ["../../../../sim/tools/runner.ts"], "sourcesContent": ["/**\r\n * Battle Simulator runner.\r\n * Pokemon Showdown - http://pokemonshowdown.com/\r\n *\r\n * @license MIT\r\n */\r\n\r\nimport {strict as assert} from 'assert';\r\nimport * as fs from 'fs';\r\n\r\nimport {Dex} from '..';\r\nimport {ObjectReadWriteStream} from '../../lib/streams';\r\nimport {Battle} from '../battle';\r\nimport * as BattleStreams from '../battle-stream';\r\nimport {State} from '../state';\r\nimport {PRNG, PRNGSeed} from '../prng';\r\nimport {RandomPlayerAI} from './random-player-ai';\r\n\r\nexport interface AIOptions {\r\n\tcreateAI: (stream: ObjectReadWriteStream, options: AIOptions) => RandomPlayerAI;\r\n\tmove?: number;\r\n\tmega?: number;\r\n\tseed?: PRNG | PRNGSeed | null;\r\n\tteam?: PokemonSet[];\r\n}\r\n\r\nexport interface RunnerOptions {\r\n\tformat: string;\r\n\tprng?: PRNG | PRNGSeed | null;\r\n\tp1options?: AIOptions;\r\n\tp2options?: AIOptions;\r\n\tp3options?: AIOptions;\r\n\tp4options?: AIOptions;\r\n\tinput?: boolean;\r\n\toutput?: boolean;\r\n\terror?: boolean;\r\n\tdual?: boolean | 'debug';\r\n}\r\n\r\nexport class Runner {\r\n\tstatic readonly AI_OPTIONS: AIOptions = {\r\n\t\tcreateAI: (s: ObjectReadWriteStream, o: AIOptions) => new RandomPlayerAI(s, o),\r\n\t\tmove: 0.7,\r\n\t\tmega: 0.6,\r\n\t};\r\n\r\n\tprivate readonly prng: PRNG;\r\n\tprivate readonly p1options: AIOptions;\r\n\tprivate readonly p2options: AIOptions;\r\n\tprivate readonly p3options: AIOptions;\r\n\tprivate readonly p4options: AIOptions;\r\n\tprivate readonly format: string;\r\n\tprivate readonly input: boolean;\r\n\tprivate readonly output: boolean;\r\n\tprivate readonly error: boolean;\r\n\tprivate readonly dual: boolean | 'debug';\r\n\r\n\tconstructor(options: RunnerOptions) {\r\n\t\tthis.format = options.format;\r\n\r\n\t\tthis.prng = (options.prng && !Array.isArray(options.prng)) ?\r\n\t\t\toptions.prng : new PRNG(options.prng);\r\n\t\tthis.p1options = {...Runner.AI_OPTIONS, ...options.p1options};\r\n\t\tthis.p2options = {...Runner.AI_OPTIONS, ...options.p2options};\r\n\t\tthis.p3options = {...Runner.AI_OPTIONS, ...options.p3options};\r\n\t\tthis.p4options = {...Runner.AI_OPTIONS, ...options.p4options};\r\n\r\n\t\tthis.input = !!options.input;\r\n\t\tthis.output = !!options.output;\r\n\t\tthis.error = !!options.error;\r\n\t\tthis.dual = options.dual || false;\r\n\t}\r\n\r\n\tasync run() {\r\n\t\tconst battleStream = this.dual ?\r\n\t\t\tnew DualStream(this.input, this.dual === 'debug') :\r\n\t\t\tnew RawBattleStream(this.input);\r\n\t\tconst game = this.runGame(this.format, battleStream);\r\n\t\tif (!this.error) return game;\r\n\t\treturn game.catch(err => {\r\n\t\t\tconsole.log(`\\n${battleStream.rawInputLog.join('\\n')}\\n`);\r\n\t\t\tthrow err;\r\n\t\t});\r\n\t}\r\n\r\n\tprivate async runGame(format: string, battleStream: RawBattleStream | DualStream) {\r\n\t\t// @ts-ignore - DualStream implements everything relevant from BattleStream.\r\n\t\tconst streams = BattleStreams.getPlayerStreams(battleStream);\r\n\t\tconst spec = {formatid: format, seed: this.prng.seed};\r\n\t\tconst is4P = Dex.formats.get(format).gameType === 'multi';\r\n\t\tconst p1spec = this.getPlayerSpec(\"Bot 1\", this.p1options);\r\n\t\tconst p2spec = this.getPlayerSpec(\"Bot 2\", this.p2options);\r\n\t\tlet p3spec: typeof p1spec, p4spec: typeof p1spec;\r\n\t\tif (is4P) {\r\n\t\t\tp3spec = this.getPlayerSpec(\"Bot 3\", this.p3options);\r\n\t\t\tp4spec = this.getPlayerSpec(\"Bot 4\", this.p4options);\r\n\t\t}\r\n\r\n\t\tconst p1 = this.p1options.createAI(\r\n\t\t\tstreams.p1, {seed: this.newSeed(), ...this.p1options}\r\n\t\t);\r\n\t\tconst p2 = this.p2options.createAI(\r\n\t\t\tstreams.p2, {seed: this.newSeed(), ...this.p2options}\r\n\t\t);\r\n\t\tlet p3: RandomPlayerAI, p4: RandomPlayerAI;\r\n\t\tif (is4P) {\r\n\t\t\tp3 = this.p4options.createAI(\r\n\t\t\t\tstreams.p3, {seed: this.newSeed(), ...this.p3options}\r\n\t\t\t);\r\n\t\t\tp4 = this.p4options.createAI(\r\n\t\t\t\tstreams.p4, {seed: this.newSeed(), ...this.p4options}\r\n\t\t\t);\r\n\t\t}\r\n\t\t// TODO: Use `await Promise.race([streams.omniscient.read(), p1, p2])` to avoid\r\n\t\t// leaving these promises dangling once it no longer causes memory leaks (v8#9069).\r\n\t\tvoid p1.start();\r\n\t\tvoid p2.start();\r\n\t\tif (is4P) {\r\n\t\t\tvoid p3!.start();\r\n\t\t\tvoid p4!.start();\r\n\t\t}\r\n\r\n\t\tlet initMessage = `>start ${JSON.stringify(spec)}\\n` +\r\n\t\t`>player p1 ${JSON.stringify(p1spec)}\\n` +\r\n\t\t`>player p2 ${JSON.stringify(p2spec)}`;\r\n\t\tif (is4P) {\r\n\t\t\tinitMessage += `\\n` +\r\n\t\t\t`>player p3 ${JSON.stringify(p3spec!)}\\n` +\r\n\t\t\t`>player p4 ${JSON.stringify(p4spec!)}`;\r\n\t\t}\r\n\t\tvoid streams.omniscient.write(initMessage);\r\n\r\n\t\tfor await (const chunk of streams.omniscient) {\r\n\t\t\tif (this.output) console.log(chunk);\r\n\t\t}\r\n\t\treturn streams.omniscient.writeEnd();\r\n\t}\r\n\r\n\t// Same as PRNG#generatedSeed, only deterministic.\r\n\t// NOTE: advances this.prng's seed by 4.\r\n\tprivate newSeed(): PRNGSeed {\r\n\t\treturn [\r\n\t\t\tMath.floor(this.prng.next() * 0x10000),\r\n\t\t\tMath.floor(this.prng.next() * 0x10000),\r\n\t\t\tMath.floor(this.prng.next() * 0x10000),\r\n\t\t\tMath.floor(this.prng.next() * 0x10000),\r\n\t\t];\r\n\t}\r\n\r\n\tprivate getPlayerSpec(name: string, options: AIOptions) {\r\n\t\tif (options.team) return {name, team: options.team};\r\n\t\treturn {name, seed: this.newSeed()};\r\n\t}\r\n}\r\n\r\nclass RawBattleStream extends BattleStreams.BattleStream {\r\n\treadonly rawInputLog: string[];\r\n\r\n\tprivate readonly input: boolean;\r\n\r\n\tconstructor(input: boolean) {\r\n\t\tsuper();\r\n\t\tthis.input = !!input;\r\n\t\tthis.rawInputLog = [];\r\n\t}\r\n\r\n\t_write(message: string) {\r\n\t\tif (this.input) console.log(message);\r\n\t\tthis.rawInputLog.push(message);\r\n\t\tsuper._write(message);\r\n\t}\r\n}\r\n\r\nclass DualStream {\r\n\tprivate debug: boolean;\r\n\tprivate readonly control: RawBattleStream;\r\n\tprivate test: RawBattleStream;\r\n\r\n\tconstructor(input: boolean, debug: boolean) {\r\n\t\tthis.debug = debug;\r\n\t\t// The input to both streams should be the same, so to satisfy the\r\n\t\t// input flag we only need to track the raw input of one stream.\r\n\t\tthis.control = new RawBattleStream(input);\r\n\t\tthis.test = new RawBattleStream(false);\r\n\t}\r\n\r\n\tget rawInputLog() {\r\n\t\tconst control = this.control.rawInputLog;\r\n\t\tconst test = this.test.rawInputLog;\r\n\t\tassert.deepEqual(test, control);\r\n\t\treturn control;\r\n\t}\r\n\r\n\tasync read() {\r\n\t\tconst control = await this.control.read();\r\n\t\tconst test = await this.test.read();\r\n\t\t// In debug mode, wait to catch this as a difference in the inputLog\r\n\t\t// and error there so we get the full battle state dumped instead.\r\n\t\tif (!this.debug) assert.equal(State.normalizeLog(test), State.normalizeLog(control));\r\n\t\treturn control;\r\n\t}\r\n\r\n\twrite(message: string) {\r\n\t\tthis.control._write(message);\r\n\t\tthis.test._write(message);\r\n\t\tthis.compare();\r\n\t}\r\n\r\n\twriteEnd() {\r\n\t\t// We need to compare first because _writeEnd() destroys the battle object.\r\n\t\tthis.compare(true);\r\n\t\tthis.control._writeEnd();\r\n\t\tthis.test._writeEnd();\r\n\t}\r\n\r\n\tcompare(end?: boolean) {\r\n\t\tif (!this.control.battle || !this.test.battle) return;\r\n\r\n\t\tconst control = this.control.battle.toJSON();\r\n\t\tconst test = this.test.battle.toJSON();\r\n\t\ttry {\r\n\t\t\tassert.deepEqual(State.normalize(test), State.normalize(control));\r\n\t\t} catch (err: any) {\r\n\t\t\tif (this.debug) {\r\n\t\t\t\t// NOTE: diffing these directly won't work because the key ordering isn't stable.\r\n\t\t\t\tfs.writeFileSync('logs/control.json', JSON.stringify(control, null, 2));\r\n\t\t\t\tfs.writeFileSync('logs/test.json', JSON.stringify(test, null, 2));\r\n\t\t\t}\r\n\t\t\tthrow new Error(err.message);\r\n\t\t}\r\n\r\n\t\tif (end) return;\r\n\t\tconst send = this.test.battle.send;\r\n\t\tthis.test.battle = Battle.fromJSON(test);\r\n\t\tthis.test.battle.restart(send);\r\n\t}\r\n}\r\n"], "mappings": "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", "names": ["assert"] }