diff --git a/generatePokemonData.ts b/generatePokemonData.ts index d638b01..45ad6b2 100644 --- a/generatePokemonData.ts +++ b/generatePokemonData.ts @@ -1,9 +1,25 @@ import * as fs from 'fs'; -import * as Pokemon from 'pokemongo-json-pokedex/output/pokemon.json'; -import { IPokemon, IStats, League } from 'src/ts/models/Pokemon'; +import Pokemon from 'pokemongo-json-pokedex/output/pokemon.json'; +import { IPokemon, IStats, League, Grade } from './src/ts/models/Pokemon'; -const GREAT_LEAGUE_CP = 1500; -const ULTRA_LEAGUE_CP = 2500; +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) => { @@ -30,65 +46,94 @@ Pokemon.forEach((mon) => { }, }; + 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); - const maxGreatLeagueLevelMultiplierIndex = getClosestCpMultiplierIndex(Math.sqrt((GREAT_LEAGUE_CP * 10) / cpMultiplier)); - const maxGreatLeagueLevelMultiplier = cpMultipliers[maxGreatLeagueLevelMultiplierIndex]; - const maxGreatLeagueCp = ~~((cpMultiplier * Math.pow(maxGreatLeagueLevelMultiplier, 2)) / 10); - const maxGreatLeagueLevel = (maxGreatLeagueLevelMultiplierIndex + 2) / 2; - pokemonWithIvs = { - cp: maxGreatLeagueCp, - level: maxGreatLeagueLevel, - ivHp: ivHp, - ivAtk: ivAtk, - ivDef: ivDef, - hp: ~~((baseHp + ivHp) * maxGreatLeagueLevelMultiplier), - atk: ~~((baseAtk + ivAtk) * maxGreatLeagueLevelMultiplier), - def: ~~((baseDef + ivDef) * maxGreatLeagueLevelMultiplier), - total: 0 - }; - pokemonWithIvs.total = pokemonWithIvs.hp + pokemonWithIvs.atk + pokemonWithIvs.def; - stats.pvp.great.push(pokemonWithIvs); + 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 maxUltraLeagueLevelMultiplierIndex = getClosestCpMultiplierIndex(Math.sqrt((ULTRA_LEAGUE_CP * 10) / cpMultiplier)); - const maxUltraLeagueLevelMultiplier = cpMultipliers[maxUltraLeagueLevelMultiplierIndex]; - const maxUltraLeagueCp = ~~((cpMultiplier * Math.pow(maxUltraLeagueLevelMultiplier, 2)) / 10); - const maxUltraLeagueLevel = (maxUltraLeagueLevelMultiplierIndex + 2) / 2; - pokemonWithIvs = { - cp: maxUltraLeagueCp, - level: maxUltraLeagueLevel, - ivHp: ivHp, - ivAtk: ivAtk, - ivDef: ivDef, - hp: ~~((baseHp + ivHp) * maxUltraLeagueLevelMultiplier), - atk: ~~((baseAtk + ivAtk) * maxUltraLeagueLevelMultiplier), - def: ~~((baseDef + ivDef) * maxUltraLeagueLevelMultiplier), - total: 0 - }; - pokemonWithIvs.total = pokemonWithIvs.hp + pokemonWithIvs.atk + pokemonWithIvs.def; - stats.pvp.ultra.push(pokemonWithIvs); + const combinedStats = maxLeagueCp + pokemonWithIvs.total; + combinedStatsDistribution[league][combinedStats] = combinedStatsDistribution[league][combinedStats] || []; + combinedStatsDistribution[league][combinedStats].push(pokemonWithIvs); + }); } } } - Object.keys(stats.pvp).forEach((league) => { - const keys = league as League; - stats.pvp[keys].sort((a, b) => { - if (a.cp === b.cp) { - return a.cp > b.cp ? 0 : 1; + // 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.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.writeFile('./dist/db/' + mon.name + '.json', JSON.stringify(stats), (err) => { - if(err) { - return console.log(mon.name, err); - } - console.log(mon.name); + 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); + }); }); }); diff --git a/src/ts/models/Pokemon.ts b/src/ts/models/Pokemon.ts index e2585cc..af11be9 100644 --- a/src/ts/models/Pokemon.ts +++ b/src/ts/models/Pokemon.ts @@ -1,3 +1,12 @@ +export enum Grade { + 'S', + 'A', + 'B', + 'C', + 'D', + 'F', +} + export interface IBaseStats { baseAttack : number; baseDefense : number; @@ -25,4 +34,6 @@ export interface IStats { atk : number; def : number; total : number; + speciesGrade : Grade; + metaGrade : Grade; } diff --git a/src/ts/models/pokemon.json.d.ts b/src/ts/models/pokemon.json.d.ts deleted file mode 100644 index 66948c9..0000000 --- a/src/ts/models/pokemon.json.d.ts +++ /dev/null @@ -1,39 +0,0 @@ -// for use with https://github.com/pokemongo-dev-contrib/pokemongo-json-pokedex -import { IBaseStats } from 'src/ts/models/Pokemon'; - -declare module "pokemongo-json-pokedex/output/pokemon.json" { - interface IMove { - name : string; - id : string; - legacy : boolean; - } - - interface IFamily { - id : string; - name : string; - } - - interface IType { - id : string; - name : string; - } - - interface IForme { - id : string; - name : string; - } - - interface IPokemon { - dex : number; - name : string; - cinematicMoves : Array; - quickMoves : Array; - family : IFamily; - stats : IBaseStats; - types : Array; - forms : Array - } - - const pokemon : Array; - export = pokemon; -} diff --git a/tsconfig.json b/tsconfig.json index 7c603af..8e8f47d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -4,6 +4,8 @@ "strict": true, "noUnusedLocals": false, "allowJs": true, + "resolveJsonModule": true, + "esModuleInterop": true, "outDir": "./dist", "sourceMap": true, "module": "commonjs", @@ -12,7 +14,7 @@ "baseUrl": ".", "traceResolution": false, "paths": { - "pokemongo-json-pokedex/output/pokemon.json": ["./src/ts/models/pokemon.json.d.ts"] + "src": ["./src"] }, "plugins": [{ "name": "tslint-language-service",