import * as fs from 'fs'; import Pokemon from 'pokemongo-json-pokedex/output/pokemon.json'; import { IPokemon, IStats, League, Grade } from './src/ts/models/Pokemon'; interface ICpAndTotalFound { [ key : number ] : Array; } interface IStatsDistribution { great : ICpAndTotalFound; ultra : ICpAndTotalFound; } interface IMaxCpByLeague { great : number; ultra : number; } const outPath = './dist/db/'; const maxCpByLeague : IMaxCpByLeague = { great: 1500, ultra: 2500 }; const cpMultipliers : Array = JSON.parse(fs.readFileSync('src/db/cpMultipliers.json', 'utf8')); const getClosestCpMultiplierIndex = (value : number) => { let i; for (i = 0; i < cpMultipliers.length; i++) { if (value < cpMultipliers[i]) { break; } } return Math.max(i - 1, 0); }; Pokemon.forEach((mon) => { const baseAtk = mon.stats.baseAttack; const baseDef = mon.stats.baseDefense; const baseHp = mon.stats.baseStamina; const stats : IPokemon = { name: mon.name, id: mon.dex, stats: mon.stats, pvp: { great: [], ultra: [], }, }; const combinedStatsDistribution : IStatsDistribution = { great: {}, ultra: {}, }; for (let ivHp = 15; ivHp >= 0; ivHp--) { for (let ivAtk = 15; ivAtk >= 0; ivAtk--) { for (let ivDef = 15; ivDef >= 0; ivDef--) { let pokemonWithIvs : IStats; const cpMultiplier = (baseAtk + ivAtk) * Math.sqrt(baseDef + ivDef) * Math.sqrt(baseHp + ivHp); Object.keys(maxCpByLeague).forEach((key) => { const league = key as League; const maxCp = maxCpByLeague[league]; const maxLeagueLevelMultiplierIndex = getClosestCpMultiplierIndex(Math.sqrt((maxCp * 10) / cpMultiplier)); const maxLeagueLevelMultiplier = cpMultipliers[maxLeagueLevelMultiplierIndex]; const maxLeagueCp = ~~((cpMultiplier * Math.pow(maxLeagueLevelMultiplier, 2)) / 10); const maxLeagueLevel = (maxLeagueLevelMultiplierIndex + 2) / 2; pokemonWithIvs = { cp: maxLeagueCp, level: maxLeagueLevel, ivHp: ivHp, ivAtk: ivAtk, ivDef: ivDef, hp: ~~((baseHp + ivHp) * maxLeagueLevelMultiplier), atk: ~~((baseAtk + ivAtk) * maxLeagueLevelMultiplier), def: ~~((baseDef + ivDef) * maxLeagueLevelMultiplier), total: 0, speciesGrade: Grade.F, metaGrade: Grade.F, }; pokemonWithIvs.total = pokemonWithIvs.hp + pokemonWithIvs.atk + pokemonWithIvs.def; stats.pvp[league].push(pokemonWithIvs); const combinedStats = maxLeagueCp + pokemonWithIvs.total; combinedStatsDistribution[league][combinedStats] = combinedStatsDistribution[league][combinedStats] || []; combinedStatsDistribution[league][combinedStats].push(pokemonWithIvs); }); } } } // process the pokemon stats for league-worthiness Object.keys(stats.pvp).forEach((key) => { const league = key as League; stats.pvp[league].sort((a, b) => { if (a.total === b.total) { return a.total > b.total ? 0 : 1; } return a.level > b.level ? 0 : 1; }); const orderedCombinedStats = Object.keys(combinedStatsDistribution[league]).map(cpTotal => parseInt(cpTotal)).sort(); const offset = orderedCombinedStats[orderedCombinedStats.length - 1]; const max = orderedCombinedStats[1] - offset; // index 0 is always `Grade.S` for (let index = orderedCombinedStats.length - 1; index >= 0; index--) { const combinedStats = orderedCombinedStats[index]; const percent = (combinedStats - offset) / max; // remove all `Grade.F` stats (to save space in the DB) if (percent < 0.6) { delete combinedStatsDistribution[league][combinedStats]; continue; } combinedStatsDistribution[league][combinedStats].forEach((pokemonStats) => { if (index === 0) { pokemonStats.speciesGrade = Grade.S; } else { if (percent >= 0.9) { pokemonStats.speciesGrade = Grade.A; } else if (percent >= 0.8) { pokemonStats.speciesGrade = Grade.B; } else if (percent >= 0.7) { pokemonStats.speciesGrade = Grade.C; } else if (percent >= 0.6) { pokemonStats.speciesGrade = Grade.D; } } }); } }); fs.mkdir(outPath, {recursive: true}, () => { fs.writeFile(outPath + mon.id + '.json', JSON.stringify(stats), (err) => { if(err) { return console.log(mon.name, err); } console.log(mon.name); }); }); });