Often, you just need a random item in an array. Throughout Pokemon
Showdown's code, there are many instances of the following pattern:
let randomThing = things[this.random(things.length)];
Make this code easier to read by factoring the indexing into the
PRNG#sample function:
let randomThing = this.sample(things);
Run the following sed script to refactor lots of code to use sample:
s/\([a-zA-Z0-9.]\{1,\}\)\[this\.random(\1\.length)\]/this.sample(\1)/
This commit should not change behaviour. In particular, PRNG#next is
called the same number of times with the same number of parameter as
before this commit, and PRNG#next's results are interpreted in the same
way as before this commit.
According to `git grep getResidualStatuses`, the
Battle#getResidualStatuses function isn't referenced anywhere. Delete
the definition of that unused function.
Mechanically refactor code which uses PRNG#random for booleans to use
PRNG#randomChance instead.
Take advantage of the following properties:
random(x) < y is equivalent to randomChance(y, x)
random(x) <= y is equivalent to random(x) < (y + 1), i.e. randomChance(y + 1, x)
random(x) >= y is equivalent to !(random(x) < y), i.e. !randomChance(y, x)
random(x) > y is equivalent to random(x) >= (y + 1), i.e. !randomChance(y + 1, x)
random(x) === 0 is equivalent to random(x) < 1, i.e. randomChance(1, x)
!random(x) is equivalent to random(x) === 0, i.e. randomChance(1, x)
Boolean(random(x)) is equivalent to random(x) > 0, i.e. !randomChance(1, x)
This commit should not change behaviour. In particular, PRNG#next is
called the same number of times with the same number of parameter as
before this commit, and PRNG#next's results are interpreted in the same
way as before this commit.
Often, you just need a random boolean. Throughout Pokemon Showdown's
code, there are many creative ways of requesting random booleans. For
example:
if (this.random(10) < 3) {
if (this.isWeather(['sunnyday', 'desolateland']) || this.random(2) === 0) {
let shiny = !this.random(1024);
if (uberCount > 1 && this.random(5) >= 1) continue;
if (!this.random(3)) ability = ability1.name;
} else if ((ability === 'Iron Barbs' || ability === 'Rough Skin') && this.random(2)) {
if (typeof secondary.chance === 'undefined' || this.random(256) <= effectChance) {
if (accuracy !== true && this.random(256) > accuracy) {
Enable these methods to converge by introducing the PRNG#randomChance
function. It accepts a probability and returns true with that
probability.
Run the following sed script to refactor many common patterns to use
randomChance:
s/this\.random(\([0-9]\{1,\}\)) >= \([0-9]\{1,\}\)/!this.randomChance(\2, \1)/g
s/this\.random(\([0-9]\{1,\}\)) < \([0-9]\{1,\}\)/this.randomChance(\2, \1)/g
s/this\.random(\([0-9]\{1,\}\)) === 0/this.randomChance(1, \1)/g
s/!this\.random(\([0-9]\{1,\}\))/this.randomChance(1, \1)/g
The sed script takes advantage of the following properties:
random(x) < y is equivalent to randomChance(y, x)
random(x) >= y is equivalent to !(random(x) < y), i.e. !randomChance(y, x)
random(x) === 0 is equivalent to random(x) < 1, i.e. randomChance(1, x)
!random(x) is equivalent to random(x) === 0, i.e. randomChance(1, x)
This commit should not change behaviour. In particular, PRNG#next is
called the same number of times with the same number of parameter as
before this commit, and PRNG#next's results are interpreted in the same
way as before this commit.
Various functions call ModdedDex#getEffect with null, undefined, or an
empty string. For example, in the common case, Battle#getWeather calls
getEffect with an empty string. In these cases, getEffect basically
returns the same object each time: a PureEffect indicating that no
effect with the given name exists.
Creating the dummy PureEffect is expensive and puts pressure on the
JavaScript garbage collector. Memoize the creation in getEffect to
improve performance.
(As a bonus, special-casing null, undefined, and empty strings also lets
us drop some noisy truthiness checks in getEffect.)
On my machine, this commit speeds up Pokemon Showdown's tests by 11%.
Methodology: With and without this commit, I ran mocha four times with
zsh' 'time' builtin, dropped the first result, and averaged the wall
times:
mocha times before this commit:
18.20s user 0.33s system 118% cpu 15.704 total
17.91s user 0.34s system 118% cpu 15.454 total
18.11s user 0.33s system 118% cpu 15.558 total
mocha times after this commit:
16.53s user 0.35s system 120% cpu 13.994 total
16.43s user 0.34s system 120% cpu 13.918 total
16.54s user 0.32s system 120% cpu 14.009 total
Hardware:
Mid 2012 MacBook Pro
2.6 GHz Intel Core i7
Software:
Node v9.0.0
macOS 10.10.5
Battle#getRelevantEffectsInner performs a lookup for the base species of
every Pokemon with ModdedDex#getEffect, then invokes callbacks. The
lookup is expensive, and callbacks very rare. In fact, there are only
ever two callbacks: one for Arceus (SwitchIn) and one for Silvally
(SwitchIn).
Instead of an expensive ModdedDex#getEffect lookup for the callbacks,
put the callbacks directly on the Pokemon's Template object.
On my machine, this commit speeds up Pokemon Showdown's tests by 20%.
Methodology: With and without this commit, I ran mocha four times with
zsh' 'time' builtin, dropped the first result, and averaged the wall
times:
mocha times before this commit:
18.20s user 0.33s system 118% cpu 15.704 total
17.91s user 0.34s system 118% cpu 15.454 total
18.11s user 0.33s system 118% cpu 15.558 total
mocha times after this commit:
15.58s user 0.33s system 122% cpu 13.028 total
15.32s user 0.33s system 121% cpu 12.890 total
15.56s user 0.32s system 121% cpu 13.068 total
Hardware:
Mid 2012 MacBook Pro
2.6 GHz Intel Core i7
Software:
Node v9.0.0
macOS 10.10.5