pokemon-go-pvp-rank
Version:
Pokémon Go PVP ranking calculator
160 lines (147 loc) • 3.89 kB
text/typescript
import levelConstants, {
indexToLevel,
levelToIndex,
} from './level-constants.js';
import { PokedexEntry } from './pokedex.js';
export interface StatProducts {
attackProduct: number;
defenseProduct: number;
healthProduct: number;
product: number;
cp: number;
}
export interface RankOccurence extends StatProducts {
attackStat: number;
defenseStat: number;
healthStat: number;
level: number;
rank: number;
}
export interface Rank {
rank: RankOccurence[];
occurence: RankOccurence;
}
export interface BuildRankRequest {
pokedexEntry: PokedexEntry;
maxCP: number;
maxLevel?: number;
minimumStatValue?: number;
}
export interface CalculateRankRequest extends BuildRankRequest {
refAttackStat: number;
refDefenseStat: number;
refHealthStat: number;
}
export interface CalculateProductsRequest {
pokedexEntry: PokedexEntry;
attackStat: number;
defenseStat: number;
healthStat: number;
levelConstant: number;
}
export interface CalculateCpRequest {
attack: number;
defense: number;
health: number;
levelConstant: number;
}
function calculateCp({
attack,
defense,
health,
levelConstant,
}: CalculateCpRequest): number {
const value = attack * Math.sqrt(defense * health) * levelConstant ** 2;
return value < 100 ? 10 : Math.floor(value / 10);
}
export function calculateProducts({
pokedexEntry,
attackStat,
defenseStat,
healthStat,
levelConstant,
}: CalculateProductsRequest): StatProducts {
const { baseAttack, baseDefense, baseHealth } = pokedexEntry;
const attackProduct = (baseAttack + attackStat) * levelConstant;
const defenseProduct = (baseDefense + defenseStat) * levelConstant;
const healthProduct = Math.floor((baseHealth + healthStat) * levelConstant);
const product = attackProduct * defenseProduct * healthProduct;
const cp = calculateCp({
attack: baseAttack + attackStat,
defense: baseDefense + defenseStat,
health: baseHealth + healthStat,
levelConstant,
});
return {
attackProduct,
defenseProduct,
healthProduct,
product,
cp,
};
}
export function buildRank({
pokedexEntry,
maxCP,
maxLevel = 40,
minimumStatValue: minStat = 0,
}: BuildRankRequest): RankOccurence[] {
const rankEntries = [];
for (let attackStat = 15; attackStat >= minStat; attackStat -= 1) {
for (let defenseStat = 15; defenseStat >= minStat; defenseStat -= 1) {
for (let healthStat = 15; healthStat >= minStat; healthStat -= 1) {
const initialIndex = levelToIndex({ level: maxLevel });
for (let levelIndex = initialIndex; levelIndex >= 0; levelIndex -= 1) {
const products = calculateProducts({
pokedexEntry,
attackStat,
defenseStat,
healthStat,
levelConstant: levelConstants[levelIndex],
});
if (products.cp <= maxCP) {
rankEntries.push({
attackStat,
defenseStat,
healthStat,
level: indexToLevel({ index: levelIndex }),
...products,
});
break;
}
}
}
}
}
const rankSorted = rankEntries.sort((a, b) => b.product - a.product);
return rankSorted.map((data, index) => {
return { ...data, rank: index + 1 };
});
}
export default function calculateRank({
pokedexEntry,
refAttackStat,
refDefenseStat,
refHealthStat,
maxCP,
maxLevel = 40,
minimumStatValue = 0,
}: CalculateRankRequest): Rank {
const rank = buildRank({
pokedexEntry,
maxCP,
maxLevel,
minimumStatValue,
});
const occurence = rank.find(el => {
return (
el.attackStat === refAttackStat &&
el.defenseStat === refDefenseStat &&
el.healthStat === refHealthStat
);
});
if (!occurence) {
throw new Error('Could not find a rank occurence');
}
return { occurence, rank };
}