diff --git a/data/random-battles/gen1/teams.ts b/data/random-battles/gen1/teams.ts index 2ce6a27ef455..59b8bee73301 100644 --- a/data/random-battles/gen1/teams.ts +++ b/data/random-battles/gen1/teams.ts @@ -107,7 +107,7 @@ export class RandomGen1Teams extends RandomGen2Teams { this.enforceNoDirectCustomBanlistChanges(); // Get what we need ready. - const seed = this.prng.seed; + const seed = this.prng.getSeed(); const ruleTable = this.dex.formats.getRuleTable(this.format); const pokemon: RandomTeamsTypes.RandomSet[] = []; diff --git a/data/random-battles/gen3/teams.ts b/data/random-battles/gen3/teams.ts index b67d2e3f65ca..3865ed7030c2 100644 --- a/data/random-battles/gen3/teams.ts +++ b/data/random-battles/gen3/teams.ts @@ -639,7 +639,7 @@ export class RandomGen3Teams extends RandomGen4Teams { randomTeam() { this.enforceNoDirectCustomBanlistChanges(); - const seed = this.prng.seed; + const seed = this.prng.getSeed(); const ruleTable = this.dex.formats.getRuleTable(this.format); const pokemon: RandomTeamsTypes.RandomSet[] = []; diff --git a/data/random-battles/gen5/teams.ts b/data/random-battles/gen5/teams.ts index 0454eef66335..0ec0912b1c86 100644 --- a/data/random-battles/gen5/teams.ts +++ b/data/random-battles/gen5/teams.ts @@ -846,7 +846,7 @@ export class RandomGen5Teams extends RandomGen6Teams { randomTeam() { this.enforceNoDirectCustomBanlistChanges(); - const seed = this.prng.seed; + const seed = this.prng.getSeed(); const ruleTable = this.dex.formats.getRuleTable(this.format); const pokemon: RandomTeamsTypes.RandomSet[] = []; diff --git a/data/random-battles/gen7/teams.ts b/data/random-battles/gen7/teams.ts index d7fb776a4063..413e3767ca67 100644 --- a/data/random-battles/gen7/teams.ts +++ b/data/random-battles/gen7/teams.ts @@ -1182,7 +1182,7 @@ export class RandomGen7Teams extends RandomGen8Teams { randomTeam() { this.enforceNoDirectCustomBanlistChanges(); - const seed = this.prng.seed; + const seed = this.prng.getSeed(); const ruleTable = this.dex.formats.getRuleTable(this.format); const pokemon: RandomTeamsTypes.RandomSet[] = []; diff --git a/data/random-battles/gen8/teams.ts b/data/random-battles/gen8/teams.ts index 210968ed3df2..dc9da0fc24b5 100644 --- a/data/random-battles/gen8/teams.ts +++ b/data/random-battles/gen8/teams.ts @@ -2479,7 +2479,7 @@ export class RandomGen8Teams { randomTeam() { this.enforceNoDirectCustomBanlistChanges(); - const seed = this.prng.seed; + const seed = this.prng.getSeed(); const ruleTable = this.dex.formats.getRuleTable(this.format); const pokemon: RandomTeamsTypes.RandomSet[] = []; diff --git a/data/random-battles/gen9/teams.ts b/data/random-battles/gen9/teams.ts index c3df7c8609ea..d035868cfbb7 100644 --- a/data/random-battles/gen9/teams.ts +++ b/data/random-battles/gen9/teams.ts @@ -1632,7 +1632,7 @@ export class RandomTeams { randomTeam() { this.enforceNoDirectCustomBanlistChanges(); - const seed = this.prng.seed; + const seed = this.prng.getSeed(); const ruleTable = this.dex.formats.getRuleTable(this.format); const pokemon: RandomTeamsTypes.RandomSet[] = []; diff --git a/data/random-battles/gen9baby/teams.ts b/data/random-battles/gen9baby/teams.ts index 1cc922763a17..99ba41ae5d90 100644 --- a/data/random-battles/gen9baby/teams.ts +++ b/data/random-battles/gen9baby/teams.ts @@ -669,7 +669,7 @@ export class RandomBabyTeams extends RandomTeams { randomBabyTeam() { this.enforceNoDirectCustomBanlistChanges(); - const seed = this.prng.seed; + const seed = this.prng.getSeed(); const ruleTable = this.dex.formats.getRuleTable(this.format); const pokemon: RandomTeamsTypes.RandomSet[] = []; diff --git a/data/random-battles/gen9cap/teams.ts b/data/random-battles/gen9cap/teams.ts index d17bf8b5105b..762aa582f818 100644 --- a/data/random-battles/gen9cap/teams.ts +++ b/data/random-battles/gen9cap/teams.ts @@ -185,7 +185,7 @@ export class RandomCAPTeams extends RandomTeams { randomTeam() { this.enforceNoDirectCustomBanlistChanges(); - const seed = this.prng.seed; + const seed = this.prng.getSeed(); const ruleTable = this.dex.formats.getRuleTable(this.format); const pokemon: RandomTeamsTypes.RandomSet[] = []; diff --git a/sim/battle-stream.ts b/sim/battle-stream.ts index aa8c5ec27993..ed96d384d3b5 100644 --- a/sim/battle-stream.ts +++ b/sim/battle-stream.ts @@ -136,10 +136,12 @@ export class BattleStream extends Streams.ObjectReadWriteStream { this.battle!.inputLog.push(`>forcelose ${message}`); break; case 'reseed': - const seed = message ? message.split(',').map(Number) as PRNGSeed : null; + const seed = message ? message.split(',').map( + n => /[0-9]/.test(n.charAt(0)) ? Number(n) : n + ) as PRNGSeed : null; this.battle!.resetRNG(seed); // could go inside resetRNG, but this makes using it in `eval` slightly less buggy - this.battle!.inputLog.push(`>reseed ${this.battle!.prng.seed.join(',')}`); + this.battle!.inputLog.push(`>reseed ${this.battle!.prng.getSeed().join(',')}`); break; case 'tiebreak': this.battle!.tiebreak(); diff --git a/sim/battle.ts b/sim/battle.ts index c0cdaa99e285..ca52a3dbe4cf 100644 --- a/sim/battle.ts +++ b/sim/battle.ts @@ -65,7 +65,7 @@ interface BattleOptions { /** Output callback */ send?: (type: string, data: string | string[]) => void; prng?: PRNG; // PRNG override (you usually don't need this, just pass a seed) - seed?: PRNGSeed | Buffer; // PRNG seed + seed?: PRNGSeed; // PRNG seed rated?: boolean | string; // Rated string p1?: PlayerOptions; // Player 1 data p2?: PlayerOptions; // Player 2 data diff --git a/sim/prng.ts b/sim/prng.ts index 0aa29a0c049a..d6b59c2897b7 100644 --- a/sim/prng.ts +++ b/sim/prng.ts @@ -14,53 +14,39 @@ import * as sodium from 'sodium-native'; +export type PRNGSeed = SodiumRNGSeed | Gen5RNGSeed; +export type SodiumRNGSeed = ['sodium' | number, ...number[]]; /** 64-bit big-endian [high -> low] int */ -export type PRNGSeed = [number, number, number, number]; +export type Gen5RNGSeed = [number, number, number, number]; -interface PRNGRequired { - next(from?: number, to?: number): number; - randomChance(numerator: number, denominator: number): boolean; - sample(items: readonly T[]): T; - shuffle(items: T[], start: number, end: number): void; +interface RNG { + getSeed(): PRNGSeed; + /** random 32-bit number */ + next(): number; } /** - * A PRNG intended to emulate the on-cartridge PRNG for Gen 5 with a 64-bit - * initial seed. + * Abstract PRNG. */ -export class PRNG implements PRNGRequired { - readonly initialSeed: PRNGSeed; - seed: Buffer; +export class PRNG { + readonly startingSeed: PRNGSeed; + rng!: RNG; /** Creates a new source of randomness for the given seed. */ - constructor(seed: PRNGSeed | Buffer | null = null, initialSeed?: PRNGSeed) { + constructor(seed: PRNGSeed | null = null, initialSeed?: PRNGSeed) { if (!seed) seed = PRNG.generateSeed(); - // hack i know i know - this.initialSeed = initialSeed || [...seed] as PRNGSeed; - this.seed = Array.isArray(seed) ? PRNG.convertSeed(seed.slice() as PRNGSeed) : seed; - } - - static convertSeed(inputSeed: PRNGSeed) { - // randombytes_buf_deterministic only takes 32 bytes (8x4 here), so it's gotta be slightly longer - // than we use - const seed = new Uint32Array(8); - for (let i = 0; i < seed.length; i++) seed[i] = inputSeed[i]; - const buf = Buffer.alloc(32); - // @ts-ignore this doesn't accept buffers, but instead TypedArrays - typedef is out of date - sodium.randombytes_buf_deterministic(buf, seed); - return buf; - } - - /** - * Getter to the initial seed. - * - * This should be considered a hack and is only here for backwards compatibility. - */ - get startingSeed(): PRNGSeed { - return this.initialSeed; + this.startingSeed = initialSeed || [...seed]; // make a copy + this.setSeed(seed); } setSeed(seed: PRNGSeed) { - this.seed = PRNG.convertSeed(seed); + if (seed[0] === 'sodium') { + this.rng = new SodiumRNG(seed); + } else { + this.rng = new Gen5RNG(seed as Gen5RNGSeed); + } + } + getSeed(): PRNGSeed { + return this.rng.getSeed(); } /** @@ -69,7 +55,7 @@ export class PRNG implements PRNGRequired { * The new PRNG will have its initial seed set to the seed of the current instance. */ clone(): PRNG { - return new PRNG(this.seed, this.initialSeed); + return new PRNG(this.rng.getSeed(), this.startingSeed); } /** @@ -81,25 +67,16 @@ export class PRNG implements PRNGRequired { * m and n are converted to integers via Math.floor. If the result is NaN, they are ignored. */ next(from?: number, to?: number): number { - let result = this.seededRandom(); + const result = this.rng.next(); if (from) from = Math.floor(from); if (to) to = Math.floor(to); if (from === undefined) { - result = result / 0x100000000; + return result / 0x100000000; } else if (!to) { - result = Math.floor(result * from / 0x100000000); + return Math.floor(result * from / 0x100000000); } else { - result = Math.floor(result * (to - from) / 0x100000000) + from; + return Math.floor(result * (to - from) / 0x100000000) + from; } - return result; - } - - private seededRandom() { - const buf = Buffer.alloc(36); - sodium.randombytes_buf_deterministic(buf, this.seed); - // use the first four bytes for the output, use the other 32 bytes for the next seed - this.seed = buf.slice(4); - return buf.slice(0, 4).readUint32BE(); } /** @@ -157,78 +134,88 @@ export class PRNG implements PRNGRequired { } } - static generateSeed() { + static generateSeed(): SodiumRNGSeed { return [ - Math.floor(Math.random() * 0x10000), - Math.floor(Math.random() * 0x10000), - Math.floor(Math.random() * 0x10000), - Math.floor(Math.random() * 0x10000), - ] as PRNGSeed; + 'sodium', + Math.floor(Math.random() * 0x100000000), + Math.floor(Math.random() * 0x100000000), + Math.floor(Math.random() * 0x100000000), + Math.floor(Math.random() * 0x100000000), + ]; } } -// old, predictable PRNG. do not use this for anything but tests -export class TestPRNG implements PRNGRequired { - readonly initialSeed: PRNGSeed; - seed: PRNGSeed; +export class SodiumRNG implements RNG { + seed!: Buffer; /** Creates a new source of randomness for the given seed. */ - constructor(seed: PRNGSeed | null = null) { - if (!seed) seed = PRNG.generateSeed(); - this.initialSeed = seed.slice() as PRNGSeed; // make a copy - this.seed = seed.slice() as PRNGSeed; + constructor(seed: SodiumRNGSeed) { + this.setSeed(seed); } - get startingSeed(): PRNGSeed { - return this.initialSeed; + setSeed(inputSeed: SodiumRNGSeed) { + // randombytes_buf_deterministic requires 32 bytes (8x4 here), but + // generateSeed generates 16 bytes, so the last 16 bytes will be 0 + // when starting out. This shouldn't cause any problems. + const seed = Buffer.alloc(32); + for (let i = 1; i < inputSeed.length; i++) { + const num = inputSeed[i as 1]; // 32-bit uint + // big-endian, 8 bits each + seed.writeUInt32BE(num, i * 4 - 4); + } + this.seed = seed; } - - clone(): PRNG { - return new PRNG(this.seed); + getSeed() { + const out: SodiumRNGSeed = ['sodium']; + for (let i = 0; i < 32; i += 4) { + out.push(this.seed.readUInt32BE(i)); + } + return out; } - next(from?: number, to?: number): number { - this.seed = this.nextFrame(this.seed); // Advance the RNG - let result = (this.seed[0] << 16 >>> 0) + this.seed[1]; // Use the upper 32 bits - if (from) from = Math.floor(from); - if (to) to = Math.floor(to); - if (from === undefined) { - result = result / 0x100000000; - } else if (!to) { - result = Math.floor(result * from / 0x100000000); - } else { - result = Math.floor(result * (to - from) / 0x100000000) + from; - } - return result; + next() { + const buf = Buffer.alloc(36); + sodium.randombytes_buf_deterministic(buf, this.seed); + // use the first four bytes for the output, use the other 32 bytes for the next seed + this.seed = buf.slice(4); + return buf.slice(0, 4).readUint32BE(); } +} - randomChance(numerator: number, denominator: number): boolean { - return this.next(denominator) < numerator; +/** + * A PRNG intended to emulate the on-cartridge PRNG for Gen 5 with a 64-bit + * initial seed. + */ +export class Gen5RNG implements RNG { + seed: Gen5RNGSeed; + /** Creates a new source of randomness for the given seed. */ + constructor(seed: Gen5RNGSeed | null = null) { + this.seed = [...seed || Gen5RNG.generateSeed()]; } - sample(items: readonly T[]): T { - if (items.length === 0) { - throw new RangeError(`Cannot sample an empty array`); - } - const index = this.next(items.length); - const item = items[index]; - if (item === undefined && !Object.prototype.hasOwnProperty.call(items, index)) { - throw new RangeError(`Cannot sample a sparse array`); - } - return item; + getSeed() { + return this.seed; } - shuffle(items: T[], start = 0, end: number = items.length) { - while (start < end - 1) { - const nextIndex = this.next(start, end); - if (start !== nextIndex) { - [items[start], items[nextIndex]] = [items[nextIndex], items[start]]; - } - start++; - } + /** + * Retrieves the next random number in the sequence. + * This function has three different results, depending on arguments: + * - random() returns a real number in [0, 1), just like Math.random() + * - random(n) returns an integer in [0, n) + * - random(m, n) returns an integer in [m, n) + * m and n are converted to integers via Math.floor. If the result is NaN, they are ignored. + */ + next(): number { + this.seed = this.nextFrame(this.seed); // Advance the RNG + return (this.seed[0] << 16 >>> 0) + this.seed[1]; // Use the upper 32 bits } - multiplyAdd(a: PRNGSeed, b: PRNGSeed, c: PRNGSeed) { - const out: PRNGSeed = [0, 0, 0, 0]; + /** + * Calculates `a * b + c` (with 64-bit 2's complement integers) + * + * If you've done long multiplication, this is the same thing. + */ + multiplyAdd(a: Gen5RNGSeed, b: Gen5RNGSeed, c: Gen5RNGSeed) { + const out: Gen5RNGSeed = [0, 0, 0, 0]; let carry = 0; for (let outIndex = 3; outIndex >= 0; outIndex--) { @@ -246,9 +233,20 @@ export class TestPRNG implements PRNGRequired { return out; } - nextFrame(seed: PRNGSeed, framesToAdvance = 1): PRNGSeed { - const a: PRNGSeed = [0x5D58, 0x8B65, 0x6C07, 0x8965]; - const c: PRNGSeed = [0, 0, 0x26, 0x9EC3]; + /** + * The RNG is a Linear Congruential Generator (LCG) in the form: `x_{n + 1} = (a x_n + c) % m` + * + * Where: `x_0` is the seed, `x_n` is the random number after n iterations, + * + * ```` + * a = 0x5D588B656C078965 + * c = 0x00269EC3 + * m = 2^64 + * ```` + */ + nextFrame(seed: Gen5RNGSeed, framesToAdvance = 1): Gen5RNGSeed { + const a: Gen5RNGSeed = [0x5D58, 0x8B65, 0x6C07, 0x8965]; + const c: Gen5RNGSeed = [0, 0, 0x26, 0x9EC3]; for (let i = 0; i < framesToAdvance; i++) { seed = this.multiplyAdd(seed, a, c); @@ -256,6 +254,15 @@ export class TestPRNG implements PRNGRequired { return seed; } + + static generateSeed() { + return [ + Math.floor(Math.random() * 0x10000), + Math.floor(Math.random() * 0x10000), + Math.floor(Math.random() * 0x10000), + Math.floor(Math.random() * 0x10000), + ] as Gen5RNGSeed; + } } // The following commented-out function is designed to emulate the on-cartridge diff --git a/sim/state.ts b/sim/state.ts index b32dacdb848b..85800e2e84c0 100644 --- a/sim/state.ts +++ b/sim/state.ts @@ -66,7 +66,7 @@ export const State = new class { for (const [i, side] of battle.sides.entries()) { state.sides[i] = this.serializeSide(side); } - state.prng = battle.prng.seed; + state.prng = battle.prng.getSeed(); state.hints = Array.from(battle.hints); // We treat log specially because we only set it back on Battle after everything // else has been deserialized to avoid anything accidentally `add`-ing to it. diff --git a/sim/tools/exhaustive-runner.ts b/sim/tools/exhaustive-runner.ts index e0e0517ac43b..c0dbff11855d 100644 --- a/sim/tools/exhaustive-runner.ts +++ b/sim/tools/exhaustive-runner.ts @@ -7,7 +7,7 @@ import {ObjectReadWriteStream} from '../../lib/streams'; import {Dex, toID} from '../dex'; -import {TestPRNG as PRNG, PRNGSeed} from '../prng'; +import {PRNG, PRNGSeed} from '../prng'; import {RandomPlayerAI} from './random-player-ai'; import {AIOptions, Runner} from './runner'; @@ -74,7 +74,7 @@ export class ExhaustiveRunner { const dex = Dex.forFormat(this.format); dex.loadData(); // FIXME: This is required for `dex.gen` to be set properly... - const seed = this.prng.seed; + const seed = this.prng.getSeed(); const pools = this.createPools(dex); const createAI = (s: ObjectReadWriteStream, o: AIOptions) => new CoordinatedPlayerAI(s, o, pools); const generator = new TeamGenerator(dex, this.prng, pools, ExhaustiveRunner.getSignatures(dex, pools)); diff --git a/sim/tools/multi-random-runner.ts b/sim/tools/multi-random-runner.ts index becfcbe8ed53..70b373a4ed2d 100644 --- a/sim/tools/multi-random-runner.ts +++ b/sim/tools/multi-random-runner.ts @@ -5,7 +5,7 @@ * @license MIT */ -import {TestPRNG as PRNG, PRNGSeed} from '../prng'; +import {PRNG, PRNGSeed} from '../prng'; import {Runner, RunnerOptions} from './runner'; // @ts-ignore @@ -71,7 +71,7 @@ export class MultiRandomRunner { games = []; } - const seed = this.prng.seed; + const seed = this.prng.getSeed(); const game = new Runner({format, ...this.options}).run().catch(err => { failures++; console.error( diff --git a/sim/tools/random-player-ai.ts b/sim/tools/random-player-ai.ts index 0ad2a5172576..7c12a3dafb50 100644 --- a/sim/tools/random-player-ai.ts +++ b/sim/tools/random-player-ai.ts @@ -8,7 +8,7 @@ import {ObjectReadWriteStream} from '../../lib/streams'; import {BattlePlayer} from '../battle-stream'; -import {TestPRNG as PRNG, PRNGSeed} from '../prng'; +import {PRNG, PRNGSeed} from '../prng'; export class RandomPlayerAI extends BattlePlayer { protected readonly move: number; diff --git a/sim/tools/runner.ts b/sim/tools/runner.ts index bac2ceb2d42f..53cb70c45025 100644 --- a/sim/tools/runner.ts +++ b/sim/tools/runner.ts @@ -13,7 +13,7 @@ import {ObjectReadWriteStream} from '../../lib/streams'; import {Battle} from '../battle'; import * as BattleStreams from '../battle-stream'; import {State} from '../state'; -import {TestPRNG as PRNG, PRNGSeed} from '../prng'; +import {PRNG, PRNGSeed} from '../prng'; import {RandomPlayerAI} from './random-player-ai'; export interface AIOptions { @@ -86,7 +86,7 @@ export class Runner { private async runGame(format: string, battleStream: RawBattleStream | DualStream) { // @ts-ignore - DualStream implements everything relevant from BattleStream. const streams = BattleStreams.getPlayerStreams(battleStream); - const spec = {formatid: format, seed: this.prng.seed}; + const spec = {formatid: format, seed: this.prng.getSeed()}; const is4P = Dex.formats.get(format).playerCount > 2; const p1spec = this.getPlayerSpec("Bot 1", this.p1options); const p2spec = this.getPlayerSpec("Bot 2", this.p2options); diff --git a/test/common.js b/test/common.js index 7e9010e1545d..7fcfd897b222 100644 --- a/test/common.js +++ b/test/common.js @@ -4,7 +4,6 @@ const path = require('path'); const fs = require('fs'); const assert = require('./assert'); const Sim = require('./../dist/sim'); -const {TestPRNG} = require('./../dist/sim/prng'); const Dex = Sim.Dex; const cache = new Map(); @@ -110,11 +109,7 @@ class TestTools { strictChoices: options.strictChoices !== false, }; - if (!teams) { - const battle = new Sim.Battle(battleOptions); - battle.prng = new TestPRNG(battleOptions.seed); - return battle; - } + 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`); @@ -122,9 +117,7 @@ class TestTools { battleOptions[playerSlot] = {team: teams[i]}; } - const battle = new Sim.Battle(battleOptions); - battle.prng = new TestPRNG(battleOptions.seed); - return battle; + return new Sim.Battle(battleOptions); } /** diff --git a/test/sim/items/quickclaw.js b/test/sim/items/quickclaw.js index ba43442ddd36..a2ee83c04516 100644 --- a/test/sim/items/quickclaw.js +++ b/test/sim/items/quickclaw.js @@ -10,7 +10,7 @@ describe('Quick Claw', function () { battle.destroy(); }); - it.skip(`[Gen 2] shares its activation roll with every holder on any given turn`, function () { + it(`[Gen 2] shares its activation roll with every holder on any given turn`, function () { battle = common.gen(2).createBattle({seed: [1, 2, 3, 27]}, [[ {species: 'snorlax', item: 'quickclaw', moves: ['sleeptalk']}, ], [ @@ -27,7 +27,7 @@ describe('Quick Claw', function () { assert.notEqual(snorlax.speed, mewtwo.speed); }); - it.skip(`[Gen 3] causes Speed ties with every holder when activated`, function () { + it(`[Gen 3] causes Speed ties with every holder when activated`, function () { battle = common.gen(3).createBattle({seed: [163, 106, 112, 542]}, [[ {species: 'snorlax', item: 'quickclaw', moves: ['spore']}, ], [ diff --git a/test/sim/misc/multi-battle.js b/test/sim/misc/multi-battle.js index 6718f9cce077..ac39e23a2661 100644 --- a/test/sim/misc/multi-battle.js +++ b/test/sim/misc/multi-battle.js @@ -24,7 +24,8 @@ describe('Free-for-all', function () { battle.makeChoices(); battle.lose('p2'); assert(battle.p2.activeRequest.wait); - battle.makeChoices('auto', '', 'move uturn 1', 'auto'); + battle.makeChoices('auto', '', 'move uturn', 'auto'); + battle.lose('p3'); battle.makeChoices(); assert.equal(battle.turn, 4); }); diff --git a/test/sim/misc/prng.js b/test/sim/misc/prng.js index 60c3aacf5aaa..c000c9547f56 100644 --- a/test/sim/misc/prng.js +++ b/test/sim/misc/prng.js @@ -3,24 +3,9 @@ const PRNG = require('../../../dist/sim/prng').PRNG; const assert = require('../../assert'); -const testSeed = [2, 3, 4, 5]; +const testSeed = [1, 2, 3, 4]; describe(`PRNG`, function () { - it("should always generate the same results off the same seed", function () { - const results = []; - let testAgainst = new PRNG(testSeed); - for (let i = 0; i < 100; i++) { - results.push(testAgainst.next()); - } - for (let i = 0; i < 10; i++) { - const cur = new PRNG(testSeed); - for (let j = 0; j < results.length; j++) { - const n = cur.next(); - assert(results[j] === n, `generation ${j} for seed ${testSeed} did not match (expected: ${results[j]}, got ${n})`); - } - } - }); - describe(`randomChance(numerator=0, denominator=1)`, function () { it(`should always return false`, function () { const prng = new PRNG(testSeed); diff --git a/test/sim/misc/state.js b/test/sim/misc/state.js index 4311a8c849ed..e48bde6e1a0a 100644 --- a/test/sim/misc/state.js +++ b/test/sim/misc/state.js @@ -23,11 +23,10 @@ const TEAMS = [[ describe('State', function () { describe('Battles', function () { - it.skip('should be able to be serialized and deserialized without affecting functionality (slow)', function () { + it('should be able to be serialized and deserialized without affecting functionality (slow)', function () { this.timeout(5000); - const seed = [1, 2, 3, 4]; - const control = common.createBattle({seed}, TEAMS); - let test = common.createBattle({seed}, TEAMS); + const control = common.createBattle(TEAMS); + let test = common.createBattle(TEAMS); while (!(control.ended || test.ended)) { control.makeChoices();