mirror of
https://github.com/smogon/pokemon-showdown.git
synced 2026-04-25 07:22:09 -05:00
Add Gen 8 support for RandomPlayerAI and sim tools
- add support to the RandomPlayerAI for Dynamaxing - add support to ExhaustiveRunner for Gigantamax Pokemon - simplify range logic in RandomPlayerAPI - handle crash from toID (needed by data/scripts.js) and Config (needed if a Battle takes long enough to potentially allow for requesting ties) globals not being defined - mark Gen 8 formats as runnable by the runners
This commit is contained in:
parent
e86ec0b7fe
commit
36b5b48d79
|
|
@ -35,6 +35,7 @@ export class ExhaustiveRunner {
|
|||
|
||||
// TODO: Add triple battles once supported by the AI.
|
||||
static readonly FORMATS = [
|
||||
'gen8customgame', 'gen8doublescustomgame',
|
||||
'gen7customgame', 'gen7doublescustomgame',
|
||||
'gen6customgame', 'gen6doublescustomgame',
|
||||
'gen5customgame', 'gen5doublescustomgame',
|
||||
|
|
@ -409,7 +410,8 @@ class CoordinatedPlayerAI extends RandomPlayerAI {
|
|||
return `team ${this.choosePokemon(team.map((p, i) => ({slot: i + 1, pokemon: p}))) || 1}`;
|
||||
}
|
||||
|
||||
protected chooseMove(moves: {choice: string, move: AnyObject}[]): string {
|
||||
protected chooseMove(active: AnyObject, moves: {choice: string, move: AnyObject}[]): string {
|
||||
this.markUsedIfGmax(active);
|
||||
// Prefer to use a move which hasn't been used yet.
|
||||
for (const {choice, move} of moves) {
|
||||
const id = this.fixMove(move);
|
||||
|
|
@ -418,11 +420,12 @@ class CoordinatedPlayerAI extends RandomPlayerAI {
|
|||
return choice;
|
||||
}
|
||||
}
|
||||
return super.chooseMove(moves);
|
||||
return super.chooseMove(active, moves);
|
||||
}
|
||||
|
||||
protected chooseSwitch(switches: {slot: number, pokemon: AnyObject}[]): number {
|
||||
return this.choosePokemon(switches) || super.chooseSwitch(switches);
|
||||
protected chooseSwitch(active: AnyObject | undefined, switches: {slot: number, pokemon: AnyObject}[]): number {
|
||||
this.markUsedIfGmax(active);
|
||||
return this.choosePokemon(switches) || super.chooseSwitch(active, switches);
|
||||
}
|
||||
|
||||
private choosePokemon(choices: {slot: number, pokemon: AnyObject}[]) {
|
||||
|
|
@ -442,7 +445,7 @@ class CoordinatedPlayerAI extends RandomPlayerAI {
|
|||
}
|
||||
|
||||
// The move options provided by the simulator have been converted from the name
|
||||
// which we're tracking, so we need to convert them back;
|
||||
// which we're tracking, so we need to convert them back.
|
||||
private fixMove(m: AnyObject) {
|
||||
const id = toID(m.move);
|
||||
if (id.startsWith('return')) return 'return';
|
||||
|
|
@ -450,4 +453,12 @@ class CoordinatedPlayerAI extends RandomPlayerAI {
|
|||
if (id.startsWith('hiddenpower')) return 'hiddenpower';
|
||||
return id;
|
||||
}
|
||||
|
||||
// Gigantamax Pokemon need to be special cased for tracking because the current
|
||||
// tracking only works if you can switch in a Pokemon.
|
||||
private markUsedIfGmax(active: AnyObject | undefined) {
|
||||
if (active && !active.canDynamax && active.maxMoves && active.maxMoves.gigantamax) {
|
||||
this.pools.pokemon.markUsed(toID(active.maxMoves.gigantamax));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,8 +20,14 @@ export interface MultiRandomRunnerOptions extends RunnerOptions {
|
|||
|
||||
export class MultiRandomRunner {
|
||||
static readonly FORMATS = [
|
||||
'gen7randombattle', 'gen7randomdoublesbattle', 'gen7battlefactory', 'gen6randombattle', 'gen6battlefactory',
|
||||
'gen5randombattle', 'gen4randombattle', 'gen3randombattle', 'gen2randombattle', 'gen1randombattle',
|
||||
'gen8randombattle', 'gen8randomdoublesbattle', 'gen8battlefactory',
|
||||
'gen7randombattle', 'gen7randomdoublesbattle', 'gen7battlefactory',
|
||||
'gen6randombattle', 'gen6battlefactory',
|
||||
'gen5randombattle',
|
||||
'gen4randombattle',
|
||||
'gen3randombattle',
|
||||
'gen2randombattle',
|
||||
'gen1randombattle',
|
||||
];
|
||||
|
||||
private readonly options: Partial<RunnerOptions>;
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ export class RandomPlayerAI extends BattlePlayer {
|
|||
const choices = request.forceSwitch.map((mustSwitch: AnyObject) => {
|
||||
if (!mustSwitch) return `pass`;
|
||||
|
||||
const canSwitch = [1, 2, 3, 4, 5, 6].filter(i => (
|
||||
const canSwitch = range(1, 6).filter(i => (
|
||||
pokemon[i - 1] &&
|
||||
// not active
|
||||
i > request.forceSwitch.length &&
|
||||
|
|
@ -56,6 +56,7 @@ export class RandomPlayerAI extends BattlePlayer {
|
|||
|
||||
if (!canSwitch.length) return `pass`;
|
||||
const target = this.chooseSwitch(
|
||||
request.active,
|
||||
canSwitch.map(slot => ({slot, pokemon: pokemon[slot - 1]}))
|
||||
);
|
||||
chosen.push(target);
|
||||
|
|
@ -65,7 +66,7 @@ export class RandomPlayerAI extends BattlePlayer {
|
|||
this.choose(choices.join(`, `));
|
||||
} else if (request.active) {
|
||||
// move request
|
||||
let [canMegaEvo, canUltraBurst, canZMove] = [true, true, true];
|
||||
let [canMegaEvo, canUltraBurst, canZMove, canDynamax] = [true, true, true, true];
|
||||
const pokemon = request.side.pokemon;
|
||||
const chosen: number[] = [];
|
||||
const choices = request.active.map((active: AnyObject, i: number) => {
|
||||
|
|
@ -74,21 +75,30 @@ export class RandomPlayerAI extends BattlePlayer {
|
|||
canMegaEvo = canMegaEvo && active.canMegaEvo;
|
||||
canUltraBurst = canUltraBurst && active.canUltraBurst;
|
||||
canZMove = canZMove && !!active.canZMove;
|
||||
canDynamax = canDynamax && !!active.canDynamax;
|
||||
|
||||
let canMove = [1, 2, 3, 4].slice(0, active.moves.length).filter(j => (
|
||||
// Determine whether we should change form if we do end up switching
|
||||
const change = (canMegaEvo || canUltraBurst || canDynamax) && this.prng.next() < this.mega;
|
||||
// If we've already dynamaxed or if we're planning on potentially dynamaxing
|
||||
// we need to use the maxMoves instead of our regular moves
|
||||
|
||||
const useMaxMoves = (!active.canDynamax && active.maxMoves) || (change && canDynamax);
|
||||
const possibleMoves = useMaxMoves ? active.maxMoves.maxMoves : active.moves;
|
||||
|
||||
let canMove = range(1, possibleMoves.length).filter(j => (
|
||||
// not disabled
|
||||
!active.moves[j - 1].disabled
|
||||
!possibleMoves[j - 1].disabled
|
||||
// NOTE: we don't actually check for whether we have PP or not because the
|
||||
// simulator will mark the move as disabled if there is zero PP and there are
|
||||
// situations where we actually need to use a move with 0 PP (Gen 1 Wrap).
|
||||
)).map(j => ({
|
||||
slot: j,
|
||||
move: active.moves[j - 1].move,
|
||||
target: active.moves[j - 1].target,
|
||||
move: possibleMoves[j - 1].move,
|
||||
target: possibleMoves[j - 1].target,
|
||||
zMove: false,
|
||||
}));
|
||||
if (canZMove) {
|
||||
canMove.push(...[1, 2, 3, 4].slice(0, active.canZMove.length)
|
||||
canMove.push(...range(1, active.canZMove.length)
|
||||
.filter(j => active.canZMove[j - 1])
|
||||
.map(j => ({
|
||||
slot: j,
|
||||
|
|
@ -126,7 +136,7 @@ export class RandomPlayerAI extends BattlePlayer {
|
|||
return {choice: move, move: m};
|
||||
});
|
||||
|
||||
const canSwitch = [1, 2, 3, 4, 5, 6].filter(j => (
|
||||
const canSwitch = range(1, 6).filter(j => (
|
||||
pokemon[j - 1] &&
|
||||
// not active
|
||||
!pokemon[j - 1].active &&
|
||||
|
|
@ -139,17 +149,21 @@ export class RandomPlayerAI extends BattlePlayer {
|
|||
|
||||
if (switches.length && (!moves.length || this.prng.next() > this.move)) {
|
||||
const target = this.chooseSwitch(
|
||||
active,
|
||||
canSwitch.map(slot => ({slot, pokemon: pokemon[slot - 1]}))
|
||||
);
|
||||
chosen.push(target);
|
||||
return `switch ${target}`;
|
||||
} else if (moves.length) {
|
||||
const move = this.chooseMove(moves);
|
||||
const move = this.chooseMove(active, moves);
|
||||
if (move.endsWith(` zmove`)) {
|
||||
canZMove = false;
|
||||
return move;
|
||||
} else if ((canMegaEvo || canUltraBurst) && this.prng.next() < this.mega) {
|
||||
if (canMegaEvo) {
|
||||
} else if (change) {
|
||||
if (canDynamax) {
|
||||
canDynamax = false;
|
||||
return `${move} dynamax`;
|
||||
} else if (canMegaEvo) {
|
||||
canMegaEvo = false;
|
||||
return `${move} mega`;
|
||||
} else {
|
||||
|
|
@ -161,7 +175,8 @@ export class RandomPlayerAI extends BattlePlayer {
|
|||
}
|
||||
} else {
|
||||
throw new Error(`${this.constructor.name} unable to make choice ${i}. request='${request}',` +
|
||||
` chosen='${chosen}', (mega=${canMegaEvo}, ultra=${canUltraBurst}, zmove=${canZMove})`);
|
||||
` chosen='${chosen}', (mega=${canMegaEvo}, ultra=${canUltraBurst}, zmove=${canZMove},` +
|
||||
` dynamax='${canDynamax}')`);
|
||||
}
|
||||
});
|
||||
this.choose(choices.join(`, `));
|
||||
|
|
@ -175,11 +190,24 @@ export class RandomPlayerAI extends BattlePlayer {
|
|||
return `default`;
|
||||
}
|
||||
|
||||
protected chooseMove(moves: {choice: string, move: AnyObject}[]): string {
|
||||
protected chooseMove(active: AnyObject, moves: {choice: string, move: AnyObject}[]): string {
|
||||
return this.prng.sample(moves).choice;
|
||||
}
|
||||
|
||||
protected chooseSwitch(switches: {slot: number, pokemon: AnyObject}[]): number {
|
||||
protected chooseSwitch(active: AnyObject | undefined, switches: {slot: number, pokemon: AnyObject}[]): number {
|
||||
return this.prng.sample(switches).slot;
|
||||
}
|
||||
}
|
||||
|
||||
// Creates an array of numbers progressing from start up to and including end
|
||||
function range(start: number, end?: number, step = 1) {
|
||||
if (end === undefined) {
|
||||
end = start;
|
||||
start = 0;
|
||||
}
|
||||
const result = [];
|
||||
for (; start <= end; start += step) {
|
||||
result.push(start);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,6 +42,8 @@ const shell = cmd => child_process.execSync(cmd, {stdio: 'inherit', cwd: path.re
|
|||
shell('node build');
|
||||
|
||||
const Dex = require('../../.sim-dist/dex').Dex;
|
||||
global.toID = require('../../.sim-dist/dex').Dex.getId;
|
||||
global.Config = {allowrequestingties: false};
|
||||
Dex.includeModData();
|
||||
|
||||
const {ExhaustiveRunner} = require('../../.sim-dist/tools/exhaustive-runner');
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user